Coverage Report

Created: 2018-09-25 14:53

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