/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 | } |