Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/test/gtest/TestSurfacePipeIntegration.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "gtest/gtest.h"
8
9
#include "mozilla/gfx/2D.h"
10
#include "Common.h"
11
#include "Decoder.h"
12
#include "DecoderFactory.h"
13
#include "SourceBuffer.h"
14
#include "SurfacePipe.h"
15
16
using namespace mozilla;
17
using namespace mozilla::gfx;
18
using namespace mozilla::image;
19
20
namespace mozilla {
21
namespace image {
22
23
class TestSurfacePipeFactory
24
{
25
public:
26
  static SurfacePipe SimpleSurfacePipe()
27
0
  {
28
0
    SurfacePipe pipe;
29
0
    return pipe;
30
0
  }
31
32
  template <typename T>
33
  static SurfacePipe SurfacePipeFromPipeline(T&& aPipeline)
34
0
  {
35
0
    return SurfacePipe { std::move(aPipeline) };
36
0
  }
Unexecuted instantiation: mozilla::image::SurfacePipe mozilla::image::TestSurfacePipeFactory::SurfacePipeFromPipeline<mozilla::UniquePtr<mozilla::image::SurfaceSink, mozilla::DefaultDelete<mozilla::image::SurfaceSink> >&>(mozilla::UniquePtr<mozilla::image::SurfaceSink, mozilla::DefaultDelete<mozilla::image::SurfaceSink> >&)
Unexecuted instantiation: mozilla::image::SurfacePipe mozilla::image::TestSurfacePipeFactory::SurfacePipeFromPipeline<mozilla::UniquePtr<mozilla::image::PalettedSurfaceSink, mozilla::DefaultDelete<mozilla::image::PalettedSurfaceSink> >&>(mozilla::UniquePtr<mozilla::image::PalettedSurfaceSink, mozilla::DefaultDelete<mozilla::image::PalettedSurfaceSink> >&)
37
38
private:
39
0
  TestSurfacePipeFactory() { }
40
};
41
42
} // namespace image
43
} // namespace mozilla
44
45
void
46
CheckSurfacePipeMethodResults(SurfacePipe* aPipe,
47
                              Decoder* aDecoder,
48
                              const IntRect& aRect = IntRect(0, 0, 100, 100))
49
0
{
50
0
  // Check that the pipeline ended up in the state we expect.  Note that we're
51
0
  // explicitly testing the SurfacePipe versions of these methods, so we don't
52
0
  // want to use AssertCorrectPipelineFinalState() here.
53
0
  EXPECT_TRUE(aPipe->IsSurfaceFinished());
54
0
  Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect();
55
0
  EXPECT_TRUE(invalidRect.isSome());
56
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
57
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
58
0
59
0
  // Check the generated image.
60
0
  CheckGeneratedImage(aDecoder, aRect);
61
0
62
0
  // Reset and clear the image before the next test.
63
0
  aPipe->ResetToFirstRow();
64
0
  EXPECT_FALSE(aPipe->IsSurfaceFinished());
65
0
  invalidRect = aPipe->TakeInvalidRect();
66
0
  EXPECT_TRUE(invalidRect.isNothing());
67
0
68
0
  uint32_t count = 0;
69
0
  auto result = aPipe->WritePixels<uint32_t>([&]() {
70
0
    ++count;
71
0
    return AsVariant(BGRAColor::Transparent().AsPixel());
72
0
  });
73
0
  EXPECT_EQ(WriteState::FINISHED, result);
74
0
  EXPECT_EQ(100u * 100u, count);
75
0
76
0
  EXPECT_TRUE(aPipe->IsSurfaceFinished());
77
0
  invalidRect = aPipe->TakeInvalidRect();
78
0
  EXPECT_TRUE(invalidRect.isSome());
79
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
80
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
81
0
82
0
  aPipe->ResetToFirstRow();
83
0
  EXPECT_FALSE(aPipe->IsSurfaceFinished());
84
0
  invalidRect = aPipe->TakeInvalidRect();
85
0
  EXPECT_TRUE(invalidRect.isNothing());
86
0
}
87
88
void
89
CheckPalettedSurfacePipeMethodResults(SurfacePipe* aPipe,
90
                                      Decoder* aDecoder,
91
                                      const IntRect& aRect
92
                                        = IntRect(0, 0, 100, 100))
