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  public:
SimpleSurfacePipe()25   static SurfacePipe SimpleSurfacePipe() {
26     SurfacePipe pipe;
27     return pipe;
28   }
29 
30   template <typename T>
SurfacePipeFromPipeline(T && aPipeline)31   static SurfacePipe SurfacePipeFromPipeline(T&& aPipeline) {
32     return SurfacePipe{std::move(aPipeline)};
33   }
34 
35  private:
TestSurfacePipeFactory()36   TestSurfacePipeFactory() {}
37 };
38 
39 }  // namespace image
40 }  // namespace mozilla
41 
CheckSurfacePipeMethodResults(SurfacePipe * aPipe,image::Decoder * aDecoder,const IntRect & aRect=IntRect (0,0,100,100))42 void CheckSurfacePipeMethodResults(SurfacePipe* aPipe, image::Decoder* aDecoder,
43                                    const IntRect& aRect = IntRect(0, 0, 100,
44                                                                   100)) {
45   // Check that the pipeline ended up in the state we expect.  Note that we're
46   // explicitly testing the SurfacePipe versions of these methods, so we don't
47   // want to use AssertCorrectPipelineFinalState() here.
48   EXPECT_TRUE(aPipe->IsSurfaceFinished());
49   Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect();
50   EXPECT_TRUE(invalidRect.isSome());
51   EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
52   EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
53 
54   // Check the generated image.
55   CheckGeneratedImage(aDecoder, aRect);
56 
57   // Reset and clear the image before the next test.
58   aPipe->ResetToFirstRow();
59   EXPECT_FALSE(aPipe->IsSurfaceFinished());
60   invalidRect = aPipe->TakeInvalidRect();
61   EXPECT_TRUE(invalidRect.isNothing());
62 
63   uint32_t count = 0;
64   auto result = aPipe->WritePixels<uint32_t>([&]() {
65     ++count;
66     return AsVariant(BGRAColor::Transparent().AsPixel());
67   });
68   EXPECT_EQ(WriteState::FINISHED, result);
69   EXPECT_EQ(100u * 100u, count);
70 
71   EXPECT_TRUE(aPipe->IsSurfaceFinished());
72   invalidRect = aPipe->TakeInvalidRect();
73   EXPECT_TRUE(invalidRect.isSome());
74   EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
75   EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
76 
77   aPipe->ResetToFirstRow();
78   EXPECT_FALSE(aPipe->IsSurfaceFinished());
79   invalidRect = aPipe->TakeInvalidRect();
80   EXPECT_TRUE(invalidRect.isNothing());
81 }
82 
83 class ImageSurfacePipeIntegration : public ::testing::Test {
84  protected:
85   AutoInitializeImageLib mInit;
86 };
87 
TEST_F(ImageSurfacePipeIntegration,SurfacePipe)88 TEST_F(ImageSurfacePipeIntegration, SurfacePipe) {
89   // Test that SurfacePipe objects can be initialized and move constructed.
90   SurfacePipe pipe = TestSurfacePipeFactory::SimpleSurfacePipe();
91 
92   // Test that SurfacePipe objects can be move assigned.
93   pipe = TestSurfacePipeFactory::SimpleSurfacePipe();
94 
95   // Test that SurfacePipe objects can be initialized with a pipeline.
96   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
97   ASSERT_TRUE(decoder != nullptr);
98 
99   auto sink = MakeUnique<SurfaceSink>();
100   nsresult rv = sink->Configure(
101       SurfaceConfig{decoder, IntSize(100, 100), SurfaceFormat::OS_RGBA, false});
102   ASSERT_TRUE(NS_SUCCEEDED(rv));
103 
104   pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink);
105 
106   // Test that WritePixels() gets passed through to the underlying pipeline.
107   {
108     uint32_t count = 0;
109     auto result = pipe.WritePixels<uint32_t>([&]() {
110       ++count;
111       return AsVariant(BGRAColor::Green().AsPixel());
112     });
113     EXPECT_EQ(WriteState::FINISHED, result);
114     EXPECT_EQ(100u * 100u, count);
115     CheckSurfacePipeMethodResults(&pipe, decoder);
116   }
117 
118   // Create a buffer the same size as one row of the surface, containing all
119   // green pixels. We'll use this for the WriteBuffer() tests.
120   uint32_t buffer[100];
121   for (int i = 0; i < 100; ++i) {
122     buffer[i] = BGRAColor::Green().AsPixel();
123   }
124 
125   // Test that WriteBuffer() gets passed through to the underlying pipeline.
126   {
127     uint32_t count = 0;
128     WriteState result = WriteState::NEED_MORE_DATA;
129     while (result == WriteState::NEED_MORE_DATA) {
130       result = pipe.WriteBuffer(buffer);
131       ++count;
132     }
133     EXPECT_EQ(WriteState::FINISHED, result);
134     EXPECT_EQ(100u, count);
135     CheckSurfacePipeMethodResults(&pipe, decoder);
136   }
137 
138   // Test that the 3 argument version of WriteBuffer() gets passed through to
139   // the underlying pipeline.
140   {
141     uint32_t count = 0;
142     WriteState result = WriteState::NEED_MORE_DATA;
143     while (result == WriteState::NEED_MORE_DATA) {
144       result = pipe.WriteBuffer(buffer, 0, 100);
145       ++count;
146     }
147     EXPECT_EQ(WriteState::FINISHED, result);
148     EXPECT_EQ(100u, count);
149     CheckSurfacePipeMethodResults(&pipe, decoder);
150   }
151 
152   // Test that WritePixelBlocks() gets passed through to the underlying
153   // pipeline.
154   {
155     uint32_t count = 0;
156     WriteState result = pipe.WritePixelBlocks<uint32_t>(
157         [&](uint32_t* aBlockStart, int32_t aLength) {
158           ++count;
159           EXPECT_EQ(int32_t(100), aLength);
160           memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
161           return MakeTuple(int32_t(100), Maybe<WriteState>());
162         });
163 
164     EXPECT_EQ(WriteState::FINISHED, result);
165     EXPECT_EQ(100u, count);
166     CheckSurfacePipeMethodResults(&pipe, decoder);
167   }
168 
169   // Test that WriteEmptyRow() gets passed through to the underlying pipeline.
170   {
171     uint32_t count = 0;
172     WriteState result = WriteState::NEED_MORE_DATA;
173     while (result == WriteState::NEED_MORE_DATA) {
174       result = pipe.WriteEmptyRow();
175       ++count;
176     }
177     EXPECT_EQ(WriteState::FINISHED, result);
178     EXPECT_EQ(100u, count);
179     CheckSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0));
180   }
181 
182   // Mark the frame as finished so we don't get an assertion.
183   RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
184   currentFrame->Finish();
185 }
186 
TEST_F(ImageSurfacePipeIntegration,DeinterlaceDownscaleWritePixels)187 TEST_F(ImageSurfacePipeIntegration, DeinterlaceDownscaleWritePixels) {
188   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
189   ASSERT_TRUE(decoder != nullptr);
190 
191   auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
192     CheckWritePixels(aDecoder, aFilter,
193                      /* aOutputRect = */ Some(IntRect(0, 0, 25, 25)));
194   };
195 
196   WithFilterPipeline(
197       decoder, test,
198       DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
199       DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
200       SurfaceConfig{decoder, IntSize(25, 25), SurfaceFormat::OS_RGBA, false});
201 }
202 
TEST_F(ImageSurfacePipeIntegration,RemoveFrameRectBottomRightDownscaleWritePixels)203 TEST_F(ImageSurfacePipeIntegration,
204        RemoveFrameRectBottomRightDownscaleWritePixels) {
205   // This test case uses a frame rect that extends beyond the borders of the
206   // image to the bottom and to the right. It looks roughly like this (with the
207   // box made of '#'s representing the frame rect):
208   //
209   // +------------+
210   // +            +
211   // +      +------------+
212   // +      +############+
213   // +------+############+
214   //        +############+
215   //        +------------+
216 
217   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
218   ASSERT_TRUE(decoder != nullptr);
219 
220   // Note that aInputWriteRect is 100x50 because RemoveFrameRectFilter ignores
221   // trailing rows that don't show up in the output. (Leading rows unfortunately
222   // can't be ignored.) So the action of the pipeline is as follows:
223   //
224   // (1) RemoveFrameRectFilter reads a 100x50 region of the input.
225   //     (aInputWriteRect captures this fact.) The remaining 50 rows are ignored
226   //     because they extend off the bottom of the image due to the frame rect's
227   //     (50, 50) offset. The 50 columns on the right also don't end up in the
228   //     output, so ultimately only a 50x50 region in the output contains data
229   //     from the input. The filter's output is not 50x50, though, but 100x100,
230   //     because what RemoveFrameRectFilter does is introduce blank rows or
231   //     columns as necessary to transform an image that needs a frame rect into
232   //     an image that doesn't.
233   //
234   // (2) DownscalingFilter reads the output of RemoveFrameRectFilter (100x100)
235   //     and downscales it to 20x20.
236   //
237   // (3) The surface owned by SurfaceSink logically has only a 10x10 region
238   //     region in it that's non-blank; this is the downscaled version of the
239   //     50x50 region discussed in (1). (aOutputWriteRect captures this fact.)
240   //     Some fuzz, as usual, is necessary when dealing with Lanczos
241   //     downscaling.
242 
243   auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
244     CheckWritePixels(aDecoder, aFilter,
245                      /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
246                      /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
247                      /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 50)),
248                      /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)),
249                      /* aFuzz = */ 0x33);
250   };
251 
252   WithFilterPipeline(
253       decoder, test, RemoveFrameRectConfig{IntRect(50, 50, 100, 100)},
254       DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
255       SurfaceConfig{decoder, IntSize(20, 20), SurfaceFormat::OS_RGBA, false});
256 }
257 
TEST_F(ImageSurfacePipeIntegration,RemoveFrameRectTopLeftDownscaleWritePixels)258 TEST_F(ImageSurfacePipeIntegration,
259        RemoveFrameRectTopLeftDownscaleWritePixels) {
260   // This test case uses a frame rect that extends beyond the borders of the
261   // image to the top and to the left. It looks roughly like this (with the
262   // box made of '#'s representing the frame rect):
263   //
264   // +------------+
265   // +############+
266   // +############+------+
267   // +############+      +
268   // +------------+      +
269   //        +            +
270   //        +------------+
271 
272   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
273   ASSERT_TRUE(decoder != nullptr);
274 
275   auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
276     CheckWritePixels(aDecoder, aFilter,
277                      /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
278                      /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
279                      /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
280                      /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)),
281                      /* aFuzz = */ 0x21);
282   };
283 
284   WithFilterPipeline(
285       decoder, test, RemoveFrameRectConfig{IntRect(-50, -50, 100, 100)},
286       DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
287       SurfaceConfig{decoder, IntSize(20, 20), SurfaceFormat::OS_RGBA, false});
288 }
289 
TEST_F(ImageSurfacePipeIntegration,DeinterlaceRemoveFrameRectWritePixels)290 TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels) {
291   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
292   ASSERT_TRUE(decoder != nullptr);
293 
294   // Note that aInputRect is the full 100x100 size even though
295   // RemoveFrameRectFilter is part of this pipeline, because deinterlacing
296   // requires reading every row.
297 
298   auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
299     CheckWritePixels(aDecoder, aFilter,
300                      /* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
301                      /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
302                      /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)),
303                      /* aOutputWriteRect = */ Some(IntRect(50, 50, 50, 50)));
304   };
305 
306   WithFilterPipeline(
307       decoder, test,
308       DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
309       RemoveFrameRectConfig{IntRect(50, 50, 100, 100)},
310       SurfaceConfig{decoder, IntSize(100, 100), SurfaceFormat::OS_RGBA, false});
311 }
312 
TEST_F(ImageSurfacePipeIntegration,DeinterlaceRemoveFrameRectDownscaleWritePixels)313 TEST_F(ImageSurfacePipeIntegration,
314        DeinterlaceRemoveFrameRectDownscaleWritePixels) {
315   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
316   ASSERT_TRUE(decoder != nullptr);
317 
318   auto test = [](image::Decoder* aDecoder, SurfaceFilter* aFilter) {
319     CheckWritePixels(aDecoder, aFilter,
320                      /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)),
321                      /* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
322                      /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)),
323                      /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)),
324                      /* aFuzz = */ 33);
325   };
326 
327   WithFilterPipeline(
328       decoder, test,
329       DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
330       RemoveFrameRectConfig{IntRect(50, 50, 100, 100)},
331       DownscalingConfig{IntSize(100, 100), SurfaceFormat::OS_RGBA},
332       SurfaceConfig{decoder, IntSize(20, 20), SurfaceFormat::OS_RGBA, false});
333 }
334 
TEST_F(ImageSurfacePipeIntegration,ConfiguringHugeDeinterlacingBufferFails)335 TEST_F(ImageSurfacePipeIntegration, ConfiguringHugeDeinterlacingBufferFails) {
336   RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
337   ASSERT_TRUE(decoder != nullptr);
338 
339   // When DownscalingFilter is used, we may succeed in allocating an output
340   // surface for huge images, because we only need to store the scaled-down
341   // version of the image. However, regardless of downscaling,
342   // DeinterlacingFilter needs to allocate a buffer as large as the size of the
343   // input. This can cause OOMs on operating systems that allow overcommit. This
344   // test makes sure that we reject such allocations.
345   AssertConfiguringPipelineFails(
346       decoder, DeinterlacingConfig<uint32_t>{/* mProgressiveDisplay = */ true},
347       DownscalingConfig{IntSize(60000, 60000), SurfaceFormat::OS_RGBA},
348       SurfaceConfig{decoder, IntSize(600, 600), SurfaceFormat::OS_RGBA, false});
349 }
350