1 /*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "Resources.h"
9 #include "SkBitmap.h"
10 #include "SkCanvas.h"
11 #include "SkColor.h"
12 #include "SkColorPriv.h"
13 #include "SkData.h"
14 #include "SkDecodingImageGenerator.h"
15 #include "SkDiscardableMemoryPool.h"
16 #include "SkForceLinking.h"
17 #include "SkGradientShader.h"
18 #include "SkImageDecoder.h"
19 #include "SkImageEncoder.h"
20 #include "SkImageGeneratorPriv.h"
21 #include "SkImagePriv.h"
22 #include "SkOSFile.h"
23 #include "SkPoint.h"
24 #include "SkShader.h"
25 #include "SkStream.h"
26 #include "SkString.h"
27 #include "Test.h"
28
29 __SK_FORCE_IMAGE_DECODER_LINKING;
30
31 /**
32 * Interprets c as an unpremultiplied color, and returns the
33 * premultiplied equivalent.
34 */
premultiply_unpmcolor(SkPMColor c)35 static SkPMColor premultiply_unpmcolor(SkPMColor c) {
36 U8CPU a = SkGetPackedA32(c);
37 U8CPU r = SkGetPackedR32(c);
38 U8CPU g = SkGetPackedG32(c);
39 U8CPU b = SkGetPackedB32(c);
40 return SkPreMultiplyARGB(a, r, g, b);
41 }
42
43 /**
44 * Return true if this stream format should be skipped, due
45 * to do being an opaque format or not a valid format.
46 */
skip_image_format(SkImageDecoder::Format format)47 static bool skip_image_format(SkImageDecoder::Format format) {
48 switch (format) {
49 case SkImageDecoder::kPNG_Format:
50 case SkImageDecoder::kWEBP_Format:
51 return false;
52 // Skip unknown since it will not be decoded anyway.
53 case SkImageDecoder::kUnknown_Format:
54 // Technically ICO and BMP supports alpha channels, but our image
55 // decoders do not, so skip them as well.
56 case SkImageDecoder::kICO_Format:
57 case SkImageDecoder::kBMP_Format:
58 // KTX and ASTC are texture formats so it's not particularly clear how to
59 // decode the alpha from them.
60 case SkImageDecoder::kKTX_Format:
61 case SkImageDecoder::kASTC_Format:
62 // The rest of these are opaque.
63 case SkImageDecoder::kPKM_Format:
64 case SkImageDecoder::kWBMP_Format:
65 case SkImageDecoder::kGIF_Format:
66 case SkImageDecoder::kJPEG_Format:
67 return true;
68 }
69 SkASSERT(false);
70 return true;
71 }
72
73 /**
74 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
75 * them.
76 */
compare_unpremul(skiatest::Reporter * reporter,const SkString & filename)77 static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
78 // Decode a resource:
79 SkBitmap bm8888;
80 SkBitmap bm8888Unpremul;
81
82 SkFILEStream stream(filename.c_str());
83
84 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
85 if (skip_image_format(format)) {
86 return;
87 }
88
89 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
90 if (NULL == decoder.get()) {
91 SkDebugf("couldn't decode %s\n", filename.c_str());
92 return;
93 }
94
95 bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType,
96 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
97 if (!success) {
98 return;
99 }
100
101 success = stream.rewind();
102 REPORTER_ASSERT(reporter, success);
103 if (!success) {
104 return;
105 }
106
107 decoder->setRequireUnpremultipliedColors(true);
108 success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType,
109 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
110 if (!success) {
111 return;
112 }
113
114 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
115 && bm8888.height() == bm8888Unpremul.height();
116 REPORTER_ASSERT(reporter, dimensionsMatch);
117 if (!dimensionsMatch) {
118 return;
119 }
120
121 // Only do the comparison if the two bitmaps are both 8888.
122 if (bm8888.colorType() != kN32_SkColorType || bm8888Unpremul.colorType() != kN32_SkColorType) {
123 return;
124 }
125
126 // Now compare the two bitmaps.
127 for (int i = 0; i < bm8888.width(); ++i) {
128 for (int j = 0; j < bm8888.height(); ++j) {
129 // "c0" is the color of the premultiplied bitmap at (i, j).
130 const SkPMColor c0 = *bm8888.getAddr32(i, j);
131 // "c1" is the result of premultiplying the color of the unpremultiplied
132 // bitmap at (i, j).
133 const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
134 // Compute the difference for each component.
135 int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
136 int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
137 int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
138 int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
139
140 // Alpha component must be exactly the same.
141 REPORTER_ASSERT(reporter, 0 == da);
142
143 // Color components may not match exactly due to rounding error.
144 REPORTER_ASSERT(reporter, dr <= 1);
145 REPORTER_ASSERT(reporter, dg <= 1);
146 REPORTER_ASSERT(reporter, db <= 1);
147 }
148 }
149 }
150
test_unpremul(skiatest::Reporter * reporter)151 static void test_unpremul(skiatest::Reporter* reporter) {
152 // This test cannot run if there is no resource path.
153 SkString resourcePath = GetResourcePath();
154 if (resourcePath.isEmpty()) {
155 SkDebugf("Could not run unpremul test because resourcePath not specified.");
156 return;
157 }
158 SkOSFile::Iter iter(resourcePath.c_str());
159 SkString basename;
160 if (iter.next(&basename)) {
161 do {
162 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
163 // SkDebugf("about to decode \"%s\"\n", filename.c_str());
164 compare_unpremul(reporter, filename);
165 } while (iter.next(&basename));
166 } else {
167 SkDebugf("Failed to find any files :(\n");
168 }
169 }
170
171 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
172 // Test that the alpha type is what we expect.
test_alphaType(skiatest::Reporter * reporter,const SkString & filename,bool requireUnpremul)173 static void test_alphaType(skiatest::Reporter* reporter, const SkString& filename,
174 bool requireUnpremul) {
175 SkBitmap bm;
176 SkFILEStream stream(filename.c_str());
177
178 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
179 if (NULL == decoder.get()) {
180 return;
181 }
182
183 decoder->setRequireUnpremultipliedColors(requireUnpremul);
184
185 // Decode just the bounds. This should always succeed.
186 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
187 SkImageDecoder::kDecodeBounds_Mode);
188 REPORTER_ASSERT(reporter, success);
189 if (!success) {
190 return;
191 }
192
193 // Keep track of the alpha type for testing later. If the full decode
194 // succeeds, the alpha type should be the same, unless the full decode
195 // determined that the alpha type should actually be opaque, which may
196 // not be known when only decoding the bounds.
197 const SkAlphaType boundsAlphaType = bm.alphaType();
198
199 // rewind should always succeed on SkFILEStream.
200 success = stream.rewind();
201 REPORTER_ASSERT(reporter, success);
202 if (!success) {
203 return;
204 }
205
206 success = decoder->decode(&stream, &bm, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode);
207
208 if (!success) {
209 // When the decoder is set to require unpremul, if it does not support
210 // unpremul it will fail. This is the only reason the decode should
211 // fail (since we know the files we are using to test can be decoded).
212 REPORTER_ASSERT(reporter, requireUnpremul);
213 return;
214 }
215
216 // The bounds decode should return with either the requested
217 // premul/unpremul or opaque, if that value could be determined when only
218 // decoding the bounds.
219 if (requireUnpremul) {
220 REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType
221 || kOpaque_SkAlphaType == boundsAlphaType
222 || filename.endsWith(".ico"));
223 // TODO(halcanary): Find out why color_wheel.ico fails this test.
224 } else {
225 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
226 || kOpaque_SkAlphaType == boundsAlphaType);
227 }
228
229 // When decoding the full image, the alpha type should match the one
230 // returned by the bounds decode, unless the full decode determined that
231 // the alpha type is actually opaque.
232 REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
233 || bm.alphaType() == kOpaque_SkAlphaType);
234 }
235
DEF_TEST(ImageDecoding_alphaType,reporter)236 DEF_TEST(ImageDecoding_alphaType, reporter) {
237 SkString resourcePath = GetResourcePath();
238 if (resourcePath.isEmpty()) {
239 SkDebugf("Could not run alphaType test because resourcePath not specified.");
240 return;
241 }
242
243 SkOSFile::Iter iter(resourcePath.c_str());
244 SkString basename;
245 if (iter.next(&basename)) {
246 do {
247 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
248 for (int truth = 0; truth <= 1; ++truth) {
249 test_alphaType(reporter, filename, SkToBool(truth));
250 }
251 } while (iter.next(&basename));
252 } else {
253 SkDebugf("Failed to find any files :(\n");
254 }
255
256 }
257
258 // Using known images, test that decoding into unpremul and premul behave as expected.
DEF_TEST(ImageDecoding_unpremul,reporter)259 DEF_TEST(ImageDecoding_unpremul, reporter) {
260 SkString resourcePath = GetResourcePath();
261 if (resourcePath.isEmpty()) {
262 SkDebugf("Could not run unpremul test because resourcePath not specified.");
263 return;
264 }
265 const char* root = "half-transparent-white-pixel";
266 const char* suffixes[] = { ".png", ".webp" };
267
268 for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
269 SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
270 SkString fullName = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
271
272 SkBitmap bm;
273 SkFILEStream stream(fullName.c_str());
274
275 if (!stream.isValid()) {
276 SkDebugf("file %s missing from resource directoy %s\n",
277 basename.c_str(), resourcePath.c_str());
278 continue;
279 }
280
281 // This should never fail since we know the images we're decoding.
282 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
283 REPORTER_ASSERT(reporter, decoder.get());
284 if (NULL == decoder.get()) {
285 continue;
286 }
287
288 // Test unpremultiplied. We know what color this should result in.
289 decoder->setRequireUnpremultipliedColors(true);
290 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
291 SkImageDecoder::kDecodePixels_Mode);
292 REPORTER_ASSERT(reporter, success);
293 if (!success) {
294 continue;
295 }
296
297 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
298 {
299 SkAutoLockPixels alp(bm);
300 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
301 }
302
303 success = stream.rewind();
304 REPORTER_ASSERT(reporter, success);
305 if (!success) {
306 continue;
307 }
308
309 // Test premultiplied. Once again, we know which color this should
310 // result in.
311 decoder->setRequireUnpremultipliedColors(false);
312 success = decoder->decode(&stream, &bm, kN32_SkColorType,
313 SkImageDecoder::kDecodePixels_Mode);
314 REPORTER_ASSERT(reporter, success);
315 if (!success) {
316 continue;
317 }
318
319 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
320 {
321 SkAutoLockPixels alp(bm);
322 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
323 }
324 }
325 }
326 #endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
327
328 #ifdef SK_DEBUG
329 // Test inside SkScaledBitmapSampler.cpp
330 extern void test_row_proc_choice();
331 #endif // SK_DEBUG
332
DEF_TEST(ImageDecoding,reporter)333 DEF_TEST(ImageDecoding, reporter) {
334 test_unpremul(reporter);
335 #ifdef SK_DEBUG
336 test_row_proc_choice();
337 #endif
338 }
339
340 // expected output for 8x8 bitmap
341 static const int kExpectedWidth = 8;
342 static const int kExpectedHeight = 8;
343 static const SkColor kExpectedPixels[] = {
344 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
345 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
346 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
347 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
348 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
349 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
350 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
351 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
352 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
353 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
354 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
355 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
356 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
357 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
358 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
359 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
360 };
361 SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
362 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
363
DEF_TEST(WebP,reporter)364 DEF_TEST(WebP, reporter) {
365 const unsigned char encodedWebP[] = {
366 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
367 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
368 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
369 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
370 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
371 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
372 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
373 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
374 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
375 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
376 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
377 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
378 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
379 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
380 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
381 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
382 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
383 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
384 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
385 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
386 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
387 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
388 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
389 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
390 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
391 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
392 };
393 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
394 sizeof(encodedWebP)));
395 SkBitmap bm;
396
397 bool success = SkInstallDiscardablePixelRef(encoded, &bm);
398
399 REPORTER_ASSERT(reporter, success);
400 if (!success) {
401 return;
402 }
403 SkAutoLockPixels alp(bm);
404
405 bool rightSize = ((kExpectedWidth == bm.width())
406 && (kExpectedHeight == bm.height()));
407 REPORTER_ASSERT(reporter, rightSize);
408 if (rightSize) {
409 bool error = false;
410 const SkColor* correctPixel = kExpectedPixels;
411 for (int y = 0; y < bm.height(); ++y) {
412 for (int x = 0; x < bm.width(); ++x) {
413 error |= (*correctPixel != bm.getColor(x, y));
414 ++correctPixel;
415 }
416 }
417 REPORTER_ASSERT(reporter, !error);
418 }
419 }
420
421 ////////////////////////////////////////////////////////////////////////////////
422
423 // example of how Android will do this inside their BitmapFactory
install_pixel_ref(SkBitmap * bitmap,SkStreamRewindable * stream,int sampleSize,bool ditherImage)424 static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
425 SkStreamRewindable* stream,
426 int sampleSize, bool ditherImage) {
427 SkASSERT(bitmap != NULL);
428 SkASSERT(stream != NULL);
429 SkASSERT(stream->rewind());
430 SkColorType colorType = bitmap->colorType();
431 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
432 if (SkInstallDiscardablePixelRef(
433 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
434 return bitmap->pixelRef();
435 }
436 return NULL;
437 }
438 /**
439 * A test for the SkDecodingImageGenerator::Create and
440 * SkInstallDiscardablePixelRef functions.
441 */
DEF_TEST(ImprovedBitmapFactory,reporter)442 DEF_TEST(ImprovedBitmapFactory, reporter) {
443 SkString pngFilename = GetResourcePath("randPixels.png");
444 SkAutoTDelete<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
445 if (sk_exists(pngFilename.c_str())) {
446 SkBitmap bm;
447 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
448 REPORTER_ASSERT(reporter,
449 install_pixel_ref(&bm, stream.detach(), 1, true));
450 SkAutoLockPixels alp(bm);
451 REPORTER_ASSERT(reporter, bm.getPixels());
452 }
453 }
454
455
456 ////////////////////////////////////////////////////////////////////////////////
457
458 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
check_rounding(int value,int dividend,int divisor)459 static inline bool check_rounding(int value, int dividend, int divisor) {
460 // returns true if the value is greater than floor(dividend/divisor)
461 // and less than SkNextPow2(ceil(dividend - divisor))
462 return (((divisor * value) > (dividend - divisor))
463 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
464 }
465 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
466
467
468 #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
469 #define kBackwards_SkColorType kRGBA_8888_SkColorType
470 #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
471 #define kBackwards_SkColorType kBGRA_8888_SkColorType
472 #else
473 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
474 #endif
475
SkColorType_to_string(SkColorType colorType)476 static inline const char* SkColorType_to_string(SkColorType colorType) {
477 switch(colorType) {
478 case kAlpha_8_SkColorType: return "Alpha_8";
479 case kRGB_565_SkColorType: return "RGB_565";
480 case kARGB_4444_SkColorType: return "ARGB_4444";
481 case kN32_SkColorType: return "N32";
482 case kBackwards_SkColorType: return "Backwards";
483 case kIndex_8_SkColorType: return "Index_8";
484 default: return "ERROR";
485 }
486 }
487
options_colorType(const SkDecodingImageGenerator::Options & opts)488 static inline const char* options_colorType(
489 const SkDecodingImageGenerator::Options& opts) {
490 if (opts.fUseRequestedColorType) {
491 return SkColorType_to_string(opts.fRequestedColorType);
492 } else {
493 return "(none)";
494 }
495 }
496
yn(bool value)497 static inline const char* yn(bool value) {
498 if (value) {
499 return "yes";
500 } else {
501 return "no";
502 }
503 }
504
505 /**
506 * Given either a SkStream or a SkData, try to decode the encoded
507 * image using the specified options and report errors.
508 */
test_options(skiatest::Reporter * reporter,const SkDecodingImageGenerator::Options & opts,SkStreamRewindable * encodedStream,SkData * encodedData,bool useData,const SkString & path)509 static void test_options(skiatest::Reporter* reporter,
510 const SkDecodingImageGenerator::Options& opts,
511 SkStreamRewindable* encodedStream,
512 SkData* encodedData,
513 bool useData,
514 const SkString& path) {
515 SkBitmap bm;
516 bool success = false;
517 if (useData) {
518 if (NULL == encodedData) {
519 return;
520 }
521 success = SkInstallDiscardablePixelRef(
522 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
523 } else {
524 if (NULL == encodedStream) {
525 return;
526 }
527 success = SkInstallDiscardablePixelRef(
528 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
529 }
530 if (!success) {
531 if (opts.fUseRequestedColorType
532 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
533 return; // Ignore known conversion inabilities.
534 }
535 // If we get here, it's a failure and we will need more
536 // information about why it failed.
537 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
538 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
539 options_colorType(opts), path.c_str());
540 return;
541 }
542 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
543 // Android is the only system that use Skia's image decoders in
544 // production. For now, we'll only verify that samplesize works
545 // on systems where it already is known to work.
546 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
547 opts.fSampleSize));
548 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
549 opts.fSampleSize));
550 // The ImageDecoder API doesn't guarantee that SampleSize does
551 // anything at all, but the decoders that this test excercises all
552 // produce an output size in the following range:
553 // (((sample_size * out_size) > (in_size - sample_size))
554 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
555 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
556 SkAutoLockPixels alp(bm);
557 if (bm.getPixels() == NULL) {
558 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
559 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
560 options_colorType(opts), path.c_str());
561 return;
562 }
563
564 SkColorType requestedColorType = opts.fRequestedColorType;
565 REPORTER_ASSERT(reporter,
566 (!opts.fUseRequestedColorType)
567 || (bm.colorType() == requestedColorType));
568
569 // Condition under which we should check the decoding results:
570 if ((kN32_SkColorType == bm.colorType())
571 && (!path.endsWith(".jpg")) // lossy
572 && (opts.fSampleSize == 1)) { // scaled
573 const SkColor* correctPixels = kExpectedPixels;
574 SkASSERT(bm.height() == kExpectedHeight);
575 SkASSERT(bm.width() == kExpectedWidth);
576 int pixelErrors = 0;
577 for (int y = 0; y < bm.height(); ++y) {
578 for (int x = 0; x < bm.width(); ++x) {
579 if (*correctPixels != bm.getColor(x, y)) {
580 ++pixelErrors;
581 }
582 ++correctPixels;
583 }
584 }
585 if (pixelErrors != 0) {
586 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
587 "[sampleSize=%d dither=%s colorType=%s %s]",
588 pixelErrors, kExpectedHeight * kExpectedWidth,
589 opts.fSampleSize, yn(opts.fDitherImage),
590 options_colorType(opts), path.c_str());
591 }
592 }
593 }
594
595 /**
596 * SkDecodingImageGenerator has an Options struct which lets the
597 * client of the generator set sample size, dithering, and bitmap
598 * config. This test loops through many possible options and tries
599 * them on a set of 5 small encoded images (each in a different
600 * format). We test both SkData and SkStreamRewindable decoding.
601 */
DEF_TEST(ImageDecoderOptions,reporter)602 DEF_TEST(ImageDecoderOptions, reporter) {
603 const char* files[] = {
604 "randPixels.bmp",
605 "randPixels.jpg",
606 "randPixels.png",
607 "randPixels.webp",
608 #if !defined(SK_BUILD_FOR_WIN)
609 // TODO(halcanary): Find out why this fails sometimes.
610 "randPixels.gif",
611 #endif
612 };
613
614 SkString resourceDir = GetResourcePath();
615 if (!sk_exists(resourceDir.c_str())) {
616 return;
617 }
618
619 int scaleList[] = {1, 2, 3, 4};
620 bool ditherList[] = {true, false};
621 SkColorType colorList[] = {
622 kAlpha_8_SkColorType,
623 kRGB_565_SkColorType,
624 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
625 kN32_SkColorType
626 // Note that indexed color is left out of the list. Lazy
627 // decoding doesn't do indexed color.
628 };
629 const bool useDataList[] = {true, false};
630
631 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
632 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
633 if (!sk_exists(path.c_str())) {
634 continue;
635 }
636
637 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
638 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
639 SkAutoTDelete<SkStreamRewindable> encodedStream(
640 SkStream::NewFromFile(path.c_str()));
641 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
642
643 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
644 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
645 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
646 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
647 SkDecodingImageGenerator::Options opts(scaleList[i],
648 ditherList[j],
649 colorList[k]);
650 test_options(reporter, opts, encodedStream, encodedData,
651 useDataList[m], path);
652
653 }
654 SkDecodingImageGenerator::Options options(scaleList[i],
655 ditherList[j]);
656 test_options(reporter, options, encodedStream, encodedData,
657 useDataList[m], path);
658 }
659 }
660 }
661 }
662 }
663
DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck,r)664 DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) {
665 SkString resourceDir = GetResourcePath();
666 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
667 if (!sk_exists(path.c_str())) {
668 return;
669 }
670 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
671 SkBitmap bitmap;
672 if (!SkInstallDiscardablePixelRef(
673 SkDecodingImageGenerator::Create(
674 encoded, SkDecodingImageGenerator::Options()), &bitmap)) {
675 #ifndef SK_BUILD_FOR_WIN
676 ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed.");
677 #endif
678 return;
679 }
680 if (kIndex_8_SkColorType != bitmap.colorType()) {
681 return;
682 }
683 {
684 SkAutoLockPixels alp(bitmap);
685 REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass");
686 }
687 {
688 SkAutoLockPixels alp(bitmap);
689 REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass");
690 }
691 }
692
693
694 ////////////////////////////////////////////////////////////////////////////////
695 namespace {
696 class SingleAllocator : public SkBitmap::Allocator {
697 public:
SingleAllocator(void * p,size_t s)698 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
~SingleAllocator()699 ~SingleAllocator() {}
700 // If the pixels in fPixels are big enough, use them.
allocPixelRef(SkBitmap * bm,SkColorTable * ct)701 bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) override {
702 SkASSERT(bm);
703 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
704 bm->setPixels(fPixels, ct);
705 fPixels = NULL;
706 fSize = 0;
707 return true;
708 }
709 return bm->tryAllocPixels(NULL, ct);
710 }
ready()711 bool ready() { return fPixels != NULL; }
712 private:
713 void* fPixels;
714 size_t fSize;
715 };
716 } // namespace
717
718 /* This tests for a bug in libjpeg where INT32 is typedefed to long
719 and memory can be written to outside of the array. */
DEF_TEST(ImageDecoding_JpegOverwrite,r)720 DEF_TEST(ImageDecoding_JpegOverwrite, r) {
721 SkString resourceDir = GetResourcePath();
722 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
723 SkAutoTDelete<SkStreamAsset> stream(
724 SkStream::NewFromFile(path.c_str()));
725 if (!stream.get()) {
726 SkDebugf("\nPath '%s' missing.\n", path.c_str());
727 return;
728 }
729 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
730 if (NULL == decoder.get()) {
731 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
732 return;
733 }
734 SkAssertResult(stream->rewind());
735
736 static const uint16_t sentinal = 0xBEEF;
737 static const int pixelCount = 16;
738 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
739 // pixels.get() should be 4-byte aligned.
740 // This is necessary to reproduce the bug.
741
742 pixels[pixelCount] = sentinal; // This value should not be changed.
743
744 SkAutoTUnref<SingleAllocator> allocator(
745 SkNEW_ARGS(SingleAllocator,
746 ((void*)pixels.get(), sizeof(uint16_t) * pixelCount)));
747 decoder->setAllocator(allocator);
748 decoder->setSampleSize(2);
749 SkBitmap bitmap;
750 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
751 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
752 REPORTER_ASSERT(r, success);
753 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
754 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
755 }
756