93
0
{
94
0
  // Check that the pipeline ended up in the state we expect.  Note that we're
95
0
  // explicitly testing the SurfacePipe versions of these methods, so we don't
96
0
  // want to use AssertCorrectPipelineFinalState() here.
97
0
  EXPECT_TRUE(aPipe->IsSurfaceFinished());
98
0
  Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect();
99
0
  EXPECT_TRUE(invalidRect.isSome());
100
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
101
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
102
0
103
0
  // Check the generated image.
104
0
  CheckGeneratedPalettedImage(aDecoder, aRect);
105
0
106
0
  // Reset and clear the image before the next test.
107
0
  aPipe->ResetToFirstRow();
108
0
  EXPECT_FALSE(aPipe->IsSurfaceFinished());
109
0
  invalidRect = aPipe->TakeInvalidRect();
110
0
  EXPECT_TRUE(invalidRect.isNothing());
111
0
112
0
  uint32_t count = 0;
113
0
  auto result = aPipe->WritePixels<uint8_t>([&]() {
114
0
    ++count;
115
0
    return AsVariant(uint8_t(0));
116
0
  });
117
0
  EXPECT_EQ(WriteState::FINISHED, result);
118
0
  EXPECT_EQ(100u * 100u, count);
119
0
120
0
  EXPECT_TRUE(aPipe->IsSurfaceFinished());
121
0
  invalidRect = aPipe->TakeInvalidRect();
122
0
  EXPECT_TRUE(invalidRect.isSome());
123
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
124
0
  EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
125
0
126
0
  aPipe->ResetToFirstRow();
127
0
  EXPECT_FALSE(aPipe->IsSurfaceFinished());
128
0
  invalidRect = aPipe->TakeInvalidRect();
129
0
  EXPECT_TRUE(invalidRect.isNothing());
130
0
}
131
132
class ImageSurfacePipeIntegration : public ::testing::Test
133
{
134
protected:
135
  AutoInitializeImageLib mInit;
136
};
137
138
TEST_F(ImageSurfacePipeIntegration, SurfacePipe)
139
0
{
140
0
  // Test that SurfacePipe objects can be initialized and move constructed.
141
0
  SurfacePipe pipe = TestSurfacePipeFactory::SimpleSurfacePipe();
142
0
143
0
  // Test that SurfacePipe objects can be move assigned.
144
0
  pipe = TestSurfacePipeFactory::SimpleSurfacePipe();
145
0
146
0
  // Test that SurfacePipe objects can be initialized with a pipeline.
147
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
148
0
  ASSERT_TRUE(decoder != nullptr);
149
0
150
0
  auto sink = MakeUnique<SurfaceSink>();
151
0
  nsresult rv =
152
0
    sink->Configure(SurfaceConfig { decoder, IntSize(100, 100),
153
0
                                    SurfaceFormat::B8G8R8A8, false });
154
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
155
0
156
0
  pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink);
157
0
158
0
  // Test that WritePixels() gets passed through to the underlying pipeline.
159
0
  {
160
0
    uint32_t count = 0;
161
0
    auto result = pipe.WritePixels<uint32_t>([&]() {
162
0
      ++count;
163
0
      return AsVariant(BGRAColor::Green().AsPixel());
164
0
    });
165
0
    EXPECT_EQ(WriteState::FINISHED, result);
166
0
    EXPECT_EQ(100u * 100u, count);
167
0
    CheckSurfacePipeMethodResults(&pipe, decoder);
168
0
  }
169
0
170
0
  // Create a buffer the same size as one row of the surface, containing all
171
0
  // green pixels. We'll use this for the WriteBuffer() tests.
172
0
  uint32_t buffer[100];
173
0
  for (int i = 0; i < 100; ++i) {
174
0
    buffer[i] = BGRAColor::Green().AsPixel();
175
0
  }
176
0
177
0
  // Test that WriteBuffer() gets passed through to the underlying pipeline.
