blob: 16f602b605e25d155c290a250661499525bc82a0 [file] [log] [blame]
scroggo@google.com2bbc2c92013-06-14 15:33:20 +00001/*
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 "SkBitmap.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +00009#include "SkCanvas.h"
10#include "SkColor.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000011#include "SkColorPriv.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000012#include "SkData.h"
halcanary@google.comdedd44a2013-12-20 16:35:22 +000013#include "SkDecodingImageGenerator.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000014#include "SkDiscardableMemoryPool.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000015#include "SkForceLinking.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000016#include "SkGradientShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000017#include "SkImageDecoder.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000018#include "SkImageEncoder.h"
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +000019#include "SkImageGeneratorPriv.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000020#include "SkImagePriv.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000021#include "SkOSFile.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000022#include "SkPoint.h"
23#include "SkShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000024#include "SkStream.h"
25#include "SkString.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000026#include "Test.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000027
28__SK_FORCE_IMAGE_DECODER_LINKING;
29
30/**
31 * Interprets c as an unpremultiplied color, and returns the
32 * premultiplied equivalent.
33 */
34static SkPMColor premultiply_unpmcolor(SkPMColor c) {
35 U8CPU a = SkGetPackedA32(c);
36 U8CPU r = SkGetPackedR32(c);
37 U8CPU g = SkGetPackedG32(c);
38 U8CPU b = SkGetPackedB32(c);
39 return SkPreMultiplyARGB(a, r, g, b);
40}
41
42/**
43 * Return true if this stream format should be skipped, due
44 * to do being an opaque format or not a valid format.
45 */
46static bool skip_image_format(SkImageDecoder::Format format) {
47 switch (format) {
48 case SkImageDecoder::kPNG_Format:
49 case SkImageDecoder::kWEBP_Format:
50 return false;
51 // Skip unknown since it will not be decoded anyway.
52 case SkImageDecoder::kUnknown_Format:
53 // Technically ICO and BMP supports alpha channels, but our image
54 // decoders do not, so skip them as well.
55 case SkImageDecoder::kICO_Format:
56 case SkImageDecoder::kBMP_Format:
krajcevski99ffe242014-06-03 13:04:35 -070057 // KTX is a Texture format so it's not particularly clear how to
58 // decode the alpha from it.
59 case SkImageDecoder::kKTX_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000060 // The rest of these are opaque.
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000061 case SkImageDecoder::kPKM_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000062 case SkImageDecoder::kWBMP_Format:
63 case SkImageDecoder::kGIF_Format:
64 case SkImageDecoder::kJPEG_Format:
65 return true;
66 }
67 SkASSERT(false);
68 return true;
69}
70
71/**
72 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
73 * them.
74 */
75static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
76 // Decode a resource:
77 SkBitmap bm8888;
78 SkBitmap bm8888Unpremul;
79
80 SkFILEStream stream(filename.c_str());
81
82 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
83 if (skip_image_format(format)) {
84 return;
85 }
86
87 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
88 if (NULL == decoder.get()) {
89 SkDebugf("couldn't decode %s\n", filename.c_str());
90 return;
91 }
92
93 bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config,
94 SkImageDecoder::kDecodePixels_Mode);
95 if (!success) {
96 return;
97 }
98
99 success = stream.rewind();
100 REPORTER_ASSERT(reporter, success);
101 if (!success) {
102 return;
103 }
104
105 decoder->setRequireUnpremultipliedColors(true);
106 success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config,
107 SkImageDecoder::kDecodePixels_Mode);
108 if (!success) {
109 return;
110 }
111
112 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
113 && bm8888.height() == bm8888Unpremul.height();
114 REPORTER_ASSERT(reporter, dimensionsMatch);
115 if (!dimensionsMatch) {
116 return;
117 }
118
119 // Only do the comparison if the two bitmaps are both 8888.
120 if (bm8888.config() != SkBitmap::kARGB_8888_Config
121 || bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) {
122 return;
123 }
124
125 // Now compare the two bitmaps.
126 for (int i = 0; i < bm8888.width(); ++i) {
127 for (int j = 0; j < bm8888.height(); ++j) {
128 // "c0" is the color of the premultiplied bitmap at (i, j).
129 const SkPMColor c0 = *bm8888.getAddr32(i, j);
130 // "c1" is the result of premultiplying the color of the unpremultiplied
131 // bitmap at (i, j).
132 const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
133 // Compute the difference for each component.
134 int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
135 int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
136 int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
137 int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
138
139 // Alpha component must be exactly the same.
140 REPORTER_ASSERT(reporter, 0 == da);
scroggo@google.comdaaea2d2013-06-14 20:39:48 +0000141
142 // Color components may not match exactly due to rounding error.
143 REPORTER_ASSERT(reporter, dr <= 1);
144 REPORTER_ASSERT(reporter, dg <= 1);
145 REPORTER_ASSERT(reporter, db <= 1);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000146 }
147 }
148}
149
scroggo@google.comf698c822013-07-18 19:34:49 +0000150static void test_unpremul(skiatest::Reporter* reporter) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000151 // This test cannot run if there is no resource path.
152 SkString resourcePath = skiatest::Test::GetResourcePath();
153 if (resourcePath.isEmpty()) {
bungeman@google.com3c996f82013-10-24 21:39:35 +0000154 SkDebugf("Could not run unpremul test because resourcePath not specified.");
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000155 return;
156 }
157 SkOSFile::Iter iter(resourcePath.c_str());
158 SkString basename;
159 if (iter.next(&basename)) {
160 do {
161 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000162 // SkDebugf("about to decode \"%s\"\n", filename.c_str());
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000163 compare_unpremul(reporter, filename);
164 } while (iter.next(&basename));
165 } else {
166 SkDebugf("Failed to find any files :(\n");
167 }
168}
169
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000170#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
171// Test that the alpha type is what we expect.
172static void test_alphaType(skiatest::Reporter* reporter, const SkString& filename,
173 bool requireUnpremul) {
174 SkBitmap bm;
175 SkFILEStream stream(filename.c_str());
176
177 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
178 if (NULL == decoder.get()) {
179 return;
180 }
181
182 decoder->setRequireUnpremultipliedColors(requireUnpremul);
183
184 // Decode just the bounds. This should always succeed.
185 bool success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
186 SkImageDecoder::kDecodeBounds_Mode);
187 REPORTER_ASSERT(reporter, success);
188 if (!success) {
189 return;
190 }
191
192 // Keep track of the alpha type for testing later. If the full decode
193 // succeeds, the alpha type should be the same, unless the full decode
194 // determined that the alpha type should actually be opaque, which may
195 // not be known when only decoding the bounds.
196 const SkAlphaType boundsAlphaType = bm.alphaType();
197
198 // rewind should always succeed on SkFILEStream.
199 success = stream.rewind();
200 REPORTER_ASSERT(reporter, success);
201 if (!success) {
202 return;
203 }
204
205 success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
206 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 } else {
223 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
224 || kOpaque_SkAlphaType == boundsAlphaType);
225 }
226
227 // When decoding the full image, the alpha type should match the one
228 // returned by the bounds decode, unless the full decode determined that
229 // the alpha type is actually opaque.
230 REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
231 || bm.alphaType() == kOpaque_SkAlphaType);
232}
233
234DEF_TEST(ImageDecoding_alphaType, reporter) {
235 SkString resourcePath = skiatest::Test::GetResourcePath();
236 if (resourcePath.isEmpty()) {
237 SkDebugf("Could not run alphaType test because resourcePath not specified.");
238 return;
239 }
240
241 SkOSFile::Iter iter(resourcePath.c_str());
242 SkString basename;
243 if (iter.next(&basename)) {
244 do {
245 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
246 for (int truth = 0; truth <= 1; ++truth) {
247 test_alphaType(reporter, filename, SkToBool(truth));
248 }
249 } while (iter.next(&basename));
250 } else {
251 SkDebugf("Failed to find any files :(\n");
252 }
253
254}
255
256// Using known images, test that decoding into unpremul and premul behave as expected.
257DEF_TEST(ImageDecoding_unpremul, reporter) {
258 SkString resourcePath = skiatest::Test::GetResourcePath();
259 if (resourcePath.isEmpty()) {
260 SkDebugf("Could not run unpremul test because resourcePath not specified.");
261 return;
262 }
263 const char* root = "half-transparent-white-pixel";
264 const char* suffixes[] = { ".png", ".webp" };
265
266 for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
267 SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
268 SkString fullName = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
269
270 SkBitmap bm;
271 SkFILEStream stream(fullName.c_str());
272
273 if (!stream.isValid()) {
274 SkDebugf("file %s missing from resource directoy %s\n",
275 basename.c_str(), resourcePath.c_str());
276 continue;
277 }
278
279 // This should never fail since we know the images we're decoding.
280 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
281 REPORTER_ASSERT(reporter, NULL != decoder.get());
282 if (NULL == decoder.get()) {
283 continue;
284 }
285
286 // Test unpremultiplied. We know what color this should result in.
287 decoder->setRequireUnpremultipliedColors(true);
288 bool success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
289 SkImageDecoder::kDecodePixels_Mode);
290 REPORTER_ASSERT(reporter, success);
291 if (!success) {
292 continue;
293 }
294
295 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
296 {
297 SkAutoLockPixels alp(bm);
298 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
299 }
300
301 success = stream.rewind();
302 REPORTER_ASSERT(reporter, success);
303 if (!success) {
304 continue;
305 }
306
307 // Test premultiplied. Once again, we know which color this should
308 // result in.
309 decoder->setRequireUnpremultipliedColors(false);
310 success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
311 SkImageDecoder::kDecodePixels_Mode);
312 REPORTER_ASSERT(reporter, success);
313 if (!success) {
314 continue;
315 }
316
317 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
318 {
319 SkAutoLockPixels alp(bm);
320 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
321 }
322 }
323}
324#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
325
scroggo@google.com826d63a2013-07-18 20:06:28 +0000326#ifdef SK_DEBUG
327// Create a stream containing a bitmap encoded to Type type.
scroggo@google.comb5571b32013-09-25 21:34:24 +0000328static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000329 SkBitmap bm;
330 const int size = 50;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000331 bm.allocN32Pixels(size, size);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000332 SkCanvas canvas(bm);
333 SkPoint points[2] = {
334 { SkIntToScalar(0), SkIntToScalar(0) },
335 { SkIntToScalar(size), SkIntToScalar(size) }
336 };
337 SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
338 SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
339 SK_ARRAY_COUNT(colors),
340 SkShader::kClamp_TileMode);
341 SkPaint paint;
342 paint.setShader(shader)->unref();
343 canvas.drawPaint(paint);
344 // Now encode it to a stream.
345 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
346 if (NULL == data.get()) {
347 return NULL;
348 }
349 return SkNEW_ARGS(SkMemoryStream, (data.get()));
350}
351
352// For every format that supports tile based decoding, ensure that
353// calling decodeSubset will not fail if the caller has unreffed the
354// stream provided in buildTileIndex.
355// Only runs in debug mode since we are testing for a crash.
356static void test_stream_life() {
357 const SkImageEncoder::Type gTypes[] = {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000358#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comd79277f2013-08-07 19:53:53 +0000359 SkImageEncoder::kJPEG_Type,
scroggo@google.com826d63a2013-07-18 20:06:28 +0000360 SkImageEncoder::kPNG_Type,
361#endif
362 SkImageEncoder::kWEBP_Type,
363 };
364 for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000365 // SkDebugf("encoding to %i\n", i);
scroggo@google.comb5571b32013-09-25 21:34:24 +0000366 SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
scroggo@google.com826d63a2013-07-18 20:06:28 +0000367 if (NULL == stream.get()) {
368 SkDebugf("no stream\n");
369 continue;
370 }
371 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
372 if (NULL == decoder.get()) {
373 SkDebugf("no decoder\n");
374 continue;
375 }
376 int width, height;
377 if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
378 SkDebugf("could not build a tile index\n");
379 continue;
380 }
381 // Now unref the stream to make sure it survives
382 stream.reset(NULL);
383 SkBitmap bm;
384 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height),
385 SkBitmap::kARGB_8888_Config);
386 }
387}
scroggo@google.com8d239242013-10-01 17:27:15 +0000388
389// Test inside SkScaledBitmapSampler.cpp
390extern void test_row_proc_choice();
391
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000392#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000393
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000394DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000395 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000396#ifdef SK_DEBUG
397 test_stream_life();
scroggo@google.com8d239242013-10-01 17:27:15 +0000398 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000399#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000400}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000401
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000402// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000403static const int kExpectedWidth = 8;
404static const int kExpectedHeight = 8;
405static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000406 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
407 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
408 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
409 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
410 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
411 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
412 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
413 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
414 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
415 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
416 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
417 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
418 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
419 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
420 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
421 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
422};
423SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
424 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000425
426DEF_TEST(WebP, reporter) {
427 const unsigned char encodedWebP[] = {
428 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
429 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
430 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
431 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
432 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
433 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
434 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
435 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
436 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
437 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
438 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
439 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
440 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
441 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
442 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
443 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
444 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
445 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
446 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
447 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
448 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
449 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
450 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
451 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
452 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
453 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
454 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000455 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
456 sizeof(encodedWebP)));
457 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000458
459 bool success = SkInstallDiscardablePixelRef(
460 SkDecodingImageGenerator::Create(encoded,
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000461 SkDecodingImageGenerator::Options()), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000462
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000463 REPORTER_ASSERT(reporter, success);
464 if (!success) {
465 return;
466 }
467 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000468
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000469 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000470 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000471 REPORTER_ASSERT(reporter, rightSize);
472 if (rightSize) {
473 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000474 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000475 for (int y = 0; y < bm.height(); ++y) {
476 for (int x = 0; x < bm.width(); ++x) {
477 error |= (*correctPixel != bm.getColor(x, y));
478 ++correctPixel;
479 }
480 }
481 REPORTER_ASSERT(reporter, !error);
482 }
483}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000484
485////////////////////////////////////////////////////////////////////////////////
486
487// example of how Android will do this inside their BitmapFactory
488static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
489 SkStreamRewindable* stream,
490 int sampleSize, bool ditherImage) {
491 SkASSERT(bitmap != NULL);
492 SkASSERT(stream != NULL);
493 SkASSERT(stream->rewind());
494 SkASSERT(stream->unique());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000495 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000496 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000497 if (SkInstallDiscardablePixelRef(
498 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000499 return bitmap->pixelRef();
500 }
501 return NULL;
502}
503/**
504 * A test for the SkDecodingImageGenerator::Create and
505 * SkInstallDiscardablePixelRef functions.
506 */
507DEF_TEST(ImprovedBitmapFactory, reporter) {
508 SkString resourcePath = skiatest::Test::GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000509 SkString path = SkOSPath::SkPathJoin(
510 resourcePath.c_str(), "randPixels.png");
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000511 SkAutoTUnref<SkStreamRewindable> stream(
512 SkStream::NewFromFile(path.c_str()));
513 if (sk_exists(path.c_str())) {
514 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000515 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000516 REPORTER_ASSERT(reporter,
517 NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
518 SkAutoLockPixels alp(bm);
519 REPORTER_ASSERT(reporter, NULL != bm.getPixels());
520 }
521}
522
523
524////////////////////////////////////////////////////////////////////////////////
525
526#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
527static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000528 // returns true if the value is greater than floor(dividend/divisor)
529 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000530 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000531 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000532}
533#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
534
535
536#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
537 #define kBackwards_SkColorType kRGBA_8888_SkColorType
538#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
539 #define kBackwards_SkColorType kBGRA_8888_SkColorType
540#else
541 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
542#endif
543
544static inline const char* SkColorType_to_string(SkColorType colorType) {
545 switch(colorType) {
546 case kAlpha_8_SkColorType: return "Alpha_8";
547 case kRGB_565_SkColorType: return "RGB_565";
548 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000549 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000550 case kBackwards_SkColorType: return "Backwards";
551 case kIndex_8_SkColorType: return "Index_8";
552 default: return "ERROR";
553 }
554}
555
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000556static inline const char* options_colorType(
557 const SkDecodingImageGenerator::Options& opts) {
558 if (opts.fUseRequestedColorType) {
559 return SkColorType_to_string(opts.fRequestedColorType);
560 } else {
561 return "(none)";
562 }
563}
564
565static inline const char* yn(bool value) {
566 if (value) {
567 return "yes";
568 } else {
569 return "no";
570 }
571}
572
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000573/**
574 * Given either a SkStream or a SkData, try to decode the encoded
575 * image using the specified options and report errors.
576 */
577static void test_options(skiatest::Reporter* reporter,
578 const SkDecodingImageGenerator::Options& opts,
579 SkStreamRewindable* encodedStream,
580 SkData* encodedData,
581 bool useData,
582 const SkString& path) {
583 SkBitmap bm;
584 bool success = false;
585 if (useData) {
586 if (NULL == encodedData) {
587 return;
588 }
589 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000590 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000591 } else {
592 if (NULL == encodedStream) {
593 return;
594 }
595 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000596 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000597 }
598 if (!success) {
599 if (opts.fUseRequestedColorType
600 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
601 return; // Ignore known conversion inabilities.
602 }
603 // If we get here, it's a failure and we will need more
604 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000605 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
606 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
607 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000608 return;
609 }
610 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
611 // Android is the only system that use Skia's image decoders in
612 // production. For now, we'll only verify that samplesize works
613 // on systems where it already is known to work.
614 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
615 opts.fSampleSize));
616 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
617 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000618 // The ImageDecoder API doesn't guarantee that SampleSize does
619 // anything at all, but the decoders that this test excercises all
620 // produce an output size in the following range:
621 // (((sample_size * out_size) > (in_size - sample_size))
622 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000623 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
624 SkAutoLockPixels alp(bm);
625 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000626 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
627 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
628 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000629 return;
630 }
631
632 SkBitmap::Config requestedConfig
633 = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
634 REPORTER_ASSERT(reporter,
635 (!opts.fUseRequestedColorType)
636 || (bm.config() == requestedConfig));
637
638 // Condition under which we should check the decoding results:
639 if ((SkBitmap::kARGB_8888_Config == bm.config())
640 && (!path.endsWith(".jpg")) // lossy
641 && (opts.fSampleSize == 1)) { // scaled
642 const SkColor* correctPixels = kExpectedPixels;
643 SkASSERT(bm.height() == kExpectedHeight);
644 SkASSERT(bm.width() == kExpectedWidth);
645 int pixelErrors = 0;
646 for (int y = 0; y < bm.height(); ++y) {
647 for (int x = 0; x < bm.width(); ++x) {
648 if (*correctPixels != bm.getColor(x, y)) {
649 ++pixelErrors;
650 }
651 ++correctPixels;
652 }
653 }
654 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000655 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
656 "[sampleSize=%d dither=%s colorType=%s %s]",
657 pixelErrors, kExpectedHeight * kExpectedWidth,
658 opts.fSampleSize, yn(opts.fDitherImage),
659 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000660 }
661 }
662}
663
664/**
665 * SkDecodingImageGenerator has an Options struct which lets the
666 * client of the generator set sample size, dithering, and bitmap
667 * config. This test loops through many possible options and tries
668 * them on a set of 5 small encoded images (each in a different
669 * format). We test both SkData and SkStreamRewindable decoding.
670 */
671DEF_TEST(ImageDecoderOptions, reporter) {
672 const char* files[] = {
673 "randPixels.bmp",
674 "randPixels.jpg",
675 "randPixels.png",
676 "randPixels.webp",
677 #if !defined(SK_BUILD_FOR_WIN)
678 // TODO(halcanary): Find out why this fails sometimes.
679 "randPixels.gif",
680 #endif
681 };
682
683 SkString resourceDir = skiatest::Test::GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000684 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000685 return;
686 }
687
688 int scaleList[] = {1, 2, 3, 4};
689 bool ditherList[] = {true, false};
690 SkColorType colorList[] = {
691 kAlpha_8_SkColorType,
692 kRGB_565_SkColorType,
693 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000694 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000695 // Note that indexed color is left out of the list. Lazy
696 // decoding doesn't do indexed color.
697 };
698 const bool useDataList[] = {true, false};
699
700 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000701 SkString path = SkOSPath::SkPathJoin(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000702 if (!sk_exists(path.c_str())) {
703 continue;
704 }
705
706 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
707 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
708 SkAutoTUnref<SkStreamRewindable> encodedStream(
709 SkStream::NewFromFile(path.c_str()));
710 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
711
712 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
713 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
714 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
715 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
716 SkDecodingImageGenerator::Options opts(scaleList[i],
717 ditherList[j],
718 colorList[k]);
719 test_options(reporter, opts, encodedStream, encodedData,
720 useDataList[m], path);
721
722 }
723 SkDecodingImageGenerator::Options options(scaleList[i],
724 ditherList[j]);
725 test_options(reporter, options, encodedStream, encodedData,
726 useDataList[m], path);
727 }
728 }
729 }
730 }
731}