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