178
0
  {
179
0
    uint32_t count = 0;
180
0
    WriteState result = WriteState::NEED_MORE_DATA;
181
0
    while (result == WriteState::NEED_MORE_DATA) {
182
0
      result = pipe.WriteBuffer(buffer);
183
0
      ++count;
184
0
    }
185
0
    EXPECT_EQ(WriteState::FINISHED, result);
186
0
    EXPECT_EQ(100u, count);
187
0
    CheckSurfacePipeMethodResults(&pipe, decoder);
188
0
  }
189
0
190
0
  // Test that the 3 argument version of WriteBuffer() gets passed through to
191
0
  // the underlying pipeline.
192
0
  {
193
0
    uint32_t count = 0;
194
0
    WriteState result = WriteState::NEED_MORE_DATA;
195
0
    while (result == WriteState::NEED_MORE_DATA) {
196
0
      result = pipe.WriteBuffer(buffer, 0, 100);
197
0
      ++count;
198
0
    }
199
0
    EXPECT_EQ(WriteState::FINISHED, result);
200
0
    EXPECT_EQ(100u, count);
201
0
    CheckSurfacePipeMethodResults(&pipe, decoder);
202
0
  }
203
0
204
0
  // Test that WritePixelBlocks() gets passed through to the underlying pipeline.
205
0
  {
206
0
    uint32_t count = 0;
207
0
    WriteState result = pipe.WritePixelBlocks<uint32_t>([&](uint32_t* aBlockStart,
208
0
                                                            int32_t aLength) {
209
0
      ++count;
210
0
      EXPECT_EQ(int32_t(100), aLength);
211
0
      memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
212
0
      return MakeTuple(int32_t(100), Maybe<WriteState>());
213
0
    });
214
0
215
0
    EXPECT_EQ(WriteState::FINISHED, result);
216
0
    EXPECT_EQ(100u, count);
217
0
    CheckSurfacePipeMethodResults(&pipe, decoder);
218
0
  }
219
0
220
0
  // Test that WriteEmptyRow() gets passed through to the underlying pipeline.
221
0
  {
222
0
    uint32_t count = 0;
223
0
    WriteState result = WriteState::NEED_MORE_DATA;
224
0
    while (result == WriteState::NEED_MORE_DATA) {
225
0
      result = pipe.WriteEmptyRow();
226
0
      ++count;
227
0
    }
228
0
    EXPECT_EQ(WriteState::FINISHED, result);
229
0
    EXPECT_EQ(100u, count);
230
0
    CheckSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0));
231
0
  }
232
0
233
0
  // Mark the frame as finished so we don't get an assertion.
234
0
  RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
235
0
  currentFrame->Finish();
236
0
}
237
238
TEST_F(ImageSurfacePipeIntegration, PalettedSurfacePipe)
239
0
{
240
0
  // Create a SurfacePipe containing a PalettedSurfaceSink.
241
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
242
0
  ASSERT_TRUE(decoder != nullptr);
243
0
244
0
  auto sink = MakeUnique<PalettedSurfaceSink>();
245
0
  nsresult rv =
246
0
    sink->Configure(PalettedSurfaceConfig { decoder, IntSize(100, 100),
247
0
                                            IntRect(0, 0, 100, 100),
248
0
                                            SurfaceFormat::B8G8R8A8,
249
0
                                            8, false });
250
0
  ASSERT_TRUE(NS_SUCCEEDED(rv));
251
0
252
0
  SurfacePipe pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink);
253
0
254
0
  // Test that WritePixels() gets passed through to the underlying pipeline.
255
0
  {
256
0
    uint32_t count = 0;
257
0
    auto result = pipe.WritePixels<uint8_t>([&]() {
258
0
      ++count;
259
0
      return AsVariant(uint8_t(255));
260
0
    });
261
0
    EXPECT_EQ(WriteState::FINISHED, result);
262
0
    EXPECT_EQ(100u * 100u, count);
263
0
    CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
264
0
  }
265
0
266
0
  // Create a buffer the same size as one row of the surface, containing all
