1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "gtest/gtest.h"
6
7 #include "Common.h"
8 #include "AnimationSurfaceProvider.h"
9 #include "DecodePool.h"
10 #include "Decoder.h"
11 #include "DecoderFactory.h"
12 #include "decoders/nsBMPDecoder.h"
13 #include "IDecodingTask.h"
14 #include "ImageOps.h"
15 #include "imgIContainer.h"
16 #include "ImageFactory.h"
17 #include "mozilla/gfx/2D.h"
18 #include "nsComponentManagerUtils.h"
19 #include "nsCOMPtr.h"
20 #include "nsIInputStream.h"
21 #include "mozilla/RefPtr.h"
22 #include "nsStreamUtils.h"
23 #include "nsString.h"
24 #include "nsThreadUtils.h"
25 #include "ProgressTracker.h"
26 #include "SourceBuffer.h"
27
28 using namespace mozilla;
29 using namespace mozilla::gfx;
30 using namespace mozilla::image;
31
CheckDecoderState(const ImageTestCase & aTestCase,image::Decoder * aDecoder)32 static already_AddRefed<SourceSurface> CheckDecoderState(
33 const ImageTestCase& aTestCase, image::Decoder* aDecoder) {
34 // image::Decoder should match what we asked for in the MIME type.
35 EXPECT_NE(aDecoder->GetType(), DecoderType::UNKNOWN);
36 EXPECT_EQ(aDecoder->GetType(),
37 DecoderFactory::GetDecoderType(aTestCase.mMimeType));
38
39 EXPECT_TRUE(aDecoder->GetDecodeDone());
40 EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR), aDecoder->HasError());
41
42 // Verify that the decoder made the expected progress.
43 Progress progress = aDecoder->TakeProgress();
44 EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR),
45 bool(progress & FLAG_HAS_ERROR));
46
47 if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
48 return nullptr; // That's all we can check for bad images.
49 }
50
51 EXPECT_TRUE(bool(progress & FLAG_SIZE_AVAILABLE));
52 EXPECT_TRUE(bool(progress & FLAG_DECODE_COMPLETE));
53 EXPECT_TRUE(bool(progress & FLAG_FRAME_COMPLETE));
54 EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT),
55 bool(progress & FLAG_HAS_TRANSPARENCY));
56 EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
57 bool(progress & FLAG_IS_ANIMATED));
58
59 // The decoder should get the correct size.
60 IntSize size = aDecoder->Size();
61 EXPECT_EQ(aTestCase.mSize.width, size.width);
62 EXPECT_EQ(aTestCase.mSize.height, size.height);
63
64 // Get the current frame, which is always the first frame of the image
65 // because CreateAnonymousDecoder() forces a first-frame-only decode.
66 RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
67 RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
68
69 // Verify that the resulting surfaces matches our expectations.
70 EXPECT_TRUE(surface->IsDataSourceSurface());
71 EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::OS_RGBX ||
72 surface->GetFormat() == SurfaceFormat::OS_RGBA);
73 EXPECT_EQ(aTestCase.mOutputSize, surface->GetSize());
74
75 return surface.forget();
76 }
77
CheckDecoderResults(const ImageTestCase & aTestCase,image::Decoder * aDecoder)78 static void CheckDecoderResults(const ImageTestCase& aTestCase,
79 image::Decoder* aDecoder) {
80 RefPtr<SourceSurface> surface = CheckDecoderState(aTestCase, aDecoder);
81 if (!surface) {
82 return;
83 }
84
85 if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) {
86 return;
87 }
88
89 // Check the output.
90 EXPECT_TRUE(IsSolidColor(surface, aTestCase.Color(), aTestCase.Fuzz()));
91 }
92
93 template <typename Func>
WithBadBufferDecode(const ImageTestCase & aTestCase,const Maybe<IntSize> & aOutputSize,Func aResultChecker)94 void WithBadBufferDecode(const ImageTestCase& aTestCase,
95 const Maybe<IntSize>& aOutputSize,
96 Func aResultChecker) {
97 // Prepare a SourceBuffer with an error that will immediately move iterators
98 // to COMPLETE.
99 auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
100 sourceBuffer->ExpectLength(SIZE_MAX);
101
102 // Create a decoder.
103 DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
104 RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
105 decoderType, sourceBuffer, aOutputSize, DecoderFlags::FIRST_FRAME_ONLY,
106 aTestCase.mSurfaceFlags);
107 ASSERT_TRUE(decoder != nullptr);
108 RefPtr<IDecodingTask> task =
109 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
110
111 // Run the full decoder synchronously on the main thread.
112 task->Run();
113
114 // Call the lambda to verify the expected results.
115 aResultChecker(decoder);
116 }
117
CheckDecoderBadBuffer(const ImageTestCase & aTestCase)118 static void CheckDecoderBadBuffer(const ImageTestCase& aTestCase) {
119 WithBadBufferDecode(aTestCase, Nothing(), [&](image::Decoder* aDecoder) {
120 CheckDecoderResults(aTestCase, aDecoder);
121 });
122 }
123
124 template <typename Func>
WithSingleChunkDecode(const ImageTestCase & aTestCase,const Maybe<IntSize> & aOutputSize,bool aUseDecodePool,Func aResultChecker)125 void WithSingleChunkDecode(const ImageTestCase& aTestCase,
126 const Maybe<IntSize>& aOutputSize,
127 bool aUseDecodePool, Func aResultChecker) {
128 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
129 ASSERT_TRUE(inputStream != nullptr);
130
131 // Figure out how much data we have.
132 uint64_t length;
133 nsresult rv = inputStream->Available(&length);
134 ASSERT_TRUE(NS_SUCCEEDED(rv));
135
136 // Write the data into a SourceBuffer.
137 auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
138 sourceBuffer->ExpectLength(length);
139 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
140 ASSERT_TRUE(NS_SUCCEEDED(rv));
141 sourceBuffer->Complete(NS_OK);
142
143 // Create a decoder.
144 DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
145 RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
146 decoderType, sourceBuffer, aOutputSize, DecoderFlags::FIRST_FRAME_ONLY,
147 aTestCase.mSurfaceFlags);
148 ASSERT_TRUE(decoder != nullptr);
149 RefPtr<IDecodingTask> task =
150 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
151
152 if (aUseDecodePool) {
153 DecodePool::Singleton()->AsyncRun(task.get());
154
155 while (!decoder->GetDecodeDone()) {
156 task->Resume();
157 }
158 } else { // Run the full decoder synchronously on the main thread.
159 task->Run();
160 }
161
162 // Call the lambda to verify the expected results.
163 aResultChecker(decoder);
164 }
165
CheckDecoderSingleChunk(const ImageTestCase & aTestCase,bool aUseDecodePool=false)166 static void CheckDecoderSingleChunk(const ImageTestCase& aTestCase,
167 bool aUseDecodePool = false) {
168 WithSingleChunkDecode(aTestCase, Nothing(), aUseDecodePool,
169 [&](image::Decoder* aDecoder) {
170 CheckDecoderResults(aTestCase, aDecoder);
171 });
172 }
173
174 template <typename Func>
WithDelayedChunkDecode(const ImageTestCase & aTestCase,const Maybe<IntSize> & aOutputSize,Func aResultChecker)175 void WithDelayedChunkDecode(const ImageTestCase& aTestCase,
176 const Maybe<IntSize>& aOutputSize,
177 Func aResultChecker) {
178 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
179 ASSERT_TRUE(inputStream != nullptr);
180
181 // Figure out how much data we have.
182 uint64_t length;
183 nsresult rv = inputStream->Available(&length);
184 ASSERT_TRUE(NS_SUCCEEDED(rv));
185
186 // Prepare an empty SourceBuffer.
187 auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
188
189 // Create a decoder.
190 DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
191 RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
192 decoderType, sourceBuffer, aOutputSize, DecoderFlags::FIRST_FRAME_ONLY,
193 aTestCase.mSurfaceFlags);
194 ASSERT_TRUE(decoder != nullptr);
195 RefPtr<IDecodingTask> task =
196 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ true);
197
198 // Run the full decoder synchronously. It should now be waiting on
199 // the iterator to yield some data since we haven't written anything yet.
200 task->Run();
201
202 // Writing all of the data should wake up the decoder to complete.
203 sourceBuffer->ExpectLength(length);
204 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
205 ASSERT_TRUE(NS_SUCCEEDED(rv));
206 sourceBuffer->Complete(NS_OK);
207
208 // It would have gotten posted to the main thread to avoid mutex contention.
209 SpinPendingEvents();
210
211 // Call the lambda to verify the expected results.
212 aResultChecker(decoder);
213 }
214
CheckDecoderDelayedChunk(const ImageTestCase & aTestCase)215 static void CheckDecoderDelayedChunk(const ImageTestCase& aTestCase) {
216 WithDelayedChunkDecode(aTestCase, Nothing(), [&](image::Decoder* aDecoder) {
217 CheckDecoderResults(aTestCase, aDecoder);
218 });
219 }
220
CheckDecoderMultiChunk(const ImageTestCase & aTestCase,uint64_t aChunkSize=1)221 static void CheckDecoderMultiChunk(const ImageTestCase& aTestCase,
222 uint64_t aChunkSize = 1) {
223 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
224 ASSERT_TRUE(inputStream != nullptr);
225
226 // Figure out how much data we have.
227 uint64_t length;
228 nsresult rv = inputStream->Available(&length);
229 ASSERT_TRUE(NS_SUCCEEDED(rv));
230
231 // Create a SourceBuffer and a decoder.
232 auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
233 sourceBuffer->ExpectLength(length);
234 DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
235 RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
236 decoderType, sourceBuffer, Nothing(), DecoderFlags::FIRST_FRAME_ONLY,
237 aTestCase.mSurfaceFlags);
238 ASSERT_TRUE(decoder != nullptr);
239 RefPtr<IDecodingTask> task =
240 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ true);
241
242 // Run the full decoder synchronously. It should now be waiting on
243 // the iterator to yield some data since we haven't written anything yet.
244 task->Run();
245
246 while (length > 0) {
247 uint64_t read = length > aChunkSize ? aChunkSize : length;
248 length -= read;
249
250 uint64_t available = 0;
251 rv = inputStream->Available(&available);
252 ASSERT_TRUE(available >= read);
253 ASSERT_TRUE(NS_SUCCEEDED(rv));
254
255 // Writing any data should wake up the decoder to complete.
256 rv = sourceBuffer->AppendFromInputStream(inputStream, read);
257 ASSERT_TRUE(NS_SUCCEEDED(rv));
258
259 // It would have gotten posted to the main thread to avoid mutex contention.
260 SpinPendingEvents();
261 }
262
263 sourceBuffer->Complete(NS_OK);
264 SpinPendingEvents();
265
266 CheckDecoderResults(aTestCase, decoder);
267 }
268
CheckDownscaleDuringDecode(const ImageTestCase & aTestCase)269 static void CheckDownscaleDuringDecode(const ImageTestCase& aTestCase) {
270 // This function expects that |aTestCase| consists of 25 lines of green,
271 // followed by 25 lines of red, followed by 25 lines of green, followed by 25
272 // more lines of red. We'll downscale it from 100x100 to 20x20.
273 IntSize outputSize(20, 20);
274
275 WithSingleChunkDecode(
276 aTestCase, Some(outputSize), /* aUseDecodePool */ false,
277 [&](image::Decoder* aDecoder) {
278 RefPtr<SourceSurface> surface = CheckDecoderState(aTestCase, aDecoder);
279
280 // There are no downscale-during-decode tests that have
281 // TEST_CASE_HAS_ERROR set, so we expect to always get a surface here.
282 EXPECT_TRUE(surface != nullptr);
283
284 if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) {
285 return;
286 }
287
288 // Check that the downscaled image is correct. Note that we skip rows
289 // near the transitions between colors, since the downscaler does not
290 // produce a sharp boundary at these points. Even some of the rows we
291 // test need a small amount of fuzz; this is just the nature of Lanczos
292 // downscaling.
293 EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4,
294 aTestCase.ChooseColor(BGRAColor::Green()),
295 /* aFuzz = */ 47));
296 EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3,
297 aTestCase.ChooseColor(BGRAColor::Red()),
298 /* aFuzz = */ 27));
299 EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(),
300 /* aFuzz = */ 47));
301 EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4,
302 aTestCase.ChooseColor(BGRAColor::Red()),
303 /* aFuzz = */ 27));
304 });
305 }
306
CheckAnimationDecoderResults(const ImageTestCase & aTestCase,AnimationSurfaceProvider * aProvider,image::Decoder * aDecoder)307 static void CheckAnimationDecoderResults(const ImageTestCase& aTestCase,
308 AnimationSurfaceProvider* aProvider,
309 image::Decoder* aDecoder) {
310 EXPECT_TRUE(aDecoder->GetDecodeDone());
311 EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR), aDecoder->HasError());
312
313 if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
314 return; // That's all we can check for bad images.
315 }
316
317 // The decoder should get the correct size.
318 IntSize size = aDecoder->Size();
319 EXPECT_EQ(aTestCase.mSize.width, size.width);
320 EXPECT_EQ(aTestCase.mSize.height, size.height);
321
322 if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) {
323 return;
324 }
325
326 // Check the output.
327 AutoTArray<BGRAColor, 2> framePixels;
328 framePixels.AppendElement(aTestCase.ChooseColor(BGRAColor::Green()));
329 framePixels.AppendElement(
330 aTestCase.ChooseColor(BGRAColor(0x7F, 0x7F, 0x7F, 0xFF)));
331
332 DrawableSurface drawableSurface(WrapNotNull(aProvider));
333 for (size_t i = 0; i < framePixels.Length(); ++i) {
334 nsresult rv = drawableSurface.Seek(i);
335 EXPECT_TRUE(NS_SUCCEEDED(rv));
336
337 // Check the first frame, all green.
338 RawAccessFrameRef rawFrame = drawableSurface->RawAccessRef();
339 RefPtr<SourceSurface> surface = rawFrame->GetSourceSurface();
340
341 // Verify that the resulting surfaces matches our expectations.
342 EXPECT_TRUE(surface->IsDataSourceSurface());
343 EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::OS_RGBX ||
344 surface->GetFormat() == SurfaceFormat::OS_RGBA);
345 EXPECT_EQ(aTestCase.mOutputSize, surface->GetSize());
346 EXPECT_TRUE(IsSolidColor(surface, framePixels[i], aTestCase.Fuzz()));
347 }
348
349 // Should be no more frames.
350 nsresult rv = drawableSurface.Seek(framePixels.Length());
351 EXPECT_TRUE(NS_FAILED(rv));
352 }
353
354 template <typename Func>
WithSingleChunkAnimationDecode(const ImageTestCase & aTestCase,Func aResultChecker)355 static void WithSingleChunkAnimationDecode(const ImageTestCase& aTestCase,
356 Func aResultChecker) {
357 // Create an image.
358 RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
359 nsDependentCString(aTestCase.mMimeType));
360 ASSERT_TRUE(!image->HasError());
361
362 NotNull<RefPtr<RasterImage>> rasterImage =
363 WrapNotNull(static_cast<RasterImage*>(image.get()));
364
365 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
366 ASSERT_TRUE(inputStream != nullptr);
367
368 // Figure out how much data we have.
369 uint64_t length;
370 nsresult rv = inputStream->Available(&length);
371 ASSERT_TRUE(NS_SUCCEEDED(rv));
372
373 // Write the data into a SourceBuffer.
374 NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer());
375 sourceBuffer->ExpectLength(length);
376 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
377 ASSERT_TRUE(NS_SUCCEEDED(rv));
378 sourceBuffer->Complete(NS_OK);
379
380 // Create a metadata decoder first, because otherwise RasterImage will get
381 // unhappy about finding out the image is animated during a full decode.
382 DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
383 RefPtr<IDecodingTask> task = DecoderFactory::CreateMetadataDecoder(
384 decoderType, rasterImage, sourceBuffer);
385 ASSERT_TRUE(task != nullptr);
386
387 // Run the metadata decoder synchronously.
388 task->Run();
389
390 // Create a decoder.
391 DecoderFlags decoderFlags = DefaultDecoderFlags();
392 SurfaceFlags surfaceFlags = aTestCase.mSurfaceFlags;
393 RefPtr<image::Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
394 decoderType, sourceBuffer, Nothing(), decoderFlags, surfaceFlags);
395 ASSERT_TRUE(decoder != nullptr);
396
397 // Create an AnimationSurfaceProvider which will manage the decoding process
398 // and make this decoder's output available in the surface cache.
399 SurfaceKey surfaceKey = RasterSurfaceKey(aTestCase.mOutputSize, surfaceFlags,
400 PlaybackType::eAnimated);
401 RefPtr<AnimationSurfaceProvider> provider = new AnimationSurfaceProvider(
402 rasterImage, surfaceKey, WrapNotNull(decoder),
403 /* aCurrentFrame */ 0);
404
405 // Run the full decoder synchronously.
406 provider->Run();
407
408 // Call the lambda to verify the expected results.
409 aResultChecker(provider, decoder);
410 }
411
CheckAnimationDecoderSingleChunk(const ImageTestCase & aTestCase)412 static void CheckAnimationDecoderSingleChunk(const ImageTestCase& aTestCase) {
413 WithSingleChunkAnimationDecode(
414 aTestCase,
415 [&](AnimationSurfaceProvider* aProvider, image::Decoder* aDecoder) {
416 CheckAnimationDecoderResults(aTestCase, aProvider, aDecoder);
417 });
418 }
419
CheckDecoderFrameFirst(const ImageTestCase & aTestCase)420 static void CheckDecoderFrameFirst(const ImageTestCase& aTestCase) {
421 // Verify that we can decode this test case and retrieve the first frame using
422 // imgIContainer::FRAME_FIRST. This ensures that we correctly trigger a
423 // single-frame decode rather than an animated decode when
424 // imgIContainer::FRAME_FIRST is requested.
425
426 // Create an image.
427 RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
428 nsDependentCString(aTestCase.mMimeType));
429 ASSERT_TRUE(!image->HasError());
430
431 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
432 ASSERT_TRUE(inputStream);
433
434 // Figure out how much data we have.
435 uint64_t length;
436 nsresult rv = inputStream->Available(&length);
437 ASSERT_TRUE(NS_SUCCEEDED(rv));
438
439 // Write the data into the image.
440 rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
441 static_cast<uint32_t>(length));
442 ASSERT_TRUE(NS_SUCCEEDED(rv));
443
444 // Let the image know we've sent all the data.
445 rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
446 ASSERT_TRUE(NS_SUCCEEDED(rv));
447
448 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
449 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
450
451 // Lock the image so its surfaces don't disappear during the test.
452 image->LockImage();
453
454 auto unlock = mozilla::MakeScopeExit([&] { image->UnlockImage(); });
455
456 // Use GetFrame() to force a sync decode of the image, specifying FRAME_FIRST
457 // to ensure that we don't get an animated decode.
458 RefPtr<SourceSurface> surface = image->GetFrame(
459 imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
460
461 // Ensure that the image's metadata meets our expectations.
462 IntSize imageSize(0, 0);
463 rv = image->GetWidth(&imageSize.width);
464 EXPECT_TRUE(NS_SUCCEEDED(rv));
465 rv = image->GetHeight(&imageSize.height);
466 EXPECT_TRUE(NS_SUCCEEDED(rv));
467
468 EXPECT_EQ(aTestCase.mSize.width, imageSize.width);
469 EXPECT_EQ(aTestCase.mSize.height, imageSize.height);
470
471 Progress imageProgress = tracker->GetProgress();
472
473 EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
474 EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
475
476 // Ensure that we decoded the static version of the image.
477 {
478 LookupResult result = SurfaceCache::Lookup(
479 ImageKey(image.get()),
480 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
481 PlaybackType::eStatic),
482 /* aMarkUsed = */ false);
483 ASSERT_EQ(MatchType::EXACT, result.Type());
484 EXPECT_TRUE(bool(result.Surface()));
485 }
486
487 // Ensure that we didn't decode the animated version of the image.
488 {
489 LookupResult result = SurfaceCache::Lookup(
490 ImageKey(image.get()),
491 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
492 PlaybackType::eAnimated),
493 /* aMarkUsed = */ false);
494 ASSERT_EQ(MatchType::NOT_FOUND, result.Type());
495 }
496
497 // Use GetFrame() to force a sync decode of the image, this time specifying
498 // FRAME_CURRENT to ensure that we get an animated decode.
499 RefPtr<SourceSurface> animatedSurface = image->GetFrame(
500 imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
501
502 // Ensure that we decoded both frames of the animated version of the image.
503 {
504 LookupResult result = SurfaceCache::Lookup(
505 ImageKey(image.get()),
506 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
507 PlaybackType::eAnimated),
508 /* aMarkUsed = */ true);
509 ASSERT_EQ(MatchType::EXACT, result.Type());
510
511 EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
512 EXPECT_TRUE(bool(result.Surface()));
513
514 RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
515 EXPECT_TRUE(bool(partialFrame));
516 }
517
518 // Ensure that the static version is still around.
519 {
520 LookupResult result = SurfaceCache::Lookup(
521 ImageKey(image.get()),
522 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
523 PlaybackType::eStatic),
524 /* aMarkUsed = */ true);
525 ASSERT_EQ(MatchType::EXACT, result.Type());
526 EXPECT_TRUE(bool(result.Surface()));
527 }
528 }
529
CheckDecoderFrameCurrent(const ImageTestCase & aTestCase)530 static void CheckDecoderFrameCurrent(const ImageTestCase& aTestCase) {
531 // Verify that we can decode this test case and retrieve the entire sequence
532 // of frames using imgIContainer::FRAME_CURRENT. This ensures that we
533 // correctly trigger an animated decode rather than a single-frame decode when
534 // imgIContainer::FRAME_CURRENT is requested.
535
536 // Create an image.
537 RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
538 nsDependentCString(aTestCase.mMimeType));
539 ASSERT_TRUE(!image->HasError());
540
541 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
542 ASSERT_TRUE(inputStream);
543
544 // Figure out how much data we have.
545 uint64_t length;
546 nsresult rv = inputStream->Available(&length);
547 ASSERT_TRUE(NS_SUCCEEDED(rv));
548
549 // Write the data into the image.
550 rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
551 static_cast<uint32_t>(length));
552 ASSERT_TRUE(NS_SUCCEEDED(rv));
553
554 // Let the image know we've sent all the data.
555 rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
556 ASSERT_TRUE(NS_SUCCEEDED(rv));
557
558 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
559 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
560
561 // Lock the image so its surfaces don't disappear during the test.
562 image->LockImage();
563
564 // Use GetFrame() to force a sync decode of the image, specifying
565 // FRAME_CURRENT to ensure we get an animated decode.
566 RefPtr<SourceSurface> surface = image->GetFrame(
567 imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
568
569 // Ensure that the image's metadata meets our expectations.
570 IntSize imageSize(0, 0);
571 rv = image->GetWidth(&imageSize.width);
572 EXPECT_TRUE(NS_SUCCEEDED(rv));
573 rv = image->GetHeight(&imageSize.height);
574 EXPECT_TRUE(NS_SUCCEEDED(rv));
575
576 EXPECT_EQ(aTestCase.mSize.width, imageSize.width);
577 EXPECT_EQ(aTestCase.mSize.height, imageSize.height);
578
579 Progress imageProgress = tracker->GetProgress();
580
581 EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
582 EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
583
584 // Ensure that we decoded both frames of the animated version of the image.
585 {
586 LookupResult result = SurfaceCache::Lookup(
587 ImageKey(image.get()),
588 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
589 PlaybackType::eAnimated),
590 /* aMarkUsed = */ true);
591 ASSERT_EQ(MatchType::EXACT, result.Type());
592
593 EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
594 EXPECT_TRUE(bool(result.Surface()));
595
596 RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
597 EXPECT_TRUE(bool(partialFrame));
598 }
599
600 // Ensure that we didn't decode the static version of the image.
601 {
602 LookupResult result = SurfaceCache::Lookup(
603 ImageKey(image.get()),
604 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
605 PlaybackType::eStatic),
606 /* aMarkUsed = */ false);
607 ASSERT_EQ(MatchType::NOT_FOUND, result.Type());
608 }
609
610 // Use GetFrame() to force a sync decode of the image, this time specifying
611 // FRAME_FIRST to ensure that we get a single-frame decode.
612 RefPtr<SourceSurface> animatedSurface = image->GetFrame(
613 imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
614
615 // Ensure that we decoded the static version of the image.
616 {
617 LookupResult result = SurfaceCache::Lookup(
618 ImageKey(image.get()),
619 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
620 PlaybackType::eStatic),
621 /* aMarkUsed = */ true);
622 ASSERT_EQ(MatchType::EXACT, result.Type());
623 EXPECT_TRUE(bool(result.Surface()));
624 }
625
626 // Ensure that both frames of the animated version are still around.
627 {
628 LookupResult result = SurfaceCache::Lookup(
629 ImageKey(image.get()),
630 RasterSurfaceKey(imageSize, aTestCase.mSurfaceFlags,
631 PlaybackType::eAnimated),
632 /* aMarkUsed = */ true);
633 ASSERT_EQ(MatchType::EXACT, result.Type());
634
635 EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
636 EXPECT_TRUE(bool(result.Surface()));
637
638 RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
639 EXPECT_TRUE(bool(partialFrame));
640 }
641 }
642
643 class ImageDecoders : public ::testing::Test {
644 protected:
645 AutoInitializeImageLib mInit;
646 };
647
648 #define IMAGE_GTEST_DECODER_BASE_F(test_prefix) \
649 TEST_F(ImageDecoders, test_prefix##SingleChunk) { \
650 CheckDecoderSingleChunk(Green##test_prefix##TestCase()); \
651 } \
652 \
653 TEST_F(ImageDecoders, test_prefix##DelayedChunk) { \
654 CheckDecoderDelayedChunk(Green##test_prefix##TestCase()); \
655 } \
656 \
657 TEST_F(ImageDecoders, test_prefix##MultiChunk) { \
658 CheckDecoderMultiChunk(Green##test_prefix##TestCase()); \
659 } \
660 \
661 TEST_F(ImageDecoders, test_prefix##DownscaleDuringDecode) { \
662 CheckDownscaleDuringDecode(Downscaled##test_prefix##TestCase()); \
663 } \
664 \
665 TEST_F(ImageDecoders, test_prefix##ForceSRGB) { \
666 CheckDecoderSingleChunk(Green##test_prefix##TestCase().WithSurfaceFlags( \
667 SurfaceFlags::TO_SRGB_COLORSPACE)); \
668 } \
669 \
670 TEST_F(ImageDecoders, test_prefix##BadBuffer) { \
671 CheckDecoderBadBuffer(Green##test_prefix##TestCase().WithFlags( \
672 TEST_CASE_HAS_ERROR | TEST_CASE_IGNORE_OUTPUT)); \
673 }
674
675 IMAGE_GTEST_DECODER_BASE_F(PNG)
IMAGE_GTEST_DECODER_BASE_F(GIF)676 IMAGE_GTEST_DECODER_BASE_F(GIF)
677 IMAGE_GTEST_DECODER_BASE_F(JPG)
678 IMAGE_GTEST_DECODER_BASE_F(BMP)
679 IMAGE_GTEST_DECODER_BASE_F(ICO)
680 IMAGE_GTEST_DECODER_BASE_F(Icon)
681 IMAGE_GTEST_DECODER_BASE_F(WebP)
682
683 TEST_F(ImageDecoders, ICOWithANDMaskDownscaleDuringDecode) {
684 CheckDownscaleDuringDecode(DownscaledTransparentICOWithANDMaskTestCase());
685 }
686
TEST_F(ImageDecoders,WebPLargeMultiChunk)687 TEST_F(ImageDecoders, WebPLargeMultiChunk) {
688 CheckDecoderMultiChunk(LargeWebPTestCase(), /* aChunkSize */ 64);
689 }
690
TEST_F(ImageDecoders,WebPIccSrgbMultiChunk)691 TEST_F(ImageDecoders, WebPIccSrgbMultiChunk) {
692 CheckDecoderMultiChunk(GreenWebPIccSrgbTestCase());
693 }
694
TEST_F(ImageDecoders,WebPTransparentSingleChunk)695 TEST_F(ImageDecoders, WebPTransparentSingleChunk) {
696 CheckDecoderSingleChunk(TransparentWebPTestCase());
697 }
698
TEST_F(ImageDecoders,WebPTransparentNoAlphaHeaderSingleChunk)699 TEST_F(ImageDecoders, WebPTransparentNoAlphaHeaderSingleChunk) {
700 CheckDecoderSingleChunk(TransparentNoAlphaHeaderWebPTestCase());
701 }
702
TEST_F(ImageDecoders,AVIFSingleChunk)703 TEST_F(ImageDecoders, AVIFSingleChunk) {
704 CheckDecoderSingleChunk(GreenAVIFTestCase());
705 }
706
707 // This test must use the decode pool in order to check for regressions
708 // of crashing the dav1d decoder when the ImgDecoder threads have a standard-
709 // sized stack.
TEST_F(ImageDecoders,AVIFStackCheck)710 TEST_F(ImageDecoders, AVIFStackCheck) {
711 CheckDecoderSingleChunk(StackCheckAVIFTestCase(), /* aUseDecodePool */ true);
712 }
713
TEST_F(ImageDecoders,AVIFDelayedChunk)714 TEST_F(ImageDecoders, AVIFDelayedChunk) {
715 CheckDecoderDelayedChunk(GreenAVIFTestCase());
716 }
717
TEST_F(ImageDecoders,AVIFMultiChunk)718 TEST_F(ImageDecoders, AVIFMultiChunk) {
719 CheckDecoderMultiChunk(GreenAVIFTestCase());
720 }
721
TEST_F(ImageDecoders,AVIFLargeMultiChunk)722 TEST_F(ImageDecoders, AVIFLargeMultiChunk) {
723 CheckDecoderMultiChunk(LargeAVIFTestCase(), /* aChunkSize */ 64);
724 }
725
TEST_F(ImageDecoders,AVIFDownscaleDuringDecode)726 TEST_F(ImageDecoders, AVIFDownscaleDuringDecode) {
727 CheckDownscaleDuringDecode(DownscaledAVIFTestCase());
728 }
729
TEST_F(ImageDecoders,AnimatedGIFSingleChunk)730 TEST_F(ImageDecoders, AnimatedGIFSingleChunk) {
731 CheckDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase());
732 }
733
TEST_F(ImageDecoders,AnimatedGIFMultiChunk)734 TEST_F(ImageDecoders, AnimatedGIFMultiChunk) {
735 CheckDecoderMultiChunk(GreenFirstFrameAnimatedGIFTestCase());
736 }
737
TEST_F(ImageDecoders,AnimatedGIFWithBlendedFrames)738 TEST_F(ImageDecoders, AnimatedGIFWithBlendedFrames) {
739 CheckAnimationDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase());
740 }
741
TEST_F(ImageDecoders,AnimatedPNGSingleChunk)742 TEST_F(ImageDecoders, AnimatedPNGSingleChunk) {
743 CheckDecoderSingleChunk(GreenFirstFrameAnimatedPNGTestCase());
744 }
745
TEST_F(ImageDecoders,AnimatedPNGMultiChunk)746 TEST_F(ImageDecoders, AnimatedPNGMultiChunk) {
747 CheckDecoderMultiChunk(GreenFirstFrameAnimatedPNGTestCase());
748 }
749
TEST_F(ImageDecoders,AnimatedPNGWithBlendedFrames)750 TEST_F(ImageDecoders, AnimatedPNGWithBlendedFrames) {
751 CheckAnimationDecoderSingleChunk(GreenFirstFrameAnimatedPNGTestCase());
752 }
753
TEST_F(ImageDecoders,AnimatedWebPSingleChunk)754 TEST_F(ImageDecoders, AnimatedWebPSingleChunk) {
755 CheckDecoderSingleChunk(GreenFirstFrameAnimatedWebPTestCase());
756 }
757
TEST_F(ImageDecoders,AnimatedWebPMultiChunk)758 TEST_F(ImageDecoders, AnimatedWebPMultiChunk) {
759 CheckDecoderMultiChunk(GreenFirstFrameAnimatedWebPTestCase());
760 }
761
TEST_F(ImageDecoders,AnimatedWebPWithBlendedFrames)762 TEST_F(ImageDecoders, AnimatedWebPWithBlendedFrames) {
763 CheckAnimationDecoderSingleChunk(GreenFirstFrameAnimatedWebPTestCase());
764 }
765
TEST_F(ImageDecoders,CorruptSingleChunk)766 TEST_F(ImageDecoders, CorruptSingleChunk) {
767 CheckDecoderSingleChunk(CorruptTestCase());
768 }
769
TEST_F(ImageDecoders,CorruptMultiChunk)770 TEST_F(ImageDecoders, CorruptMultiChunk) {
771 CheckDecoderMultiChunk(CorruptTestCase());
772 }
773
TEST_F(ImageDecoders,CorruptBMPWithTruncatedHeaderSingleChunk)774 TEST_F(ImageDecoders, CorruptBMPWithTruncatedHeaderSingleChunk) {
775 CheckDecoderSingleChunk(CorruptBMPWithTruncatedHeader());
776 }
777
TEST_F(ImageDecoders,CorruptBMPWithTruncatedHeaderMultiChunk)778 TEST_F(ImageDecoders, CorruptBMPWithTruncatedHeaderMultiChunk) {
779 CheckDecoderMultiChunk(CorruptBMPWithTruncatedHeader());
780 }
781
TEST_F(ImageDecoders,CorruptICOWithBadBMPWidthSingleChunk)782 TEST_F(ImageDecoders, CorruptICOWithBadBMPWidthSingleChunk) {
783 CheckDecoderSingleChunk(CorruptICOWithBadBMPWidthTestCase());
784 }
785
TEST_F(ImageDecoders,CorruptICOWithBadBMPWidthMultiChunk)786 TEST_F(ImageDecoders, CorruptICOWithBadBMPWidthMultiChunk) {
787 CheckDecoderMultiChunk(CorruptICOWithBadBMPWidthTestCase());
788 }
789
TEST_F(ImageDecoders,CorruptICOWithBadBMPHeightSingleChunk)790 TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightSingleChunk) {
791 CheckDecoderSingleChunk(CorruptICOWithBadBMPHeightTestCase());
792 }
793
TEST_F(ImageDecoders,CorruptICOWithBadBMPHeightMultiChunk)794 TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightMultiChunk) {
795 CheckDecoderMultiChunk(CorruptICOWithBadBMPHeightTestCase());
796 }
797
TEST_F(ImageDecoders,CorruptICOWithBadBppSingleChunk)798 TEST_F(ImageDecoders, CorruptICOWithBadBppSingleChunk) {
799 CheckDecoderSingleChunk(CorruptICOWithBadBppTestCase());
800 }
801
TEST_F(ImageDecoders,AnimatedGIFWithFRAME_FIRST)802 TEST_F(ImageDecoders, AnimatedGIFWithFRAME_FIRST) {
803 CheckDecoderFrameFirst(GreenFirstFrameAnimatedGIFTestCase());
804 }
805
TEST_F(ImageDecoders,AnimatedGIFWithFRAME_CURRENT)806 TEST_F(ImageDecoders, AnimatedGIFWithFRAME_CURRENT) {
807 CheckDecoderFrameCurrent(GreenFirstFrameAnimatedGIFTestCase());
808 }
809
TEST_F(ImageDecoders,AnimatedGIFWithExtraImageSubBlocks)810 TEST_F(ImageDecoders, AnimatedGIFWithExtraImageSubBlocks) {
811 ImageTestCase testCase = ExtraImageSubBlocksAnimatedGIFTestCase();
812
813 // Verify that we can decode this test case and get two frames, even though
814 // there are extra image sub blocks between the first and second frame. The
815 // extra data shouldn't confuse the decoder or cause the decode to fail.
816
817 // Create an image.
818 RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
819 nsDependentCString(testCase.mMimeType));
820 ASSERT_TRUE(!image->HasError());
821
822 nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
823 ASSERT_TRUE(inputStream);
824
825 // Figure out how much data we have.
826 uint64_t length;
827 nsresult rv = inputStream->Available(&length);
828 ASSERT_TRUE(NS_SUCCEEDED(rv));
829
830 // Write the data into the image.
831 rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
832 static_cast<uint32_t>(length));
833 ASSERT_TRUE(NS_SUCCEEDED(rv));
834
835 // Let the image know we've sent all the data.
836 rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
837 ASSERT_TRUE(NS_SUCCEEDED(rv));
838
839 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
840 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
841
842 // Use GetFrame() to force a sync decode of the image.
843 RefPtr<SourceSurface> surface = image->GetFrame(
844 imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
845
846 // Ensure that the image's metadata meets our expectations.
847 IntSize imageSize(0, 0);
848 rv = image->GetWidth(&imageSize.width);
849 EXPECT_TRUE(NS_SUCCEEDED(rv));
850 rv = image->GetHeight(&imageSize.height);
851 EXPECT_TRUE(NS_SUCCEEDED(rv));
852
853 EXPECT_EQ(testCase.mSize.width, imageSize.width);
854 EXPECT_EQ(testCase.mSize.height, imageSize.height);
855
856 Progress imageProgress = tracker->GetProgress();
857
858 EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
859 EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
860
861 // Ensure that we decoded both frames of the image.
862 LookupResult result =
863 SurfaceCache::Lookup(ImageKey(image.get()),
864 RasterSurfaceKey(imageSize, testCase.mSurfaceFlags,
865 PlaybackType::eAnimated),
866 /* aMarkUsed = */ true);
867 ASSERT_EQ(MatchType::EXACT, result.Type());
868
869 EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
870 EXPECT_TRUE(bool(result.Surface()));
871
872 RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
873 EXPECT_TRUE(bool(partialFrame));
874 }
875
TEST_F(ImageDecoders,AnimatedWebPWithFRAME_FIRST)876 TEST_F(ImageDecoders, AnimatedWebPWithFRAME_FIRST) {
877 CheckDecoderFrameFirst(GreenFirstFrameAnimatedWebPTestCase());
878 }
879
TEST_F(ImageDecoders,AnimatedWebPWithFRAME_CURRENT)880 TEST_F(ImageDecoders, AnimatedWebPWithFRAME_CURRENT) {
881 CheckDecoderFrameCurrent(GreenFirstFrameAnimatedWebPTestCase());
882 }
883
TEST_F(ImageDecoders,TruncatedSmallGIFSingleChunk)884 TEST_F(ImageDecoders, TruncatedSmallGIFSingleChunk) {
885 CheckDecoderSingleChunk(TruncatedSmallGIFTestCase());
886 }
887
TEST_F(ImageDecoders,LargeICOWithBMPSingleChunk)888 TEST_F(ImageDecoders, LargeICOWithBMPSingleChunk) {
889 CheckDecoderSingleChunk(LargeICOWithBMPTestCase());
890 }
891
TEST_F(ImageDecoders,LargeICOWithBMPMultiChunk)892 TEST_F(ImageDecoders, LargeICOWithBMPMultiChunk) {
893 CheckDecoderMultiChunk(LargeICOWithBMPTestCase(), /* aChunkSize */ 64);
894 }
895
TEST_F(ImageDecoders,LargeICOWithPNGSingleChunk)896 TEST_F(ImageDecoders, LargeICOWithPNGSingleChunk) {
897 CheckDecoderSingleChunk(LargeICOWithPNGTestCase());
898 }
899
TEST_F(ImageDecoders,LargeICOWithPNGMultiChunk)900 TEST_F(ImageDecoders, LargeICOWithPNGMultiChunk) {
901 CheckDecoderMultiChunk(LargeICOWithPNGTestCase());
902 }
903
TEST_F(ImageDecoders,MultipleSizesICOSingleChunk)904 TEST_F(ImageDecoders, MultipleSizesICOSingleChunk) {
905 ImageTestCase testCase = GreenMultipleSizesICOTestCase();
906
907 // Create an image.
908 RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
909 nsDependentCString(testCase.mMimeType));
910 ASSERT_TRUE(!image->HasError());
911
912 nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
913 ASSERT_TRUE(inputStream);
914
915 // Figure out how much data we have.
916 uint64_t length;
917 nsresult rv = inputStream->Available(&length);
918 ASSERT_TRUE(NS_SUCCEEDED(rv));
919
920 // Write the data into the image.
921 rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
922 static_cast<uint32_t>(length));
923 ASSERT_TRUE(NS_SUCCEEDED(rv));
924
925 // Let the image know we've sent all the data.
926 rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
927 ASSERT_TRUE(NS_SUCCEEDED(rv));
928
929 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
930 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
931
932 // Use GetFrame() to force a sync decode of the image.
933 RefPtr<SourceSurface> surface = image->GetFrame(
934 imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
935
936 // Ensure that the image's metadata meets our expectations.
937 IntSize imageSize(0, 0);
938 rv = image->GetWidth(&imageSize.width);
939 EXPECT_TRUE(NS_SUCCEEDED(rv));
940 rv = image->GetHeight(&imageSize.height);
941 EXPECT_TRUE(NS_SUCCEEDED(rv));
942
943 EXPECT_EQ(testCase.mSize.width, imageSize.width);
944 EXPECT_EQ(testCase.mSize.height, imageSize.height);
945
946 nsTArray<IntSize> nativeSizes;
947 rv = image->GetNativeSizes(nativeSizes);
948 EXPECT_TRUE(NS_SUCCEEDED(rv));
949 ASSERT_EQ(6u, nativeSizes.Length());
950
951 IntSize expectedSizes[] = {IntSize(16, 16), IntSize(32, 32),
952 IntSize(64, 64), IntSize(128, 128),
953 IntSize(256, 256), IntSize(256, 128)};
954
955 for (int i = 0; i < 6; ++i) {
956 EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
957 }
958
959 RefPtr<Image> image90 =
960 ImageOps::Orient(image, Orientation(Angle::D90, Flip::Unflipped));
961 rv = image90->GetNativeSizes(nativeSizes);
962 EXPECT_TRUE(NS_SUCCEEDED(rv));
963 ASSERT_EQ(6u, nativeSizes.Length());
964
965 for (int i = 0; i < 5; ++i) {
966 EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
967 }
968 EXPECT_EQ(IntSize(128, 256), nativeSizes[5]);
969
970 RefPtr<Image> image180 =
971 ImageOps::Orient(image, Orientation(Angle::D180, Flip::Unflipped));
972 rv = image180->GetNativeSizes(nativeSizes);
973 EXPECT_TRUE(NS_SUCCEEDED(rv));
974 ASSERT_EQ(6u, nativeSizes.Length());
975
976 for (int i = 0; i < 6; ++i) {
977 EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
978 }
979 }
980