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 #ifndef mozilla_image_test_gtest_Common_h
7 #define mozilla_image_test_gtest_Common_h
8 
9 #include <vector>
10 
11 #include "gtest/gtest.h"
12 
13 #include "mozilla/Maybe.h"
14 #include "mozilla/UniquePtr.h"
15 #include "mozilla/gfx/2D.h"
16 #include "Decoder.h"
17 #include "gfxColor.h"
18 #include "imgITools.h"
19 #include "nsCOMPtr.h"
20 #include "SurfacePipe.h"
21 #include "SurfacePipeFactory.h"
22 
23 class nsIInputStream;
24 
25 namespace mozilla {
26 namespace image {
27 
28 ///////////////////////////////////////////////////////////////////////////////
29 // Types
30 ///////////////////////////////////////////////////////////////////////////////
31 
32 enum TestCaseFlags
33 {
34   TEST_CASE_DEFAULT_FLAGS   = 0,
35   TEST_CASE_IS_FUZZY        = 1 << 0,
36   TEST_CASE_HAS_ERROR       = 1 << 1,
37   TEST_CASE_IS_TRANSPARENT  = 1 << 2,
38   TEST_CASE_IS_ANIMATED     = 1 << 3,
39   TEST_CASE_IGNORE_OUTPUT   = 1 << 4,
40 };
41 
42 struct ImageTestCase
43 {
44   ImageTestCase(const char* aPath,
45                 const char* aMimeType,
46                 gfx::IntSize aSize,
47                 uint32_t aFlags = TEST_CASE_DEFAULT_FLAGS)
mPathImageTestCase48     : mPath(aPath)
49     , mMimeType(aMimeType)
50     , mSize(aSize)
51     , mOutputSize(aSize)
52     , mFlags(aFlags)
53   { }
54 
55   ImageTestCase(const char* aPath,
56                 const char* aMimeType,
57                 gfx::IntSize aSize,
58                 gfx::IntSize aOutputSize,
59                 uint32_t aFlags = TEST_CASE_DEFAULT_FLAGS)
mPathImageTestCase60     : mPath(aPath)
61     , mMimeType(aMimeType)
62     , mSize(aSize)
63     , mOutputSize(aOutputSize)
64     , mFlags(aFlags)
65   { }
66 
67   const char* mPath;
68   const char* mMimeType;
69   gfx::IntSize mSize;
70   gfx::IntSize mOutputSize;
71   uint32_t mFlags;
72 };
73 
74 struct BGRAColor
75 {
BGRAColorBGRAColor76   BGRAColor() : BGRAColor(0, 0, 0, 0) { }
77 
BGRAColorBGRAColor78   BGRAColor(uint8_t aBlue, uint8_t aGreen, uint8_t aRed, uint8_t aAlpha)
79     : mBlue(aBlue)
80     , mGreen(aGreen)
81     , mRed(aRed)
82     , mAlpha(aAlpha)
83   { }
84 
GreenBGRAColor85   static BGRAColor Green() { return BGRAColor(0x00, 0xFF, 0x00, 0xFF); }
RedBGRAColor86   static BGRAColor Red()   { return BGRAColor(0x00, 0x00, 0xFF, 0xFF); }
BlueBGRAColor87   static BGRAColor Blue()   { return BGRAColor(0xFF, 0x00, 0x00, 0xFF); }
TransparentBGRAColor88   static BGRAColor Transparent() { return BGRAColor(0x00, 0x00, 0x00, 0x00); }
89 
AsPixelBGRAColor90   uint32_t AsPixel() const { return gfxPackedPixel(mAlpha, mRed, mGreen, mBlue); }
91 
92   uint8_t mBlue;
93   uint8_t mGreen;
94   uint8_t mRed;
95   uint8_t mAlpha;
96 };
97 
98 
99 ///////////////////////////////////////////////////////////////////////////////
100 // General Helpers
101 ///////////////////////////////////////////////////////////////////////////////
102 
103 /**
104  * A RAII class that ensure that ImageLib services are available. Any tests that
105  * require ImageLib to be initialized (for example, any test that uses the
106  * SurfaceCache; see image::EnsureModuleInitialized() for the full list) can
107  * use this class to ensure that ImageLib services are available. Failure to do
108  * so can result in strange, non-deterministic failures.
109  */
110 class AutoInitializeImageLib
111 {
112 public:
113   AutoInitializeImageLib();
114 };
115 
116 /// Loads a file from the current directory. @return an nsIInputStream for it.
117 already_AddRefed<nsIInputStream> LoadFile(const char* aRelativePath);
118 
119 /**
120  * @returns true if every pixel of @aSurface is @aColor.
121  *
122  * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
123  * component. This may be necessary for tests that involve JPEG images or
124  * downscaling.
125  */
126 bool IsSolidColor(gfx::SourceSurface* aSurface,
127                   BGRAColor aColor,
128                   uint8_t aFuzz = 0);
129 
130 /**
131  * @returns true if every pixel of @aDecoder's surface has the palette index
132  * specified by @aColor.
133  */
134 bool IsSolidPalettedColor(Decoder* aDecoder, uint8_t aColor);
135 
136 /**
137  * @returns true if every pixel in the range of rows specified by @aStartRow and
138  * @aRowCount of @aSurface is @aColor.
139  *
140  * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
141  * component. This may be necessary for tests that involve JPEG images or
142  * downscaling.
143  */
144 bool RowsAreSolidColor(gfx::SourceSurface* aSurface,
145                        int32_t aStartRow,
146                        int32_t aRowCount,
147                        BGRAColor aColor,
148                        uint8_t aFuzz = 0);
149 
150 /**
151  * @returns true if every pixel in the range of rows specified by @aStartRow and
152  * @aRowCount of @aDecoder's surface has the palette index specified by @aColor.
153  */
154 bool PalettedRowsAreSolidColor(Decoder* aDecoder,
155                                int32_t aStartRow,
156                                int32_t aRowCount,
157                                uint8_t aColor);
158 
159 /**
160  * @returns true if every pixel in the rect specified by @aRect is @aColor.
161  *
162  * If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
163  * component. This may be necessary for tests that involve JPEG images or
164  * downscaling.
165  */
166 bool RectIsSolidColor(gfx::SourceSurface* aSurface,
167                       const gfx::IntRect& aRect,
168                       BGRAColor aColor,
169                       uint8_t aFuzz = 0);
170 
171 /**
172  * @returns true if every pixel in the rect specified by @aRect has the palette
173  * index specified by @aColor.
174  */
175 bool PalettedRectIsSolidColor(Decoder* aDecoder,
176                               const gfx::IntRect& aRect,
177                               uint8_t aColor);
178 
179 /**
180  * @returns true if the pixels in @aRow of @aSurface match the pixels given in
181  * @aPixels.
182  */
183 bool RowHasPixels(gfx::SourceSurface* aSurface,
184                   int32_t aRow,
185                   const std::vector<BGRAColor>& aPixels);
186 
187 // ExpectNoResume is an IResumable implementation for use by tests that expect
188 // Resume() to never get called.
189 class ExpectNoResume final : public IResumable
190 {
191 public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExpectNoResume,override)192   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExpectNoResume, override)
193 
194   void Resume() override { FAIL() << "Resume() should not get called"; }
195 
196 private:
~ExpectNoResume()197   ~ExpectNoResume() override { }
198 };
199 
200 // CountResumes is an IResumable implementation for use by tests that expect
201 // Resume() to get called a certain number of times.
202 class CountResumes : public IResumable
203 {
204 public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CountResumes,override)205   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CountResumes, override)
206 
207   CountResumes() : mCount(0) { }
208 
Resume()209   void Resume() override { mCount++; }
Count()210   uint32_t Count() const { return mCount; }
211 
212 private:
~CountResumes()213   ~CountResumes() override { }
214 
215   uint32_t mCount;
216 };
217 
218 
219 ///////////////////////////////////////////////////////////////////////////////
220 // SurfacePipe Helpers
221 ///////////////////////////////////////////////////////////////////////////////
222 
223 /**
224  * Creates a decoder with no data associated with, suitable for testing code
225  * that requires a decoder to initialize or to allocate surfaces but doesn't
226  * actually need the decoder to do any decoding.
227  *
228  * XXX(seth): We only need this because SurfaceSink and PalettedSurfaceSink
229  * defer to the decoder for surface allocation. Once all decoders use
230  * SurfacePipe we won't need to do that anymore and we can remove this function.
231  */
232 already_AddRefed<Decoder> CreateTrivialDecoder();
233 
234 /**
235  * Creates a pipeline of SurfaceFilters from a list of Config structs and passes
236  * it to the provided lambda @aFunc. Assertions that the pipeline is constructly
237  * correctly and cleanup of any allocated surfaces is handled automatically.
238  *
239  * @param aDecoder The decoder to use for allocating surfaces.
240  * @param aFunc The lambda function to pass the filter pipeline to.
241  * @param aConfigs The configuration for the pipeline.
242  */
243 template <typename Func, typename... Configs>
WithFilterPipeline(Decoder * aDecoder,Func aFunc,Configs...aConfigs)244 void WithFilterPipeline(Decoder* aDecoder, Func aFunc, Configs... aConfigs)
245 {
246   auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
247   nsresult rv = pipe->Configure(aConfigs...);
248   ASSERT_TRUE(NS_SUCCEEDED(rv));
249 
250   aFunc(aDecoder, pipe.get());
251 
252   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
253   if (currentFrame) {
254     currentFrame->Finish();
255   }
256 }
257 
258 /**
259  * Creates a pipeline of SurfaceFilters from a list of Config structs and
260  * asserts that configuring it fails. Cleanup of any allocated surfaces is
261  * handled automatically.
262  *
263  * @param aDecoder The decoder to use for allocating surfaces.
264  * @param aConfigs The configuration for the pipeline.
265  */
266 template <typename... Configs>
AssertConfiguringPipelineFails(Decoder * aDecoder,Configs...aConfigs)267 void AssertConfiguringPipelineFails(Decoder* aDecoder, Configs... aConfigs)
268 {
269   auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
270   nsresult rv = pipe->Configure(aConfigs...);
271 
272   // Callers expect configuring the pipeline to fail.
273   ASSERT_TRUE(NS_FAILED(rv));
274 
275   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
276   if (currentFrame) {
277     currentFrame->Finish();
278   }
279 }
280 
281 /**
282  * Asserts that the provided filter pipeline is in the correct final state,
283  * which is to say, the entire surface has been written to (IsSurfaceFinished()
284  * returns true) and the invalid rects are as expected.
285  *
286  * @param aFilter The filter pipeline to check.
287  * @param aInputSpaceRect The expect invalid rect, in input space.
288  * @param aoutputSpaceRect The expect invalid rect, in output space.
289  */
290 void AssertCorrectPipelineFinalState(SurfaceFilter* aFilter,
291                                      const gfx::IntRect& aInputSpaceRect,
292                                      const gfx::IntRect& aOutputSpaceRect);
293 
294 /**
295  * Checks a generated image for correctness. Reports any unexpected deviation
296  * from the expected image as GTest failures.
297  *
298  * @param aDecoder The decoder which contains the image. The decoder's current
299  *                 frame will be checked.
300  * @param aRect The region in the space of the output surface that the filter
301  *              pipeline will actually write to. It's expected that pixels in
302  *              this region are green, while pixels outside this region are
303  *              transparent.
304  * @param aFuzz The amount of fuzz to use in pixel comparisons.
305  */
306 void CheckGeneratedImage(Decoder* aDecoder,
307                          const gfx::IntRect& aRect,
308                          uint8_t aFuzz = 0);
309 
310 /**
311  * Checks a generated paletted image for correctness. Reports any unexpected
312  * deviation from the expected image as GTest failures.
313  *
314  * @param aDecoder The decoder which contains the image. The decoder's current
315  *                 frame will be checked.
316  * @param aRect The region in the space of the output surface that the filter
317  *              pipeline will actually write to. It's expected that pixels in
318  *              this region have a palette index of 255, while pixels outside
319  *              this region have a palette index of 0.
320  */
321 void CheckGeneratedPalettedImage(Decoder* aDecoder, const gfx::IntRect& aRect);
322 
323 /**
324  * Tests the result of calling WritePixels() using the provided SurfaceFilter
325  * pipeline. The pipeline must be a normal (i.e., non-paletted) pipeline.
326  *
327  * The arguments are specified in the an order intended to minimize the number
328  * of arguments that most test cases need to pass.
329  *
330  * @param aDecoder The decoder whose current frame will be written to.
331  * @param aFilter The SurfaceFilter pipeline to use.
332  * @param aOutputRect The region in the space of the output surface that will be
333  *                    invalidated by the filter pipeline. Defaults to
334  *                    (0, 0, 100, 100).
335  * @param aInputRect The region in the space of the input image that will be
336  *                   invalidated by the filter pipeline. Defaults to
337  *                   (0, 0, 100, 100).
338  * @param aInputWriteRect The region in the space of the input image that the
339  *                        filter pipeline will allow writes to. Note the
340  *                        difference from @aInputRect: @aInputRect is the actual
341  *                        region invalidated, while @aInputWriteRect is the
342  *                        region that is written to. These can differ in cases
343  *                        where the input is not clipped to the size of the image.
344  *                        Defaults to the entire input rect.
345  * @param aOutputWriteRect The region in the space of the output surface that
346  *                         the filter pipeline will actually write to. It's
347  *                         expected that pixels in this region are green, while
348  *                         pixels outside this region are transparent. Defaults
349  *                         to the entire output rect.
350  */
351 void CheckWritePixels(Decoder* aDecoder,
352                       SurfaceFilter* aFilter,
353                       const Maybe<gfx::IntRect>& aOutputRect = Nothing(),
354                       const Maybe<gfx::IntRect>& aInputRect = Nothing(),
355                       const Maybe<gfx::IntRect>& aInputWriteRect = Nothing(),
356                       const Maybe<gfx::IntRect>& aOutputWriteRect = Nothing(),
357                       uint8_t aFuzz = 0);
358 
359 /**
360  * Tests the result of calling WritePixels() using the provided SurfaceFilter
361  * pipeline. The pipeline must be a paletted pipeline.
362  * @see CheckWritePixels() for documentation of the arguments.
363  */
364 void CheckPalettedWritePixels(Decoder* aDecoder,
365                               SurfaceFilter* aFilter,
366                               const Maybe<gfx::IntRect>& aOutputRect = Nothing(),
367                               const Maybe<gfx::IntRect>& aInputRect = Nothing(),
368                               const Maybe<gfx::IntRect>& aInputWriteRect = Nothing(),
369                               const Maybe<gfx::IntRect>& aOutputWriteRect = Nothing(),
370                               uint8_t aFuzz = 0);
371 
372 
373 ///////////////////////////////////////////////////////////////////////////////
374 // Test Data
375 ///////////////////////////////////////////////////////////////////////////////
376 
377 ImageTestCase GreenPNGTestCase();
378 ImageTestCase GreenGIFTestCase();
379 ImageTestCase GreenJPGTestCase();
380 ImageTestCase GreenBMPTestCase();
381 ImageTestCase GreenICOTestCase();
382 ImageTestCase GreenIconTestCase();
383 
384 ImageTestCase GreenFirstFrameAnimatedGIFTestCase();
385 ImageTestCase GreenFirstFrameAnimatedPNGTestCase();
386 
387 ImageTestCase CorruptTestCase();
388 ImageTestCase CorruptBMPWithTruncatedHeader();
389 ImageTestCase CorruptICOWithBadBMPWidthTestCase();
390 ImageTestCase CorruptICOWithBadBMPHeightTestCase();
391 ImageTestCase CorruptICOWithBadBppTestCase();
392 
393 ImageTestCase TransparentPNGTestCase();
394 ImageTestCase TransparentGIFTestCase();
395 ImageTestCase FirstFramePaddingGIFTestCase();
396 ImageTestCase TransparentIfWithinICOBMPTestCase(TestCaseFlags aFlags);
397 ImageTestCase NoFrameDelayGIFTestCase();
398 ImageTestCase ExtraImageSubBlocksAnimatedGIFTestCase();
399 
400 ImageTestCase TransparentBMPWhenBMPAlphaEnabledTestCase();
401 ImageTestCase RLE4BMPTestCase();
402 ImageTestCase RLE8BMPTestCase();
403 
404 ImageTestCase DownscaledPNGTestCase();
405 ImageTestCase DownscaledGIFTestCase();
406 ImageTestCase DownscaledJPGTestCase();
407 ImageTestCase DownscaledBMPTestCase();
408 ImageTestCase DownscaledICOTestCase();
409 ImageTestCase DownscaledIconTestCase();
410 ImageTestCase DownscaledTransparentICOWithANDMaskTestCase();
411 
412 ImageTestCase TruncatedSmallGIFTestCase();
413 
414 ImageTestCase LargeICOWithBMPTestCase();
415 ImageTestCase LargeICOWithPNGTestCase();
416 ImageTestCase GreenMultipleSizesICOTestCase();
417 
418 } // namespace image
419 } // namespace mozilla
420 
421 #endif // mozilla_image_test_gtest_Common_h
422