267
0
  // 255 pixels. We'll use this for the WriteBuffer() tests.
268
0
  uint8_t buffer[100];
269
0
  for (int i = 0; i < 100; ++i) {
270
0
    buffer[i] = 255;
271
0
  }
272
0
273
0
  // Test that WriteBuffer() gets passed through to the underlying pipeline.
274
0
  {
275
0
    uint32_t count = 0;
276
0
    WriteState result = WriteState::NEED_MORE_DATA;
277
0
    while (result == WriteState::NEED_MORE_DATA) {
278
0
      result = pipe.WriteBuffer(buffer);
279
0
      ++count;
280
0
    }
281
0
    EXPECT_EQ(WriteState::FINISHED, result);
282
0
    EXPECT_EQ(100u, count);
283
0
    CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
284
0
  }
285
0
286
0
  // Test that the 3 argument version of WriteBuffer() gets passed through to
287
0
  // the underlying pipeline.
288
0
  {
289
0
    uint32_t count = 0;
290
0
    WriteState result = WriteState::NEED_MORE_DATA;
291
0
    while (result == WriteState::NEED_MORE_DATA) {
292
0
      result = pipe.WriteBuffer(buffer, 0, 100);
293
0
      ++count;
294
0
    }
295
0
    EXPECT_EQ(WriteState::FINISHED, result);
296
0
    EXPECT_EQ(100u, count);
297
0
    CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
298
0
  }
299
0
300
0
  // Test that WritePixelBlocks() gets passed through to the underlying pipeline.
301
0
  {
302
0
    uint32_t count = 0;
303
0
    WriteState result = pipe.WritePixelBlocks<uint8_t>([&](uint8_t* aBlockStart,
304
0
                                                           int32_t aLength) {
305
0
      ++count;
306
0
      EXPECT_EQ(int32_t(100), aLength);
307
0
      memcpy(aBlockStart, buffer, 100 * sizeof(uint8_t));
308
0
      return MakeTuple(int32_t(100), Maybe<WriteState>());
309
0
    });
310
0
311
0
    EXPECT_EQ(WriteState::FINISHED, result);
312
0
    EXPECT_EQ(100u, count);
313
0
    CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
314
0
  }
315
0
316
0
  // Test that WriteEmptyRow() gets passed through to the underlying pipeline.
317
0
  {
318
0
    uint32_t count = 0;
319
0
    WriteState result = WriteState::NEED_MORE_DATA;
320
0
    while (result == WriteState::NEED_MORE_DATA) {
321
0
      result = pipe.WriteEmptyRow();
322
0
      ++count;
323
0
    }
324
0
    EXPECT_EQ(WriteState::FINISHED, result);
325
0
    EXPECT_EQ(100u, count);
326
0
    CheckPalettedSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0));
327
0
  }
328
0
329
0
  // Mark the frame as finished so we don't get an assertion.
330
0
  RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
331
0
  currentFrame->Finish();
332
0
}
333
334
TEST_F(ImageSurfacePipeIntegration, DeinterlaceDownscaleWritePixels)
335
0
{
336
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
337
0
  ASSERT_TRUE(decoder != nullptr);
338
0
339
0
  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
340
0
    CheckWritePixels(aDecoder, aFilter,
341
0
                     /* aOutputRect = */ Some(IntRect(0, 0, 25, 25)));
342
0
  };
343
0
344
0
  WithFilterPipeline(decoder, test,
345
0
                     DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true },
346
0
                     DownscalingConfig { IntSize(100, 100),
347
0
                                         SurfaceFormat::B8G8R8A8 },
348
0
                     SurfaceConfig { decoder, IntSize(25, 25),
349
0
                                     SurfaceFormat::B8G8R8A8, false });
