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