OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "platform/image-decoders/ImageDecoderTestHelpers.h" | 5 #include "platform/image-decoders/ImageDecoderTestHelpers.h" |
6 | 6 |
7 #include "platform/SharedBuffer.h" | 7 #include "platform/SharedBuffer.h" |
8 #include "platform/image-decoders/ImageDecoder.h" | 8 #include "platform/image-decoders/ImageDecoder.h" |
9 #include "platform/image-decoders/ImageFrame.h" | 9 #include "platform/image-decoders/ImageFrame.h" |
10 #include "platform/testing/UnitTestHelpers.h" | 10 #include "platform/testing/UnitTestHelpers.h" |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 Vector<unsigned>* baselineHashes) { | 48 Vector<unsigned>* baselineHashes) { |
49 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | 49 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
50 decoder->setData(data, true); | 50 decoder->setData(data, true); |
51 size_t frameCount = decoder->frameCount(); | 51 size_t frameCount = decoder->frameCount(); |
52 for (size_t i = 0; i < frameCount; ++i) { | 52 for (size_t i = 0; i < frameCount; ++i) { |
53 ImageFrame* frame = decoder->frameBufferAtIndex(i); | 53 ImageFrame* frame = decoder->frameBufferAtIndex(i); |
54 baselineHashes->append(hashBitmap(frame->bitmap())); | 54 baselineHashes->append(hashBitmap(frame->bitmap())); |
55 } | 55 } |
56 } | 56 } |
57 | 57 |
58 void testByteByByteDecode(DecoderCreator createDecoder, | 58 static void testByteByByteDecode(DecoderCreator createDecoder, |
59 const char* file, | 59 SharedBuffer* data, |
60 size_t expectedFrameCount, | 60 size_t expectedFrameCount, |
61 int expectedRepetitionCount) { | 61 int expectedRepetitionCount) { |
62 RefPtr<SharedBuffer> data = readFile(file); | |
63 ASSERT_TRUE(data.get()); | |
64 ASSERT_TRUE(data->data()); | 62 ASSERT_TRUE(data->data()); |
65 | 63 |
66 Vector<unsigned> baselineHashes; | 64 Vector<unsigned> baselineHashes; |
67 createDecodingBaseline(createDecoder, data.get(), &baselineHashes); | 65 createDecodingBaseline(createDecoder, data, &baselineHashes); |
68 | 66 |
69 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | 67 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
70 | 68 |
71 size_t frameCount = 0; | 69 size_t frameCount = 0; |
72 size_t framesDecoded = 0; | 70 size_t framesDecoded = 0; |
73 | 71 |
74 // Pass data to decoder byte by byte. | 72 // Pass data to decoder byte by byte. |
75 RefPtr<SharedBuffer> sourceData[2] = {SharedBuffer::create(), | 73 RefPtr<SharedBuffer> sourceData[2] = {SharedBuffer::create(), |
76 SharedBuffer::create()}; | 74 SharedBuffer::create()}; |
77 const char* source = data->data(); | 75 const char* source = data->data(); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 for (size_t i = 0; i < decoder->frameCount(); i++) { | 109 for (size_t i = 0; i < decoder->frameCount(); i++) { |
112 ImageFrame* frame = decoder->frameBufferAtIndex(i); | 110 ImageFrame* frame = decoder->frameBufferAtIndex(i); |
113 EXPECT_EQ(baselineHashes[i], hashBitmap(frame->bitmap())); | 111 EXPECT_EQ(baselineHashes[i], hashBitmap(frame->bitmap())); |
114 } | 112 } |
115 } | 113 } |
116 | 114 |
117 // This test verifies that calling SharedBuffer::mergeSegmentsIntoBuffer() does | 115 // This test verifies that calling SharedBuffer::mergeSegmentsIntoBuffer() does |
118 // not break decoding at a critical point: in between a call to decode the size | 116 // not break decoding at a critical point: in between a call to decode the size |
119 // (when the decoder stops while it may still have input data to read) and a | 117 // (when the decoder stops while it may still have input data to read) and a |
120 // call to do a full decode. | 118 // call to do a full decode. |
121 void testMergeBuffer(DecoderCreator createDecoder, const char* file) { | 119 static void testMergeBuffer(DecoderCreator createDecoder, SharedBuffer* data) { |
122 RefPtr<SharedBuffer> data = readFile(file); | 120 const unsigned hash = createDecodingBaseline(createDecoder, data); |
123 ASSERT_TRUE(data); | |
124 | |
125 const unsigned hash = createDecodingBaseline(createDecoder, data.get()); | |
126 | 121 |
127 // In order to do any verification, this test needs to move the data owned | 122 // In order to do any verification, this test needs to move the data owned |
128 // by the SharedBuffer. A way to guarantee that is to create a new one, and | 123 // by the SharedBuffer. A way to guarantee that is to create a new one, and |
129 // then append a string of characters greater than kSegmentSize. This | 124 // then append a string of characters greater than kSegmentSize. This |
130 // results in writing the data into a segment, skipping the internal | 125 // results in writing the data into a segment, skipping the internal |
131 // contiguous buffer. | 126 // contiguous buffer. |
132 RefPtr<SharedBuffer> segmentedData = SharedBuffer::create(); | 127 RefPtr<SharedBuffer> segmentedData = SharedBuffer::create(); |
133 segmentedData->append(data->data(), data->size()); | 128 segmentedData->append(data->data(), data->size()); |
134 | 129 |
135 std::unique_ptr<ImageDecoder> decoder = createDecoder(); | 130 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
136 decoder->setData(segmentedData.get(), true); | 131 decoder->setData(segmentedData.get(), true); |
137 | 132 |
138 ASSERT_TRUE(decoder->isSizeAvailable()); | 133 ASSERT_TRUE(decoder->isSizeAvailable()); |
139 | 134 |
140 // This will call SharedBuffer::mergeSegmentsIntoBuffer, copying all | 135 // This will call SharedBuffer::mergeSegmentsIntoBuffer, copying all |
141 // segments into the contiguous buffer. If the ImageDecoder was pointing to | 136 // segments into the contiguous buffer. If the ImageDecoder was pointing to |
142 // data in a segment, its pointer would no longer be valid. | 137 // data in a segment, its pointer would no longer be valid. |
143 segmentedData->data(); | 138 segmentedData->data(); |
144 | 139 |
145 ImageFrame* frame = decoder->frameBufferAtIndex(0); | 140 ImageFrame* frame = decoder->frameBufferAtIndex(0); |
146 ASSERT_FALSE(decoder->failed()); | 141 ASSERT_FALSE(decoder->failed()); |
147 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete); | 142 EXPECT_EQ(frame->getStatus(), ImageFrame::FrameComplete); |
148 EXPECT_EQ(hashBitmap(frame->bitmap()), hash); | 143 EXPECT_EQ(hashBitmap(frame->bitmap()), hash); |
149 } | 144 } |
150 | 145 |
| 146 static void testRandomFrameDecode(DecoderCreator createDecoder, |
| 147 SharedBuffer* fullData, |
| 148 size_t skippingStep) { |
| 149 Vector<unsigned> baselineHashes; |
| 150 createDecodingBaseline(createDecoder, fullData, &baselineHashes); |
| 151 size_t frameCount = baselineHashes.size(); |
| 152 |
| 153 // Random decoding should get the same results as sequential decoding. |
| 154 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
| 155 decoder->setData(fullData, true); |
| 156 for (size_t i = 0; i < skippingStep; ++i) { |
| 157 for (size_t j = i; j < frameCount; j += skippingStep) { |
| 158 SCOPED_TRACE(::testing::Message() << "Random i:" << i << " j:" << j); |
| 159 ImageFrame* frame = decoder->frameBufferAtIndex(j); |
| 160 EXPECT_EQ(baselineHashes[j], hashBitmap(frame->bitmap())); |
| 161 } |
| 162 } |
| 163 |
| 164 // Decoding in reverse order. |
| 165 decoder = createDecoder(); |
| 166 decoder->setData(fullData, true); |
| 167 for (size_t i = frameCount; i; --i) { |
| 168 SCOPED_TRACE(::testing::Message() << "Reverse i:" << i); |
| 169 ImageFrame* frame = decoder->frameBufferAtIndex(i - 1); |
| 170 EXPECT_EQ(baselineHashes[i - 1], hashBitmap(frame->bitmap())); |
| 171 } |
| 172 } |
| 173 |
| 174 static void testRandomDecodeAfterClearFrameBufferCache( |
| 175 DecoderCreator createDecoder, |
| 176 SharedBuffer* data, |
| 177 size_t skippingStep) { |
| 178 Vector<unsigned> baselineHashes; |
| 179 createDecodingBaseline(createDecoder, data, &baselineHashes); |
| 180 size_t frameCount = baselineHashes.size(); |
| 181 |
| 182 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
| 183 decoder->setData(data, true); |
| 184 for (size_t clearExceptFrame = 0; clearExceptFrame < frameCount; |
| 185 ++clearExceptFrame) { |
| 186 decoder->clearCacheExceptFrame(clearExceptFrame); |
| 187 for (size_t i = 0; i < skippingStep; ++i) { |
| 188 for (size_t j = 0; j < frameCount; j += skippingStep) { |
| 189 SCOPED_TRACE(::testing::Message() << "Random i:" << i << " j:" << j); |
| 190 ImageFrame* frame = decoder->frameBufferAtIndex(j); |
| 191 EXPECT_EQ(baselineHashes[j], hashBitmap(frame->bitmap())); |
| 192 } |
| 193 } |
| 194 } |
| 195 } |
| 196 |
| 197 static void testDecodeAfterReallocatingData(DecoderCreator createDecoder, |
| 198 SharedBuffer* data) { |
| 199 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
| 200 |
| 201 // Parse from 'data'. |
| 202 decoder->setData(data, true); |
| 203 size_t frameCount = decoder->frameCount(); |
| 204 |
| 205 // ... and then decode frames from 'reallocatedData'. |
| 206 RefPtr<SharedBuffer> reallocatedData = data->copy(); |
| 207 ASSERT_TRUE(reallocatedData.get()); |
| 208 data->clear(); |
| 209 decoder->setData(reallocatedData.get(), true); |
| 210 |
| 211 for (size_t i = 0; i < frameCount; ++i) { |
| 212 const ImageFrame* const frame = decoder->frameBufferAtIndex(i); |
| 213 EXPECT_EQ(ImageFrame::FrameComplete, frame->getStatus()); |
| 214 } |
| 215 } |
| 216 |
| 217 static void testByteByByteSizeAvailable(DecoderCreator createDecoder, |
| 218 SharedBuffer* data, |
| 219 size_t frameOffset, |
| 220 bool hasColorSpace, |
| 221 int expectedRepetitionCount) { |
| 222 std::unique_ptr<ImageDecoder> decoder = createDecoder(); |
| 223 EXPECT_LT(frameOffset, data->size()); |
| 224 |
| 225 // Send data to the decoder byte-by-byte and use the provided frame offset in |
| 226 // the data to check that isSizeAvailable() changes state only when that |
| 227 // offset is reached. Also check other decoder state. |
| 228 for (size_t length = 1; length <= frameOffset; ++length) { |
| 229 RefPtr<SharedBuffer> tempData = SharedBuffer::create(data->data(), length); |
| 230 decoder->setData(tempData.get(), false); |
| 231 |
| 232 if (length < frameOffset) { |
| 233 EXPECT_FALSE(decoder->isSizeAvailable()); |
| 234 EXPECT_TRUE(decoder->size().isEmpty()); |
| 235 EXPECT_FALSE(decoder->hasEmbeddedColorSpace()); |
| 236 EXPECT_EQ(0u, decoder->frameCount()); |
| 237 EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); |
| 238 EXPECT_FALSE(decoder->frameBufferAtIndex(0)); |
| 239 } else { |
| 240 EXPECT_TRUE(decoder->isSizeAvailable()); |
| 241 EXPECT_FALSE(decoder->size().isEmpty()); |
| 242 EXPECT_EQ(decoder->hasEmbeddedColorSpace(), hasColorSpace); |
| 243 EXPECT_EQ(1u, decoder->frameCount()); |
| 244 EXPECT_EQ(expectedRepetitionCount, decoder->repetitionCount()); |
| 245 } |
| 246 |
| 247 ASSERT_FALSE(decoder->failed()); |
| 248 } |
| 249 } |
| 250 |
| 251 static void testProgressiveDecoding(DecoderCreator createDecoder, |
| 252 SharedBuffer* fullData, |
| 253 size_t increment) { |
| 254 const size_t fullLength = fullData->size(); |
| 255 |
| 256 std::unique_ptr<ImageDecoder> decoder; |
| 257 |
| 258 Vector<unsigned> truncatedHashes; |
| 259 Vector<unsigned> progressiveHashes; |
| 260 |
| 261 // Compute hashes when the file is truncated. |
| 262 for (size_t i = 1; i <= fullLength; i += increment) { |
| 263 decoder = createDecoder(); |
| 264 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); |
| 265 decoder->setData(data.get(), i == fullLength); |
| 266 ImageFrame* frame = decoder->frameBufferAtIndex(0); |
| 267 if (!frame) { |
| 268 truncatedHashes.append(0); |
| 269 continue; |
| 270 } |
| 271 truncatedHashes.append(hashBitmap(frame->bitmap())); |
| 272 } |
| 273 |
| 274 // Compute hashes when the file is progressively decoded. |
| 275 decoder = createDecoder(); |
| 276 for (size_t i = 1; i <= fullLength; i += increment) { |
| 277 RefPtr<SharedBuffer> data = SharedBuffer::create(fullData->data(), i); |
| 278 decoder->setData(data.get(), i == fullLength); |
| 279 ImageFrame* frame = decoder->frameBufferAtIndex(0); |
| 280 if (!frame) { |
| 281 progressiveHashes.append(0); |
| 282 continue; |
| 283 } |
| 284 progressiveHashes.append(hashBitmap(frame->bitmap())); |
| 285 } |
| 286 |
| 287 for (size_t i = 0; i < truncatedHashes.size(); ++i) |
| 288 ASSERT_EQ(truncatedHashes[i], progressiveHashes[i]); |
| 289 } |
| 290 |
| 291 void testByteByByteDecode(DecoderCreator createDecoder, |
| 292 const char* file, |
| 293 size_t expectedFrameCount, |
| 294 int expectedRepetitionCount) { |
| 295 RefPtr<SharedBuffer> data = readFile(file); |
| 296 ASSERT_TRUE(data.get()); |
| 297 testByteByByteDecode(createDecoder, data.get(), expectedFrameCount, |
| 298 expectedRepetitionCount); |
| 299 } |
| 300 void testByteByByteDecode(DecoderCreator createDecoder, |
| 301 const char* dir, |
| 302 const char* file, |
| 303 size_t expectedFrameCount, |
| 304 int expectedRepetitionCount) { |
| 305 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 306 ASSERT_TRUE(data.get()); |
| 307 testByteByByteDecode(createDecoder, data.get(), expectedFrameCount, |
| 308 expectedRepetitionCount); |
| 309 } |
| 310 |
| 311 void testMergeBuffer(DecoderCreator createDecoder, const char* file) { |
| 312 RefPtr<SharedBuffer> data = readFile(file); |
| 313 ASSERT_TRUE(data.get()); |
| 314 testMergeBuffer(createDecoder, data.get()); |
| 315 } |
| 316 |
| 317 void testMergeBuffer(DecoderCreator createDecoder, |
| 318 const char* dir, |
| 319 const char* file) { |
| 320 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 321 ASSERT_TRUE(data.get()); |
| 322 testMergeBuffer(createDecoder, data.get()); |
| 323 } |
| 324 |
| 325 void testRandomFrameDecode(DecoderCreator createDecoder, |
| 326 const char* file, |
| 327 size_t skippingStep) { |
| 328 RefPtr<SharedBuffer> data = readFile(file); |
| 329 ASSERT_TRUE(data.get()); |
| 330 SCOPED_TRACE(file); |
| 331 testRandomFrameDecode(createDecoder, data.get(), skippingStep); |
| 332 } |
| 333 void testRandomFrameDecode(DecoderCreator createDecoder, |
| 334 const char* dir, |
| 335 const char* file, |
| 336 size_t skippingStep) { |
| 337 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 338 ASSERT_TRUE(data.get()); |
| 339 SCOPED_TRACE(file); |
| 340 testRandomFrameDecode(createDecoder, data.get(), skippingStep); |
| 341 } |
| 342 |
| 343 void testRandomDecodeAfterClearFrameBufferCache(DecoderCreator createDecoder, |
| 344 const char* file, |
| 345 size_t skippingStep) { |
| 346 RefPtr<SharedBuffer> data = readFile(file); |
| 347 ASSERT_TRUE(data.get()); |
| 348 SCOPED_TRACE(file); |
| 349 testRandomDecodeAfterClearFrameBufferCache(createDecoder, data.get(), |
| 350 skippingStep); |
| 351 } |
| 352 |
| 353 void testRandomDecodeAfterClearFrameBufferCache(DecoderCreator createDecoder, |
| 354 const char* dir, |
| 355 const char* file, |
| 356 size_t skippingStep) { |
| 357 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 358 ASSERT_TRUE(data.get()); |
| 359 SCOPED_TRACE(file); |
| 360 testRandomDecodeAfterClearFrameBufferCache(createDecoder, data.get(), |
| 361 skippingStep); |
| 362 } |
| 363 |
| 364 void testDecodeAfterReallocatingData(DecoderCreator createDecoder, |
| 365 const char* file) { |
| 366 RefPtr<SharedBuffer> data = readFile(file); |
| 367 ASSERT_TRUE(data.get()); |
| 368 testDecodeAfterReallocatingData(createDecoder, data.get()); |
| 369 } |
| 370 |
| 371 void testDecodeAfterReallocatingData(DecoderCreator createDecoder, |
| 372 const char* dir, |
| 373 const char* file) { |
| 374 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 375 ASSERT_TRUE(data.get()); |
| 376 testDecodeAfterReallocatingData(createDecoder, data.get()); |
| 377 } |
| 378 |
| 379 void testByteByByteSizeAvailable(DecoderCreator createDecoder, |
| 380 const char* file, |
| 381 size_t frameOffset, |
| 382 bool hasColorSpace, |
| 383 int expectedRepetitionCount) { |
| 384 RefPtr<SharedBuffer> data = readFile(file); |
| 385 ASSERT_TRUE(data.get()); |
| 386 testByteByByteSizeAvailable(createDecoder, data.get(), frameOffset, |
| 387 hasColorSpace, expectedRepetitionCount); |
| 388 } |
| 389 |
| 390 void testByteByByteSizeAvailable(DecoderCreator createDecoder, |
| 391 const char* dir, |
| 392 const char* file, |
| 393 size_t frameOffset, |
| 394 bool hasColorSpace, |
| 395 int expectedRepetitionCount) { |
| 396 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 397 ASSERT_TRUE(data.get()); |
| 398 testByteByByteSizeAvailable(createDecoder, data.get(), frameOffset, |
| 399 hasColorSpace, expectedRepetitionCount); |
| 400 } |
| 401 |
| 402 void testProgressiveDecoding(DecoderCreator createDecoder, |
| 403 const char* file, |
| 404 size_t increment) { |
| 405 RefPtr<SharedBuffer> data = readFile(file); |
| 406 ASSERT_TRUE(data.get()); |
| 407 testProgressiveDecoding(createDecoder, data.get(), increment); |
| 408 } |
| 409 |
| 410 void testProgressiveDecoding(DecoderCreator createDecoder, |
| 411 const char* dir, |
| 412 const char* file, |
| 413 size_t increment) { |
| 414 RefPtr<SharedBuffer> data = readFile(dir, file); |
| 415 ASSERT_TRUE(data.get()); |
| 416 testProgressiveDecoding(createDecoder, data.get(), increment); |
| 417 } |
| 418 |
151 } // namespace blink | 419 } // namespace blink |
OLD | NEW |