350
0
}
351
352
TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectBottomRightDownscaleWritePixels)
353
0
{
354
0
  // This test case uses a frame rect that extends beyond the borders of the
355
0
  // image to the bottom and to the right. It looks roughly like this (with the
356
0
  // box made of '#'s representing the frame rect):
357
0
  //
358
0
  // +------------+
359
0
  // +            +
360
0
  // +      +------------+
361
0
  // +      +############+
362
0
  // +------+############+
363
0
  //        +############+
364
0
  //        +------------+
365
0
366
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
367
0
  ASSERT_TRUE(decoder != nullptr);
368
0
369
0
  // Note that aInputWriteRect is 100x50 because RemoveFrameRectFilter ignores
370
0
  // trailing rows that don't show up in the output. (Leading rows unfortunately
371
0
  // can't be ignored.) So the action of the pipeline is as follows:
372
0
  //
373
0
  // (1) RemoveFrameRectFilter reads a 100x50 region of the input.
374
0
  //     (aInputWriteRect captures this fact.) The remaining 50 rows are ignored
375
0
  //     because they extend off the bottom of the image due to the frame rect's
376
0
  //     (50, 50) offset. The 50 columns on the right also don't end up in the
377
0
  //     output, so ultimately only a 50x50 region in the output contains data
378
0
  //     from the input. The filter's output is not 50x50, though, but 100x100,
379
0
  //     because what RemoveFrameRectFilter does is introduce blank rows or
380
0
  //     columns as necessary to transform an image that needs a frame rect into
381
0
  //     an image that doesn't.
382
0
  //
383
0
  // (2) DownscalingFilter reads the output of RemoveFrameRectFilter (100x100)
384
0
  //     and downscales it to 20x20.
385
0
  //
386
0
  // (3) The surface owned by SurfaceSink logically has only a 10x10 region
387
0
  //     region in it that's non-blank; this is the downscaled version of the
388
0
  //     50x50 region discussed in (1). (aOutputWriteRect captures this fact.)
389
0
  //     Some fuzz, as usual, is necessary when dealing with Lanczos downscaling.
390
0
391
0
  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
392
0
    CheckWritePixels(aDecoder, aFilter,
393
0
                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
394
0
                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
395
0
                     /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 50)),
396
0
                     /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)),
397
0
                     /* aFuzz = */ 0x33);
398
0
  };
399
0
400
0
  WithFilterPipeline(decoder, test,
401
0
                     RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
402
0
                     DownscalingConfig { IntSize(100, 100),
403
0
                                         SurfaceFormat::B8G8R8A8 },
404
0
                     SurfaceConfig { decoder, IntSize(20, 20),
405
0
                                     SurfaceFormat::B8G8R8A8, false });
406
0
}
407
408
TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectTopLeftDownscaleWritePixels)
409
0
{
410
0
  // This test case uses a frame rect that extends beyond the borders of the
411
0
  // image to the top and to the left. It looks roughly like this (with the
412
0
  // box made of '#'s representing the frame rect):
413
0
  //
414
0
  // +------------+
415
0
  // +############+
416
0
  // +############+------+
417
0
  // +############+      +
418
0
  // +------------+      +
419
0
  //        +            +
420
0
  //        +------------+
421
0
422
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
423
0
  ASSERT_TRUE(decoder != nullptr);
424
0
425
0
  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
426
0
    CheckWritePixels(aDecoder, aFilter,
427
0
                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
428
0
                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
429
0
                     /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
430
0
                     /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)),
431
0
                     /* aFuzz = */ 0x21);
432
0
  };
433
0
434
0
  WithFilterPipeline(decoder, test,
435
0
                     RemoveFrameRectConfig { IntRect(-50, -50, 100, 100) },
436
0
                     DownscalingConfig { IntSize(100, 100),
437
0
                                         SurfaceFormat::B8G8R8A8 },
438
0
                     SurfaceConfig { decoder, IntSize(20, 20),
439
0
                                     SurfaceFormat::B8G8R8A8, false });
440
0
}
441
442
TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels)
443
0
{
444
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
445
0
  ASSERT_TRUE(decoder != nullptr);
446
0
447
0
  // Note that aInputRect is the full 100x100 size even though
448
0
  // RemoveFrameRectFilter is part of this pipeline, because deinterlacing
449
0
  // requires reading every row.
450
0
451
0
  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
452
0
    CheckWritePixels(aDecoder, aFilter,
453
0
                     /* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
454
0
                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
455
0
                     /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)),
456
0
                     /* aOutputWriteRect = */ Some(IntRect(50, 50, 50, 50)));
457
0
  };
