1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "Common.h"
7 
8 #include <cstdlib>
9 
10 #include "gfxPlatform.h"
11 
12 #include "imgITools.h"
13 #include "nsDirectoryServiceDefs.h"
14 #include "nsIFile.h"
15 #include "nsIInputStream.h"
16 #include "nsIProperties.h"
17 #include "nsNetUtil.h"
18 #include "mozilla/RefPtr.h"
19 #include "nsStreamUtils.h"
20 #include "nsString.h"
21 
22 namespace mozilla {
23 namespace image {
24 
25 using namespace gfx;
26 
27 using std::vector;
28 
29 static bool sImageLibInitialized = false;
30 
AutoInitializeImageLib()31 AutoInitializeImageLib::AutoInitializeImageLib() {
32   if (MOZ_LIKELY(sImageLibInitialized)) {
33     return;
34   }
35 
36   EXPECT_TRUE(NS_IsMainThread());
37   sImageLibInitialized = true;
38 
39   // Ensure WebP is enabled to run decoder tests.
40   nsresult rv = Preferences::SetBool("image.webp.enabled", true);
41   EXPECT_TRUE(rv == NS_OK);
42 
43   // Ensure AVIF is enabled to run decoder tests.
44   rv = Preferences::SetBool("image.avif.enabled", true);
45   EXPECT_TRUE(rv == NS_OK);
46 
47   // Ensure that ImageLib services are initialized.
48   nsCOMPtr<imgITools> imgTools =
49       do_CreateInstance("@mozilla.org/image/tools;1");
50   EXPECT_TRUE(imgTools != nullptr);
51 
52   // Ensure gfxPlatform is initialized.
53   gfxPlatform::GetPlatform();
54 
55   // Ensure we always color manage images with gtests.
56   gfxPlatform::GetCMSMode();
57   gfxPlatform::SetCMSModeOverride(eCMSMode_All);
58 
59   // Depending on initialization order, it is possible that our pref changes
60   // have not taken effect yet because there are pending gfx-related events on
61   // the main thread.
62   SpinPendingEvents();
63 }
64 
SetUp()65 void ImageBenchmarkBase::SetUp() {
66   nsCOMPtr<nsIInputStream> inputStream = LoadFile(mTestCase.mPath);
67   ASSERT_TRUE(inputStream != nullptr);
68 
69   // Figure out how much data we have.
70   uint64_t length;
71   nsresult rv = inputStream->Available(&length);
72   ASSERT_TRUE(NS_SUCCEEDED(rv));
73 
74   // Write the data into a SourceBuffer.
75   mSourceBuffer = new SourceBuffer();
76   mSourceBuffer->ExpectLength(length);
77   rv = mSourceBuffer->AppendFromInputStream(inputStream, length);
78   ASSERT_TRUE(NS_SUCCEEDED(rv));
79   mSourceBuffer->Complete(NS_OK);
80 }
81 
TearDown()82 void ImageBenchmarkBase::TearDown() {}
83 
84 ///////////////////////////////////////////////////////////////////////////////
85 // General Helpers
86 ///////////////////////////////////////////////////////////////////////////////
87 
88 // These macros work like gtest's ASSERT_* macros, except that they can be used
89 // in functions that return values.
90 #define ASSERT_TRUE_OR_RETURN(e, rv) \
91   EXPECT_TRUE(e);                    \
92   if (!(e)) {                        \
93     return rv;                       \
94   }
95 
96 #define ASSERT_EQ_OR_RETURN(a, b, rv) \
97   EXPECT_EQ(a, b);                    \
98   if ((a) != (b)) {                   \
99     return rv;                        \
100   }
101 
102 #define ASSERT_GE_OR_RETURN(a, b, rv) \
103   EXPECT_GE(a, b);                    \
104   if (!((a) >= (b))) {                \
105     return rv;                        \
106   }
107 
108 #define ASSERT_LE_OR_RETURN(a, b, rv) \
109   EXPECT_LE(a, b);                    \
110   if (!((a) <= (b))) {                \
111     return rv;                        \
112   }
113 
114 #define ASSERT_LT_OR_RETURN(a, b, rv) \
115   EXPECT_LT(a, b);                    \
116   if (!((a) < (b))) {                 \
117     return rv;                        \
118   }
119 
SpinPendingEvents()120 void SpinPendingEvents() {
121   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
122   EXPECT_TRUE(mainThread != nullptr);
123 
124   bool processed;
125   do {
126     processed = false;
127     nsresult rv = mainThread->ProcessNextEvent(false, &processed);
128     EXPECT_TRUE(NS_SUCCEEDED(rv));
129   } while (processed);
130 }
131 
LoadFile(const char * aRelativePath)132 already_AddRefed<nsIInputStream> LoadFile(const char* aRelativePath) {
133   nsresult rv;
134 
135   nsCOMPtr<nsIProperties> dirService =
136       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
137   ASSERT_TRUE_OR_RETURN(dirService != nullptr, nullptr);
138 
139   // Retrieve the current working directory.
140   nsCOMPtr<nsIFile> file;
141   rv = dirService->Get(NS_OS_CURRENT_WORKING_DIR, NS_GET_IID(nsIFile),
142                        getter_AddRefs(file));
143   ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
144   // Construct the final path by appending the working path to the current
145   // working directory.
146   file->AppendNative(nsDependentCString(aRelativePath));
147 
148   // Construct an input stream for the requested file.
149   nsCOMPtr<nsIInputStream> inputStream;
150   rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file);
151   ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
152 
153   // Ensure the resulting input stream is buffered.
154   if (!NS_InputStreamIsBuffered(inputStream)) {
155     nsCOMPtr<nsIInputStream> bufStream;
156     rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
157                                    inputStream.forget(), 1024);
158     ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr);
159     inputStream = bufStream;
160   }
161 
162   return inputStream.forget();
163 }
164 
IsSolidColor(SourceSurface * aSurface,BGRAColor aColor,uint8_t aFuzz)165 bool IsSolidColor(SourceSurface* aSurface, BGRAColor aColor,
166                   uint8_t aFuzz /* = 0 */) {
167   IntSize size = aSurface->GetSize();
168   return RectIsSolidColor(aSurface, IntRect(0, 0, size.width, size.height),
169                           aColor, aFuzz);
170 }
171 
RowsAreSolidColor(SourceSurface * aSurface,int32_t aStartRow,int32_t aRowCount,BGRAColor aColor,uint8_t aFuzz)172 bool RowsAreSolidColor(SourceSurface* aSurface, int32_t aStartRow,
173                        int32_t aRowCount, BGRAColor aColor,
174                        uint8_t aFuzz /* = 0 */) {
175   IntSize size = aSurface->GetSize();
176   return RectIsSolidColor(
177       aSurface, IntRect(0, aStartRow, size.width, aRowCount), aColor, aFuzz);
178 }
179 
RectIsSolidColor(SourceSurface * aSurface,const IntRect & aRect,BGRAColor aColor,uint8_t aFuzz)180 bool RectIsSolidColor(SourceSurface* aSurface, const IntRect& aRect,
181                       BGRAColor aColor, uint8_t aFuzz /* = 0 */) {
182   IntSize surfaceSize = aSurface->GetSize();
183   IntRect rect =
184       aRect.Intersect(IntRect(0, 0, surfaceSize.width, surfaceSize.height));
185 
186   RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
187   ASSERT_TRUE_OR_RETURN(dataSurface != nullptr, false);
188 
189   DataSourceSurface::ScopedMap mapping(dataSurface,
190                                        DataSourceSurface::MapType::READ);
191   ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false);
192   ASSERT_EQ_OR_RETURN(mapping.GetStride(), surfaceSize.width * 4, false);
193 
194   uint8_t* data = mapping.GetData();
195   ASSERT_TRUE_OR_RETURN(data != nullptr, false);
196 
197   BGRAColor pmColor = aColor.Premultiply();
198   uint32_t expectedPixel = pmColor.AsPixel();
199 
200   int32_t rowLength = mapping.GetStride();
201   for (int32_t row = rect.Y(); row < rect.YMost(); ++row) {
202     for (int32_t col = rect.X(); col < rect.XMost(); ++col) {
203       int32_t i = row * rowLength + col * 4;
204       uint32_t gotPixel = *reinterpret_cast<uint32_t*>(data + i);
205       if (expectedPixel != gotPixel) {
206         BGRAColor gotColor = BGRAColor::FromPixel(gotPixel);
207         if (abs(pmColor.mBlue - gotColor.mBlue) > aFuzz ||
208             abs(pmColor.mGreen - gotColor.mGreen) > aFuzz ||
209             abs(pmColor.mRed - gotColor.mRed) > aFuzz ||
210             abs(pmColor.mAlpha - gotColor.mAlpha) > aFuzz) {
211           EXPECT_EQ(pmColor.mBlue, gotColor.mBlue);
212           EXPECT_EQ(pmColor.mGreen, gotColor.mGreen);
213           EXPECT_EQ(pmColor.mRed, gotColor.mRed);
214           EXPECT_EQ(pmColor.mAlpha, gotColor.mAlpha);
215           ASSERT_EQ_OR_RETURN(expectedPixel, gotPixel, false);
216         }
217       }
218     }
219   }
220 
221   return true;
222 }
223 
RowHasPixels(SourceSurface * aSurface,int32_t aRow,const vector<BGRAColor> & aPixels)224 bool RowHasPixels(SourceSurface* aSurface, int32_t aRow,
225                   const vector<BGRAColor>& aPixels) {
226   ASSERT_GE_OR_RETURN(aRow, 0, false);
227 
228   IntSize surfaceSize = aSurface->GetSize();
229   ASSERT_EQ_OR_RETURN(aPixels.size(), size_t(surfaceSize.width), false);
230   ASSERT_LT_OR_RETURN(aRow, surfaceSize.height, false);
231 
232   RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
233   ASSERT_TRUE_OR_RETURN(dataSurface, false);
234 
235   DataSourceSurface::ScopedMap mapping(dataSurface,
236                                        DataSourceSurface::MapType::READ);
237   ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false);
238   ASSERT_EQ_OR_RETURN(mapping.GetStride(), surfaceSize.width * 4, false);
239 
240   uint8_t* data = mapping.GetData();
241   ASSERT_TRUE_OR_RETURN(data != nullptr, false);
242 
243   int32_t rowLength = mapping.GetStride();
244   for (int32_t col = 0; col < surfaceSize.width; ++col) {
245     int32_t i = aRow * rowLength + col * 4;
246     uint32_t gotPixelData = *reinterpret_cast<uint32_t*>(data + i);
247     BGRAColor gotPixel = BGRAColor::FromPixel(gotPixelData);
248     EXPECT_EQ(aPixels[col].mBlue, gotPixel.mBlue);
249     EXPECT_EQ(aPixels[col].mGreen, gotPixel.mGreen);
250     EXPECT_EQ(aPixels[col].mRed, gotPixel.mRed);
251     EXPECT_EQ(aPixels[col].mAlpha, gotPixel.mAlpha);
252     ASSERT_EQ_OR_RETURN(aPixels[col].AsPixel(), gotPixelData, false);
253   }
254 
255   return true;
256 }
257 
258 ///////////////////////////////////////////////////////////////////////////////
259 // SurfacePipe Helpers
260 ///////////////////////////////////////////////////////////////////////////////
261 
CreateTrivialDecoder()262 already_AddRefed<Decoder> CreateTrivialDecoder() {
263   DecoderType decoderType = DecoderFactory::GetDecoderType("image/gif");
264   auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
265   RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
266       decoderType, sourceBuffer, Nothing(), DefaultDecoderFlags(),
267       DefaultSurfaceFlags());
268   return decoder.forget();
269 }
270 
AssertCorrectPipelineFinalState(SurfaceFilter * aFilter,const gfx::IntRect & aInputSpaceRect,const gfx::IntRect & aOutputSpaceRect)271 void AssertCorrectPipelineFinalState(SurfaceFilter* aFilter,
272                                      const gfx::IntRect& aInputSpaceRect,
273                                      const gfx::IntRect& aOutputSpaceRect) {
274   EXPECT_TRUE(aFilter->IsSurfaceFinished());
275   Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
276   EXPECT_TRUE(invalidRect.isSome());
277   EXPECT_EQ(aInputSpaceRect, invalidRect->mInputSpaceRect);
278   EXPECT_EQ(aOutputSpaceRect, invalidRect->mOutputSpaceRect);
279 }
280 
CheckGeneratedImage(Decoder * aDecoder,const IntRect & aRect,uint8_t aFuzz)281 void CheckGeneratedImage(Decoder* aDecoder, const IntRect& aRect,
282                          uint8_t aFuzz /* = 0 */) {
283   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
284   RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
285   CheckGeneratedSurface(surface, aRect, BGRAColor::Green(),
286                         BGRAColor::Transparent(), aFuzz);
287 }
288 
CheckGeneratedSurface(SourceSurface * aSurface,const IntRect & aRect,const BGRAColor & aInnerColor,const BGRAColor & aOuterColor,uint8_t aFuzz)289 void CheckGeneratedSurface(SourceSurface* aSurface, const IntRect& aRect,
290                            const BGRAColor& aInnerColor,
291                            const BGRAColor& aOuterColor,
292                            uint8_t aFuzz /* = 0 */) {
293   const IntSize surfaceSize = aSurface->GetSize();
294 
295   // This diagram shows how the surface is divided into regions that the code
296   // below tests for the correct content. The output rect is the bounds of the
297   // region labeled 'C'.
298   //
299   // +---------------------------+
300   // |             A             |
301   // +---------+--------+--------+
302   // |    B    |   C    |   D    |
303   // +---------+--------+--------+
304   // |             E             |
305   // +---------------------------+
306 
307   // Check that the output rect itself is the inner color. (Region 'C'.)
308   EXPECT_TRUE(RectIsSolidColor(aSurface, aRect, aInnerColor, aFuzz));
309 
310   // Check that the area above the output rect is the outer color. (Region 'A'.)
311   EXPECT_TRUE(RectIsSolidColor(aSurface,
312                                IntRect(0, 0, surfaceSize.width, aRect.Y()),
313                                aOuterColor, aFuzz));
314 
315   // Check that the area to the left of the output rect is the outer color.
316   // (Region 'B'.)
317   EXPECT_TRUE(RectIsSolidColor(aSurface,
318                                IntRect(0, aRect.Y(), aRect.X(), aRect.YMost()),
319                                aOuterColor, aFuzz));
320 
321   // Check that the area to the right of the output rect is the outer color.
322   // (Region 'D'.)
323   const int32_t widthOnRight = surfaceSize.width - aRect.XMost();
324   EXPECT_TRUE(RectIsSolidColor(
325       aSurface, IntRect(aRect.XMost(), aRect.Y(), widthOnRight, aRect.YMost()),
326       aOuterColor, aFuzz));
327 
328   // Check that the area below the output rect is the outer color. (Region 'E'.)
329   const int32_t heightBelow = surfaceSize.height - aRect.YMost();
330   EXPECT_TRUE(RectIsSolidColor(
331       aSurface, IntRect(0, aRect.YMost(), surfaceSize.width, heightBelow),
332       aOuterColor, aFuzz));
333 }
334 
CheckWritePixels(Decoder * aDecoder,SurfaceFilter * aFilter,const Maybe<IntRect> & aOutputRect,const Maybe<IntRect> & aInputRect,const Maybe<IntRect> & aInputWriteRect,const Maybe<IntRect> & aOutputWriteRect,uint8_t aFuzz)335 void CheckWritePixels(Decoder* aDecoder, SurfaceFilter* aFilter,
336                       const Maybe<IntRect>& aOutputRect /* = Nothing() */,
337                       const Maybe<IntRect>& aInputRect /* = Nothing() */,
338                       const Maybe<IntRect>& aInputWriteRect /* = Nothing() */,
339                       const Maybe<IntRect>& aOutputWriteRect /* = Nothing() */,
340                       uint8_t aFuzz /* = 0 */) {
341   CheckTransformedWritePixels(aDecoder, aFilter, BGRAColor::Green(),
342                               BGRAColor::Green(), aOutputRect, aInputRect,
343                               aInputWriteRect, aOutputWriteRect, aFuzz);
344 }
345 
CheckTransformedWritePixels(Decoder * aDecoder,SurfaceFilter * aFilter,const BGRAColor & aInputColor,const BGRAColor & aOutputColor,const Maybe<IntRect> & aOutputRect,const Maybe<IntRect> & aInputRect,const Maybe<IntRect> & aInputWriteRect,const Maybe<IntRect> & aOutputWriteRect,uint8_t aFuzz)346 void CheckTransformedWritePixels(
347     Decoder* aDecoder, SurfaceFilter* aFilter, const BGRAColor& aInputColor,
348     const BGRAColor& aOutputColor,
349     const Maybe<IntRect>& aOutputRect /* = Nothing() */,
350     const Maybe<IntRect>& aInputRect /* = Nothing() */,
351     const Maybe<IntRect>& aInputWriteRect /* = Nothing() */,
352     const Maybe<IntRect>& aOutputWriteRect /* = Nothing() */,
353     uint8_t aFuzz /* = 0 */) {
354   IntRect outputRect = aOutputRect.valueOr(IntRect(0, 0, 100, 100));
355   IntRect inputRect = aInputRect.valueOr(IntRect(0, 0, 100, 100));
356   IntRect inputWriteRect = aInputWriteRect.valueOr(inputRect);
357   IntRect outputWriteRect = aOutputWriteRect.valueOr(outputRect);
358 
359   // Fill the image.
360   int32_t count = 0;
361   auto result = aFilter->WritePixels<uint32_t>([&] {
362     ++count;
363     return AsVariant(aInputColor.AsPixel());
364   });
365   EXPECT_EQ(WriteState::FINISHED, result);
366   EXPECT_EQ(inputWriteRect.Width() * inputWriteRect.Height(), count);
367 
368   AssertCorrectPipelineFinalState(aFilter, inputRect, outputRect);
369 
370   // Attempt to write more data and make sure nothing changes.
371   const int32_t oldCount = count;
372   result = aFilter->WritePixels<uint32_t>([&] {
373     ++count;
374     return AsVariant(aInputColor.AsPixel());
375   });
376   EXPECT_EQ(oldCount, count);
377   EXPECT_EQ(WriteState::FINISHED, result);
378   EXPECT_TRUE(aFilter->IsSurfaceFinished());
379   Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
380   EXPECT_TRUE(invalidRect.isNothing());
381 
382   // Attempt to advance to the next row and make sure nothing changes.
383   aFilter->AdvanceRow();
384   EXPECT_TRUE(aFilter->IsSurfaceFinished());
385   invalidRect = aFilter->TakeInvalidRect();
386   EXPECT_TRUE(invalidRect.isNothing());
387 
388   // Check that the generated image is correct.
389   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
390   RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
391   CheckGeneratedSurface(surface, outputWriteRect, aOutputColor,
392                         BGRAColor::Transparent(), aFuzz);
393 }
394 
395 ///////////////////////////////////////////////////////////////////////////////
396 // Test Data
397 ///////////////////////////////////////////////////////////////////////////////
398 
GreenPNGTestCase()399 ImageTestCase GreenPNGTestCase() {
400   return ImageTestCase("green.png", "image/png", IntSize(100, 100));
401 }
402 
GreenGIFTestCase()403 ImageTestCase GreenGIFTestCase() {
404   return ImageTestCase("green.gif", "image/gif", IntSize(100, 100));
405 }
406 
GreenJPGTestCase()407 ImageTestCase GreenJPGTestCase() {
408   return ImageTestCase("green.jpg", "image/jpeg", IntSize(100, 100),
409                        TEST_CASE_IS_FUZZY);
410 }
411 
GreenBMPTestCase()412 ImageTestCase GreenBMPTestCase() {
413   return ImageTestCase("green.bmp", "image/bmp", IntSize(100, 100));
414 }
415 
GreenICOTestCase()416 ImageTestCase GreenICOTestCase() {
417   // This ICO contains a 32-bit BMP, and we use a BMP's alpha data by default
418   // when the BMP is embedded in an ICO, so it's transparent.
419   return ImageTestCase("green.ico", "image/x-icon", IntSize(100, 100),
420                        TEST_CASE_IS_TRANSPARENT);
421 }
422 
GreenIconTestCase()423 ImageTestCase GreenIconTestCase() {
424   return ImageTestCase("green.icon", "image/icon", IntSize(100, 100),
425                        TEST_CASE_IS_TRANSPARENT);
426 }
427 
GreenWebPTestCase()428 ImageTestCase GreenWebPTestCase() {
429   return ImageTestCase("green.webp", "image/webp", IntSize(100, 100));
430 }
431 
432 // Forcing sRGB is required until nsAVIFDecoder supports ICC profiles
433 // See bug 1634741
GreenAVIFTestCase()434 ImageTestCase GreenAVIFTestCase() {
435   return ImageTestCase("green.avif", "image/avif", IntSize(100, 100))
436       .WithSurfaceFlags(SurfaceFlags::TO_SRGB_COLORSPACE);
437 }
438 
439 // Forcing sRGB is required until nsAVIFDecoder supports ICC profiles
440 // See bug 1634741
StackCheckAVIFTestCase()441 ImageTestCase StackCheckAVIFTestCase() {
442   return ImageTestCase("stackcheck.avif", "image/avif", IntSize(4096, 2924),
443                        TEST_CASE_IGNORE_OUTPUT)
444       .WithSurfaceFlags(SurfaceFlags::TO_SRGB_COLORSPACE);
445 }
446 
LargeWebPTestCase()447 ImageTestCase LargeWebPTestCase() {
448   return ImageTestCase("large.webp", "image/webp", IntSize(1200, 660),
449                        TEST_CASE_IGNORE_OUTPUT);
450 }
451 
LargeAVIFTestCase()452 ImageTestCase LargeAVIFTestCase() {
453   return ImageTestCase("large.avif", "image/avif", IntSize(1200, 660),
454                        TEST_CASE_IGNORE_OUTPUT);
455 }
456 
GreenWebPIccSrgbTestCase()457 ImageTestCase GreenWebPIccSrgbTestCase() {
458   return ImageTestCase("green.icc_srgb.webp", "image/webp", IntSize(100, 100));
459 }
460 
GreenFirstFrameAnimatedGIFTestCase()461 ImageTestCase GreenFirstFrameAnimatedGIFTestCase() {
462   return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100),
463                        TEST_CASE_IS_ANIMATED);
464 }
465 
GreenFirstFrameAnimatedPNGTestCase()466 ImageTestCase GreenFirstFrameAnimatedPNGTestCase() {
467   return ImageTestCase("first-frame-green.png", "image/png", IntSize(100, 100),
468                        TEST_CASE_IS_TRANSPARENT | TEST_CASE_IS_ANIMATED);
469 }
470 
GreenFirstFrameAnimatedWebPTestCase()471 ImageTestCase GreenFirstFrameAnimatedWebPTestCase() {
472   return ImageTestCase("first-frame-green.webp", "image/webp",
473                        IntSize(100, 100), TEST_CASE_IS_ANIMATED);
474 }
475 
BlendAnimatedGIFTestCase()476 ImageTestCase BlendAnimatedGIFTestCase() {
477   return ImageTestCase("blend.gif", "image/gif", IntSize(100, 100),
478                        TEST_CASE_IS_ANIMATED);
479 }
480 
BlendAnimatedPNGTestCase()481 ImageTestCase BlendAnimatedPNGTestCase() {
482   return ImageTestCase("blend.png", "image/png", IntSize(100, 100),
483                        TEST_CASE_IS_TRANSPARENT | TEST_CASE_IS_ANIMATED);
484 }
485 
BlendAnimatedWebPTestCase()486 ImageTestCase BlendAnimatedWebPTestCase() {
487   return ImageTestCase("blend.webp", "image/webp", IntSize(100, 100),
488                        TEST_CASE_IS_TRANSPARENT | TEST_CASE_IS_ANIMATED);
489 }
490 
CorruptTestCase()491 ImageTestCase CorruptTestCase() {
492   return ImageTestCase("corrupt.jpg", "image/jpeg", IntSize(100, 100),
493                        TEST_CASE_HAS_ERROR);
494 }
495 
CorruptBMPWithTruncatedHeader()496 ImageTestCase CorruptBMPWithTruncatedHeader() {
497   // This BMP has a header which is truncated right between the BIH and the
498   // bitfields, which is a particularly error-prone place w.r.t. the BMP decoder
499   // state machine.
500   return ImageTestCase("invalid-truncated-metadata.bmp", "image/bmp",
501                        IntSize(100, 100), TEST_CASE_HAS_ERROR);
502 }
503 
CorruptICOWithBadBMPWidthTestCase()504 ImageTestCase CorruptICOWithBadBMPWidthTestCase() {
505   // This ICO contains a BMP icon which has a width that doesn't match the size
506   // listed in the corresponding ICO directory entry.
507   return ImageTestCase("corrupt-with-bad-bmp-width.ico", "image/x-icon",
508                        IntSize(100, 100), TEST_CASE_HAS_ERROR);
509 }
510 
CorruptICOWithBadBMPHeightTestCase()511 ImageTestCase CorruptICOWithBadBMPHeightTestCase() {
512   // This ICO contains a BMP icon which has a height that doesn't match the size
513   // listed in the corresponding ICO directory entry.
514   return ImageTestCase("corrupt-with-bad-bmp-height.ico", "image/x-icon",
515                        IntSize(100, 100), TEST_CASE_HAS_ERROR);
516 }
517 
CorruptICOWithBadBppTestCase()518 ImageTestCase CorruptICOWithBadBppTestCase() {
519   // This test case is an ICO with a BPP (15) in the ICO header which differs
520   // from that in the BMP header itself (1). It should ignore the ICO BPP when
521   // the BMP BPP is available and thus correctly decode the image.
522   return ImageTestCase("corrupt-with-bad-ico-bpp.ico", "image/x-icon",
523                        IntSize(100, 100), TEST_CASE_IS_TRANSPARENT);
524 }
525 
TransparentPNGTestCase()526 ImageTestCase TransparentPNGTestCase() {
527   return ImageTestCase("transparent.png", "image/png", IntSize(32, 32),
528                        TEST_CASE_IS_TRANSPARENT);
529 }
530 
TransparentGIFTestCase()531 ImageTestCase TransparentGIFTestCase() {
532   return ImageTestCase("transparent.gif", "image/gif", IntSize(16, 16),
533                        TEST_CASE_IS_TRANSPARENT);
534 }
535 
TransparentWebPTestCase()536 ImageTestCase TransparentWebPTestCase() {
537   ImageTestCase test("transparent.webp", "image/webp", IntSize(100, 100),
538                      TEST_CASE_IS_TRANSPARENT);
539   test.mColor = BGRAColor::Transparent();
540   return test;
541 }
542 
TransparentNoAlphaHeaderWebPTestCase()543 ImageTestCase TransparentNoAlphaHeaderWebPTestCase() {
544   ImageTestCase test("transparent-no-alpha-header.webp", "image/webp",
545                      IntSize(100, 100), TEST_CASE_IS_FUZZY);
546   test.mColor = BGRAColor(0x00, 0x00, 0x00, 0xFF);  // black
547   return test;
548 }
549 
FirstFramePaddingGIFTestCase()550 ImageTestCase FirstFramePaddingGIFTestCase() {
551   return ImageTestCase("transparent.gif", "image/gif", IntSize(16, 16),
552                        TEST_CASE_IS_TRANSPARENT);
553 }
554 
TransparentIfWithinICOBMPTestCase(TestCaseFlags aFlags)555 ImageTestCase TransparentIfWithinICOBMPTestCase(TestCaseFlags aFlags) {
556   // This is a BMP that is only transparent when decoded as if it is within an
557   // ICO file. (Note: aFlags needs to be set to TEST_CASE_DEFAULT_FLAGS or
558   // TEST_CASE_IS_TRANSPARENT accordingly.)
559   return ImageTestCase("transparent-if-within-ico.bmp", "image/bmp",
560                        IntSize(32, 32), aFlags);
561 }
562 
RLE4BMPTestCase()563 ImageTestCase RLE4BMPTestCase() {
564   return ImageTestCase("rle4.bmp", "image/bmp", IntSize(320, 240),
565                        TEST_CASE_IS_TRANSPARENT);
566 }
567 
RLE8BMPTestCase()568 ImageTestCase RLE8BMPTestCase() {
569   return ImageTestCase("rle8.bmp", "image/bmp", IntSize(32, 32),
570                        TEST_CASE_IS_TRANSPARENT);
571 }
572 
NoFrameDelayGIFTestCase()573 ImageTestCase NoFrameDelayGIFTestCase() {
574   // This is an invalid (or at least, questionably valid) GIF that's animated
575   // even though it specifies a frame delay of zero. It's animated, but it's not
576   // marked TEST_CASE_IS_ANIMATED because the metadata decoder can't detect that
577   // it's animated.
578   return ImageTestCase("no-frame-delay.gif", "image/gif", IntSize(100, 100));
579 }
580 
ExtraImageSubBlocksAnimatedGIFTestCase()581 ImageTestCase ExtraImageSubBlocksAnimatedGIFTestCase() {
582   // This is a corrupt GIF that has extra image sub blocks between the first and
583   // second frame.
584   return ImageTestCase("animated-with-extra-image-sub-blocks.gif", "image/gif",
585                        IntSize(100, 100));
586 }
587 
DownscaledPNGTestCase()588 ImageTestCase DownscaledPNGTestCase() {
589   // This testcase (and all the other "downscaled") testcases) consists of 25
590   // lines of green, followed by 25 lines of red, followed by 25 lines of green,
591   // followed by 25 more lines of red. It's intended that tests downscale it
592   // from 100x100 to 20x20, so we specify a 20x20 output size.
593   return ImageTestCase("downscaled.png", "image/png", IntSize(100, 100),
594                        IntSize(20, 20));
595 }
596 
DownscaledGIFTestCase()597 ImageTestCase DownscaledGIFTestCase() {
598   return ImageTestCase("downscaled.gif", "image/gif", IntSize(100, 100),
599                        IntSize(20, 20));
600 }
601 
DownscaledJPGTestCase()602 ImageTestCase DownscaledJPGTestCase() {
603   return ImageTestCase("downscaled.jpg", "image/jpeg", IntSize(100, 100),
604                        IntSize(20, 20));
605 }
606 
DownscaledBMPTestCase()607 ImageTestCase DownscaledBMPTestCase() {
608   return ImageTestCase("downscaled.bmp", "image/bmp", IntSize(100, 100),
609                        IntSize(20, 20));
610 }
611 
DownscaledICOTestCase()612 ImageTestCase DownscaledICOTestCase() {
613   return ImageTestCase("downscaled.ico", "image/x-icon", IntSize(100, 100),
614                        IntSize(20, 20), TEST_CASE_IS_TRANSPARENT);
615 }
616 
DownscaledIconTestCase()617 ImageTestCase DownscaledIconTestCase() {
618   return ImageTestCase("downscaled.icon", "image/icon", IntSize(100, 100),
619                        IntSize(20, 20), TEST_CASE_IS_TRANSPARENT);
620 }
621 
DownscaledWebPTestCase()622 ImageTestCase DownscaledWebPTestCase() {
623   return ImageTestCase("downscaled.webp", "image/webp", IntSize(100, 100),
624                        IntSize(20, 20));
625 }
626 
DownscaledAVIFTestCase()627 ImageTestCase DownscaledAVIFTestCase() {
628   return ImageTestCase("downscaled.avif", "image/avif", IntSize(100, 100),
629                        IntSize(20, 20));
630 }
631 
DownscaledTransparentICOWithANDMaskTestCase()632 ImageTestCase DownscaledTransparentICOWithANDMaskTestCase() {
633   // This test case is an ICO with AND mask transparency. We want to ensure that
634   // we can downscale it without crashing or triggering ASAN failures, but its
635   // content isn't simple to verify, so for now we don't check the output.
636   return ImageTestCase("transparent-ico-with-and-mask.ico", "image/x-icon",
637                        IntSize(32, 32), IntSize(20, 20),
638                        TEST_CASE_IS_TRANSPARENT | TEST_CASE_IGNORE_OUTPUT);
639 }
640 
TruncatedSmallGIFTestCase()641 ImageTestCase TruncatedSmallGIFTestCase() {
642   return ImageTestCase("green-1x1-truncated.gif", "image/gif", IntSize(1, 1));
643 }
644 
LargeICOWithBMPTestCase()645 ImageTestCase LargeICOWithBMPTestCase() {
646   return ImageTestCase("green-large-bmp.ico", "image/x-icon", IntSize(256, 256),
647                        TEST_CASE_IS_TRANSPARENT);
648 }
649 
LargeICOWithPNGTestCase()650 ImageTestCase LargeICOWithPNGTestCase() {
651   return ImageTestCase("green-large-png.ico", "image/x-icon", IntSize(512, 512),
652                        TEST_CASE_IS_TRANSPARENT);
653 }
654 
GreenMultipleSizesICOTestCase()655 ImageTestCase GreenMultipleSizesICOTestCase() {
656   return ImageTestCase("green-multiple-sizes.ico", "image/x-icon",
657                        IntSize(256, 256));
658 }
659 
PerfGrayJPGTestCase()660 ImageTestCase PerfGrayJPGTestCase() {
661   return ImageTestCase("perf_gray.jpg", "image/jpeg", IntSize(1000, 1000));
662 }
663 
PerfCmykJPGTestCase()664 ImageTestCase PerfCmykJPGTestCase() {
665   return ImageTestCase("perf_cmyk.jpg", "image/jpeg", IntSize(1000, 1000));
666 }
667 
PerfYCbCrJPGTestCase()668 ImageTestCase PerfYCbCrJPGTestCase() {
669   return ImageTestCase("perf_ycbcr.jpg", "image/jpeg", IntSize(1000, 1000));
670 }
671 
PerfRgbPNGTestCase()672 ImageTestCase PerfRgbPNGTestCase() {
673   return ImageTestCase("perf_srgb.png", "image/png", IntSize(1000, 1000));
674 }
675 
PerfRgbAlphaPNGTestCase()676 ImageTestCase PerfRgbAlphaPNGTestCase() {
677   return ImageTestCase("perf_srgb_alpha.png", "image/png", IntSize(1000, 1000),
678                        TEST_CASE_IS_TRANSPARENT);
679 }
680 
PerfGrayPNGTestCase()681 ImageTestCase PerfGrayPNGTestCase() {
682   return ImageTestCase("perf_gray.png", "image/png", IntSize(1000, 1000));
683 }
684 
PerfGrayAlphaPNGTestCase()685 ImageTestCase PerfGrayAlphaPNGTestCase() {
686   return ImageTestCase("perf_gray_alpha.png", "image/png", IntSize(1000, 1000),
687                        TEST_CASE_IS_TRANSPARENT);
688 }
689 
PerfRgbLosslessWebPTestCase()690 ImageTestCase PerfRgbLosslessWebPTestCase() {
691   return ImageTestCase("perf_srgb_lossless.webp", "image/webp",
692                        IntSize(1000, 1000));
693 }
694 
PerfRgbAlphaLosslessWebPTestCase()695 ImageTestCase PerfRgbAlphaLosslessWebPTestCase() {
696   return ImageTestCase("perf_srgb_alpha_lossless.webp", "image/webp",
697                        IntSize(1000, 1000), TEST_CASE_IS_TRANSPARENT);
698 }
699 
PerfRgbLossyWebPTestCase()700 ImageTestCase PerfRgbLossyWebPTestCase() {
701   return ImageTestCase("perf_srgb_lossy.webp", "image/webp",
702                        IntSize(1000, 1000));
703 }
704 
PerfRgbAlphaLossyWebPTestCase()705 ImageTestCase PerfRgbAlphaLossyWebPTestCase() {
706   return ImageTestCase("perf_srgb_alpha_lossy.webp", "image/webp",
707                        IntSize(1000, 1000), TEST_CASE_IS_TRANSPARENT);
708 }
709 
PerfRgbGIFTestCase()710 ImageTestCase PerfRgbGIFTestCase() {
711   return ImageTestCase("perf_srgb.gif", "image/gif", IntSize(1000, 1000));
712 }
713 
714 }  // namespace image
715 }  // namespace mozilla
716