blob: 89db3990e4b8703b6e1ece595c68ce425cec72b2 [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
tfarinabcbc1782014-06-18 14:32:48 -07008#include "Resources.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +00009#include "SkBitmap.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000010#include "SkCanvas.h"
11#include "SkColor.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000012#include "SkColorPriv.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000013#include "SkData.h"
halcanary@google.comdedd44a2013-12-20 16:35:22 +000014#include "SkDecodingImageGenerator.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000015#include "SkDiscardableMemoryPool.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000016#include "SkForceLinking.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000017#include "SkGradientShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000018#include "SkImageDecoder.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000019#include "SkImageEncoder.h"
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +000020#include "SkImageGeneratorPriv.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000021#include "SkImagePriv.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000022#include "SkOSFile.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000023#include "SkPoint.h"
24#include "SkShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000025#include "SkStream.h"
26#include "SkString.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000027#include "Test.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000028
29__SK_FORCE_IMAGE_DECODER_LINKING;
30
31/**
32 * Interprets c as an unpremultiplied color, and returns the
33 * premultiplied equivalent.
34 */
35static 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 */
47static 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:
krajcevski99ffe242014-06-03 13:04:35 -070058 // KTX is a Texture format so it's not particularly clear how to
59 // decode the alpha from it.
60 case SkImageDecoder::kKTX_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000061 // The rest of these are opaque.
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000062 case SkImageDecoder::kPKM_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000063 case SkImageDecoder::kWBMP_Format:
64 case SkImageDecoder::kGIF_Format:
65 case SkImageDecoder::kJPEG_Format:
66 return true;
67 }
68 SkASSERT(false);
69 return true;
70}
71
72/**
73 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
74 * them.
75 */
76static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
77 // Decode a resource:
78 SkBitmap bm8888;
79 SkBitmap bm8888Unpremul;
80
81 SkFILEStream stream(filename.c_str());
82
83 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
84 if (skip_image_format(format)) {
85 return;
86 }
87
88 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
89 if (NULL == decoder.get()) {
90 SkDebugf("couldn't decode %s\n", filename.c_str());
91 return;
92 }
93
reedbfefc7c2014-06-12 17:40:00 -070094 bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000095 SkImageDecoder::kDecodePixels_Mode);
96 if (!success) {
97 return;
98 }
99
100 success = stream.rewind();
101 REPORTER_ASSERT(reporter, success);
102 if (!success) {
103 return;
104 }
105
106 decoder->setRequireUnpremultipliedColors(true);
reedbfefc7c2014-06-12 17:40:00 -0700107 success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType,
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000108 SkImageDecoder::kDecodePixels_Mode);
109 if (!success) {
110 return;
111 }
112
113 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
114 && bm8888.height() == bm8888Unpremul.height();
115 REPORTER_ASSERT(reporter, dimensionsMatch);
116 if (!dimensionsMatch) {
117 return;
118 }
119
120 // Only do the comparison if the two bitmaps are both 8888.
reedbfefc7c2014-06-12 17:40:00 -0700121 if (bm8888.colorType() != kN32_SkColorType || bm8888Unpremul.colorType() != kN32_SkColorType) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000122 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.
tfarinabcbc1782014-06-18 14:32:48 -0700152 SkString resourcePath = GetResourcePath();
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000153 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.
reedbfefc7c2014-06-12 17:40:00 -0700185 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000186 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
reedbfefc7c2014-06-12 17:40:00 -0700205 success = decoder->decode(&stream, &bm, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode);
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000206
207 if (!success) {
208 // When the decoder is set to require unpremul, if it does not support
209 // unpremul it will fail. This is the only reason the decode should
210 // fail (since we know the files we are using to test can be decoded).
211 REPORTER_ASSERT(reporter, requireUnpremul);
212 return;
213 }
214
215 // The bounds decode should return with either the requested
216 // premul/unpremul or opaque, if that value could be determined when only
217 // decoding the bounds.
218 if (requireUnpremul) {
219 REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType
220 || kOpaque_SkAlphaType == boundsAlphaType);
221 } else {
222 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
223 || kOpaque_SkAlphaType == boundsAlphaType);
224 }
225
226 // When decoding the full image, the alpha type should match the one
227 // returned by the bounds decode, unless the full decode determined that
228 // the alpha type is actually opaque.
229 REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
230 || bm.alphaType() == kOpaque_SkAlphaType);
231}
232
233DEF_TEST(ImageDecoding_alphaType, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700234 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000235 if (resourcePath.isEmpty()) {
236 SkDebugf("Could not run alphaType test because resourcePath not specified.");
237 return;
238 }
239
240 SkOSFile::Iter iter(resourcePath.c_str());
241 SkString basename;
242 if (iter.next(&basename)) {
243 do {
244 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
245 for (int truth = 0; truth <= 1; ++truth) {
246 test_alphaType(reporter, filename, SkToBool(truth));
247 }
248 } while (iter.next(&basename));
249 } else {
250 SkDebugf("Failed to find any files :(\n");
251 }
252
253}
254
255// Using known images, test that decoding into unpremul and premul behave as expected.
256DEF_TEST(ImageDecoding_unpremul, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700257 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000258 if (resourcePath.isEmpty()) {
259 SkDebugf("Could not run unpremul test because resourcePath not specified.");
260 return;
261 }
262 const char* root = "half-transparent-white-pixel";
263 const char* suffixes[] = { ".png", ".webp" };
264
265 for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
266 SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
267 SkString fullName = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
268
269 SkBitmap bm;
270 SkFILEStream stream(fullName.c_str());
271
272 if (!stream.isValid()) {
273 SkDebugf("file %s missing from resource directoy %s\n",
274 basename.c_str(), resourcePath.c_str());
275 continue;
276 }
277
278 // This should never fail since we know the images we're decoding.
279 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
280 REPORTER_ASSERT(reporter, NULL != decoder.get());
281 if (NULL == decoder.get()) {
282 continue;
283 }
284
285 // Test unpremultiplied. We know what color this should result in.
286 decoder->setRequireUnpremultipliedColors(true);
reedbfefc7c2014-06-12 17:40:00 -0700287 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000288 SkImageDecoder::kDecodePixels_Mode);
289 REPORTER_ASSERT(reporter, success);
290 if (!success) {
291 continue;
292 }
293
294 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
295 {
296 SkAutoLockPixels alp(bm);
297 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
298 }
299
300 success = stream.rewind();
301 REPORTER_ASSERT(reporter, success);
302 if (!success) {
303 continue;
304 }
305
306 // Test premultiplied. Once again, we know which color this should
307 // result in.
308 decoder->setRequireUnpremultipliedColors(false);
reedbfefc7c2014-06-12 17:40:00 -0700309 success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000310 SkImageDecoder::kDecodePixels_Mode);
311 REPORTER_ASSERT(reporter, success);
312 if (!success) {
313 continue;
314 }
315
316 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
317 {
318 SkAutoLockPixels alp(bm);
319 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
320 }
321 }
322}
323#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
324
scroggo@google.com826d63a2013-07-18 20:06:28 +0000325#ifdef SK_DEBUG
326// Create a stream containing a bitmap encoded to Type type.
scroggo@google.comb5571b32013-09-25 21:34:24 +0000327static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000328 SkBitmap bm;
329 const int size = 50;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000330 bm.allocN32Pixels(size, size);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000331 SkCanvas canvas(bm);
332 SkPoint points[2] = {
333 { SkIntToScalar(0), SkIntToScalar(0) },
334 { SkIntToScalar(size), SkIntToScalar(size) }
335 };
336 SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
337 SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
338 SK_ARRAY_COUNT(colors),
339 SkShader::kClamp_TileMode);
340 SkPaint paint;
341 paint.setShader(shader)->unref();
342 canvas.drawPaint(paint);
343 // Now encode it to a stream.
344 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
345 if (NULL == data.get()) {
346 return NULL;
347 }
348 return SkNEW_ARGS(SkMemoryStream, (data.get()));
349}
350
351// For every format that supports tile based decoding, ensure that
352// calling decodeSubset will not fail if the caller has unreffed the
353// stream provided in buildTileIndex.
354// Only runs in debug mode since we are testing for a crash.
355static void test_stream_life() {
356 const SkImageEncoder::Type gTypes[] = {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000357#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comd79277f2013-08-07 19:53:53 +0000358 SkImageEncoder::kJPEG_Type,
scroggo@google.com826d63a2013-07-18 20:06:28 +0000359 SkImageEncoder::kPNG_Type,
360#endif
361 SkImageEncoder::kWEBP_Type,
362 };
363 for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000364 // SkDebugf("encoding to %i\n", i);
scroggo@google.comb5571b32013-09-25 21:34:24 +0000365 SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
scroggo@google.com826d63a2013-07-18 20:06:28 +0000366 if (NULL == stream.get()) {
367 SkDebugf("no stream\n");
368 continue;
369 }
370 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
371 if (NULL == decoder.get()) {
372 SkDebugf("no decoder\n");
373 continue;
374 }
375 int width, height;
376 if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
377 SkDebugf("could not build a tile index\n");
378 continue;
379 }
380 // Now unref the stream to make sure it survives
381 stream.reset(NULL);
382 SkBitmap bm;
reedbfefc7c2014-06-12 17:40:00 -0700383 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height), kN32_SkColorType);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000384 }
385}
scroggo@google.com8d239242013-10-01 17:27:15 +0000386
387// Test inside SkScaledBitmapSampler.cpp
388extern void test_row_proc_choice();
389
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000390#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000391
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000392DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000393 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000394#ifdef SK_DEBUG
395 test_stream_life();
scroggo@google.com8d239242013-10-01 17:27:15 +0000396 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000397#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000398}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000399
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000400// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000401static const int kExpectedWidth = 8;
402static const int kExpectedHeight = 8;
403static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000404 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
405 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
406 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
407 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
408 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
409 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
410 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
411 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
412 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
413 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
414 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
415 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
416 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
417 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
418 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
419 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
420};
421SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
422 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000423
424DEF_TEST(WebP, reporter) {
425 const unsigned char encodedWebP[] = {
426 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
427 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
428 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
429 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
430 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
431 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
432 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
433 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
434 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
435 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
436 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
437 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
438 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
439 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
440 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
441 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
442 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
443 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
444 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
445 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
446 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
447 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
448 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
449 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
450 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
451 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
452 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000453 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
454 sizeof(encodedWebP)));
455 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000456
457 bool success = SkInstallDiscardablePixelRef(
458 SkDecodingImageGenerator::Create(encoded,
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000459 SkDecodingImageGenerator::Options()), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000460
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000461 REPORTER_ASSERT(reporter, success);
462 if (!success) {
463 return;
464 }
465 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000466
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000467 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000468 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000469 REPORTER_ASSERT(reporter, rightSize);
470 if (rightSize) {
471 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000472 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000473 for (int y = 0; y < bm.height(); ++y) {
474 for (int x = 0; x < bm.width(); ++x) {
475 error |= (*correctPixel != bm.getColor(x, y));
476 ++correctPixel;
477 }
478 }
479 REPORTER_ASSERT(reporter, !error);
480 }
481}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000482
483////////////////////////////////////////////////////////////////////////////////
484
485// example of how Android will do this inside their BitmapFactory
486static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
487 SkStreamRewindable* stream,
488 int sampleSize, bool ditherImage) {
489 SkASSERT(bitmap != NULL);
490 SkASSERT(stream != NULL);
491 SkASSERT(stream->rewind());
492 SkASSERT(stream->unique());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000493 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000494 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000495 if (SkInstallDiscardablePixelRef(
496 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000497 return bitmap->pixelRef();
498 }
499 return NULL;
500}
501/**
502 * A test for the SkDecodingImageGenerator::Create and
503 * SkInstallDiscardablePixelRef functions.
504 */
505DEF_TEST(ImprovedBitmapFactory, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700506 SkString resourcePath = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000507 SkString path = SkOSPath::SkPathJoin(
508 resourcePath.c_str(), "randPixels.png");
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000509 SkAutoTUnref<SkStreamRewindable> stream(
510 SkStream::NewFromFile(path.c_str()));
511 if (sk_exists(path.c_str())) {
512 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000513 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000514 REPORTER_ASSERT(reporter,
515 NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
516 SkAutoLockPixels alp(bm);
517 REPORTER_ASSERT(reporter, NULL != bm.getPixels());
518 }
519}
520
521
522////////////////////////////////////////////////////////////////////////////////
523
524#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
525static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000526 // returns true if the value is greater than floor(dividend/divisor)
527 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000528 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000529 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000530}
531#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
532
533
534#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
535 #define kBackwards_SkColorType kRGBA_8888_SkColorType
536#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
537 #define kBackwards_SkColorType kBGRA_8888_SkColorType
538#else
539 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
540#endif
541
542static inline const char* SkColorType_to_string(SkColorType colorType) {
543 switch(colorType) {
544 case kAlpha_8_SkColorType: return "Alpha_8";
545 case kRGB_565_SkColorType: return "RGB_565";
546 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000547 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000548 case kBackwards_SkColorType: return "Backwards";
549 case kIndex_8_SkColorType: return "Index_8";
550 default: return "ERROR";
551 }
552}
553
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000554static inline const char* options_colorType(
555 const SkDecodingImageGenerator::Options& opts) {
556 if (opts.fUseRequestedColorType) {
557 return SkColorType_to_string(opts.fRequestedColorType);
558 } else {
559 return "(none)";
560 }
561}
562
563static inline const char* yn(bool value) {
564 if (value) {
565 return "yes";
566 } else {
567 return "no";
568 }
569}
570
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000571/**
572 * Given either a SkStream or a SkData, try to decode the encoded
573 * image using the specified options and report errors.
574 */
575static void test_options(skiatest::Reporter* reporter,
576 const SkDecodingImageGenerator::Options& opts,
577 SkStreamRewindable* encodedStream,
578 SkData* encodedData,
579 bool useData,
580 const SkString& path) {
581 SkBitmap bm;
582 bool success = false;
583 if (useData) {
584 if (NULL == encodedData) {
585 return;
586 }
587 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000588 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000589 } else {
590 if (NULL == encodedStream) {
591 return;
592 }
593 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000594 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000595 }
596 if (!success) {
597 if (opts.fUseRequestedColorType
598 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
599 return; // Ignore known conversion inabilities.
600 }
601 // If we get here, it's a failure and we will need more
602 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000603 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
604 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
605 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000606 return;
607 }
608 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
609 // Android is the only system that use Skia's image decoders in
610 // production. For now, we'll only verify that samplesize works
611 // on systems where it already is known to work.
612 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
613 opts.fSampleSize));
614 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
615 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000616 // The ImageDecoder API doesn't guarantee that SampleSize does
617 // anything at all, but the decoders that this test excercises all
618 // produce an output size in the following range:
619 // (((sample_size * out_size) > (in_size - sample_size))
620 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000621 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
622 SkAutoLockPixels alp(bm);
623 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000624 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
625 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
626 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000627 return;
628 }
629
reedbfefc7c2014-06-12 17:40:00 -0700630 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000631 REPORTER_ASSERT(reporter,
632 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700633 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000634
635 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700636 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000637 && (!path.endsWith(".jpg")) // lossy
638 && (opts.fSampleSize == 1)) { // scaled
639 const SkColor* correctPixels = kExpectedPixels;
640 SkASSERT(bm.height() == kExpectedHeight);
641 SkASSERT(bm.width() == kExpectedWidth);
642 int pixelErrors = 0;
643 for (int y = 0; y < bm.height(); ++y) {
644 for (int x = 0; x < bm.width(); ++x) {
645 if (*correctPixels != bm.getColor(x, y)) {
646 ++pixelErrors;
647 }
648 ++correctPixels;
649 }
650 }
651 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000652 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
653 "[sampleSize=%d dither=%s colorType=%s %s]",
654 pixelErrors, kExpectedHeight * kExpectedWidth,
655 opts.fSampleSize, yn(opts.fDitherImage),
656 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000657 }
658 }
659}
660
661/**
662 * SkDecodingImageGenerator has an Options struct which lets the
663 * client of the generator set sample size, dithering, and bitmap
664 * config. This test loops through many possible options and tries
665 * them on a set of 5 small encoded images (each in a different
666 * format). We test both SkData and SkStreamRewindable decoding.
667 */
668DEF_TEST(ImageDecoderOptions, reporter) {
669 const char* files[] = {
670 "randPixels.bmp",
671 "randPixels.jpg",
672 "randPixels.png",
673 "randPixels.webp",
674 #if !defined(SK_BUILD_FOR_WIN)
675 // TODO(halcanary): Find out why this fails sometimes.
676 "randPixels.gif",
677 #endif
678 };
679
tfarinabcbc1782014-06-18 14:32:48 -0700680 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000681 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000682 return;
683 }
684
685 int scaleList[] = {1, 2, 3, 4};
686 bool ditherList[] = {true, false};
687 SkColorType colorList[] = {
688 kAlpha_8_SkColorType,
689 kRGB_565_SkColorType,
690 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000691 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000692 // Note that indexed color is left out of the list. Lazy
693 // decoding doesn't do indexed color.
694 };
695 const bool useDataList[] = {true, false};
696
697 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000698 SkString path = SkOSPath::SkPathJoin(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000699 if (!sk_exists(path.c_str())) {
700 continue;
701 }
702
703 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
704 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
705 SkAutoTUnref<SkStreamRewindable> encodedStream(
706 SkStream::NewFromFile(path.c_str()));
707 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
708
709 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
710 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
711 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
712 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
713 SkDecodingImageGenerator::Options opts(scaleList[i],
714 ditherList[j],
715 colorList[k]);
716 test_options(reporter, opts, encodedStream, encodedData,
717 useDataList[m], path);
718
719 }
720 SkDecodingImageGenerator::Options options(scaleList[i],
721 ditherList[j]);
722 test_options(reporter, options, encodedStream, encodedData,
723 useDataList[m], path);
724 }
725 }
726 }
727 }
728}