458
0
459
0
  WithFilterPipeline(decoder, test,
460
0
                     DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true },
461
0
                     RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
462
0
                     SurfaceConfig { decoder, IntSize(100, 100),
463
0
                                     SurfaceFormat::B8G8R8A8, false });
464
0
}
465
466
TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectDownscaleWritePixels)
467
0
{
468
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
469
0
  ASSERT_TRUE(decoder != nullptr);
470
0
471
0
  auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
472
0
    CheckWritePixels(aDecoder, aFilter,
473
0
                     /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
474
0
                     /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
475
0
                     /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)),
476
0
                     /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)),
477
0
                     /* aFuzz = */ 33);
478
0
  };
479
0
480
0
  WithFilterPipeline(decoder, test,
481
0
                     DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true },
482
0
                     RemoveFrameRectConfig { IntRect(50, 50, 100, 100) },
483
0
                     DownscalingConfig { IntSize(100, 100),
484
0
                                         SurfaceFormat::B8G8R8A8 },
485
0
                     SurfaceConfig { decoder, IntSize(20, 20),
486
0
                                     SurfaceFormat::B8G8R8A8, false });
487
0
}
488
489
TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedRemoveFrameRectDownscaleFails)
490
0
{
491
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
492
0
  ASSERT_TRUE(decoder != nullptr);
493
0
494
0
  // This is an invalid pipeline for paletted images, so configuration should
495
0
  // fail.
496
0
  AssertConfiguringPipelineFails(decoder,
497
0
                                 RemoveFrameRectConfig { IntRect(0, 0, 50, 50) },
498
0
                                 DownscalingConfig { IntSize(100, 100),
499
0
                                                     SurfaceFormat::B8G8R8A8 },
500
0
                                 PalettedSurfaceConfig { decoder, IntSize(100, 100),
501
0
                                                         IntRect(0, 0, 50, 50),
502
0
                                                         SurfaceFormat::B8G8R8A8, 8,
503
0
                                                         false });
504
0
}
505
506
TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedDeinterlaceDownscaleFails)
507
0
{
508
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
509
0
  ASSERT_TRUE(decoder != nullptr);
510
0
511
0
  // This is an invalid pipeline for paletted images, so configuration should
512
0
  // fail.
513
0
  AssertConfiguringPipelineFails(decoder,
514
0
                                 DeinterlacingConfig<uint8_t> { /* mProgressiveDisplay = */ true},
515
0
                                 DownscalingConfig { IntSize(100, 100),
516
0
                                                     SurfaceFormat::B8G8R8A8 },
517
0
                                 PalettedSurfaceConfig { decoder, IntSize(100, 100),
518
0
                                                         IntRect(0, 0, 20, 20),
519
0
                                                         SurfaceFormat::B8G8R8A8, 8,
520
0
                                                         false });
521
0
}
522
523
TEST_F(ImageSurfacePipeIntegration, ConfiguringHugeDeinterlacingBufferFails)
524
0
{
525
0
  RefPtr<Decoder> decoder = CreateTrivialDecoder();
526
0
  ASSERT_TRUE(decoder != nullptr);
527
0
528
0
  // When DownscalingFilter is used, we may succeed in allocating an output
529
0
  // surface for huge images, because we only need to store the scaled-down
530
0
  // version of the image. However, regardless of downscaling,
531
0
  // DeinterlacingFilter needs to allocate a buffer as large as the size of the
532
0
  // input. This can cause OOMs on operating systems that allow overcommit. This
533
0
  // test makes sure that we reject such allocations.
534
0
  AssertConfiguringPipelineFails(decoder,
535
0
                                 DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true},
536
0
                                 DownscalingConfig { IntSize(60000, 60000),
537
0
                                                     SurfaceFormat::B8G8R8A8 },
538
0
                                 SurfaceConfig { decoder, IntSize(600, 600),
539
0
                                                 SurfaceFormat::B8G8R8A8, false });
540
0
}