blob: 762852d62a5e9189b808532199486d2c5cf250f7 [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:
krajcevski95b1b3d2014-08-07 12:58:38 -070058 // KTX and ASTC are texture formats so it's not particularly clear how to
59 // decode the alpha from them.
krajcevski99ffe242014-06-03 13:04:35 -070060 case SkImageDecoder::kKTX_Format:
krajcevski95b1b3d2014-08-07 12:58:38 -070061 case SkImageDecoder::kASTC_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000062 // The rest of these are opaque.
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000063 case SkImageDecoder::kPKM_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000064 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 */
77static 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
reedbfefc7c2014-06-12 17:40:00 -070095 bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType,
scroggo2a120802014-10-22 12:07:00 -070096 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000097 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);
reedbfefc7c2014-06-12 17:40:00 -0700108 success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700109 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000110 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.
reedbfefc7c2014-06-12 17:40:00 -0700122 if (bm8888.colorType() != kN32_SkColorType || bm8888Unpremul.colorType() != kN32_SkColorType) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000123 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);
scroggo@google.comdaaea2d2013-06-14 20:39:48 +0000142
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);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000147 }
148 }
149}
150
scroggo@google.comf698c822013-07-18 19:34:49 +0000151static void test_unpremul(skiatest::Reporter* reporter) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000152 // This test cannot run if there is no resource path.
tfarinabcbc1782014-06-18 14:32:48 -0700153 SkString resourcePath = GetResourcePath();
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000154 if (resourcePath.isEmpty()) {
bungeman@google.com3c996f82013-10-24 21:39:35 +0000155 SkDebugf("Could not run unpremul test because resourcePath not specified.");
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000156 return;
157 }
158 SkOSFile::Iter iter(resourcePath.c_str());
159 SkString basename;
160 if (iter.next(&basename)) {
161 do {
tfarinaa8e2e152014-07-28 19:26:58 -0700162 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000163 // SkDebugf("about to decode \"%s\"\n", filename.c_str());
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000164 compare_unpremul(reporter, filename);
165 } while (iter.next(&basename));
166 } else {
167 SkDebugf("Failed to find any files :(\n");
168 }
169}
170
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000171#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
172// Test that the alpha type is what we expect.
173static 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.
reedbfefc7c2014-06-12 17:40:00 -0700186 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000187 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
reedbfefc7c2014-06-12 17:40:00 -0700206 success = decoder->decode(&stream, &bm, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode);
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000207
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) {
tfarinabcbc1782014-06-18 14:32:48 -0700235 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000236 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 {
tfarinaa8e2e152014-07-28 19:26:58 -0700245 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000246 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) {
tfarinabcbc1782014-06-18 14:32:48 -0700258 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000259 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]);
tfarinaa8e2e152014-07-28 19:26:58 -0700268 SkString fullName = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000269
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));
bsalomon49f085d2014-09-05 13:34:00 -0700281 REPORTER_ASSERT(reporter, decoder.get());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000282 if (NULL == decoder.get()) {
283 continue;
284 }
285
286 // Test unpremultiplied. We know what color this should result in.
287 decoder->setRequireUnpremultipliedColors(true);
reedbfefc7c2014-06-12 17:40:00 -0700288 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000289 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);
reedbfefc7c2014-06-12 17:40:00 -0700310 success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000311 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;
reedbfefc7c2014-06-12 17:40:00 -0700384 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height), kN32_SkColorType);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000385 }
386}
scroggo@google.com8d239242013-10-01 17:27:15 +0000387
388// Test inside SkScaledBitmapSampler.cpp
389extern void test_row_proc_choice();
390
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000391#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000392
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000393DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000394 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000395#ifdef SK_DEBUG
396 test_stream_life();
scroggo@google.com8d239242013-10-01 17:27:15 +0000397 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000398#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000399}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000400
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000401// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000402static const int kExpectedWidth = 8;
403static const int kExpectedHeight = 8;
404static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000405 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
406 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
407 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
408 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
409 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
410 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
411 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
412 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
413 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
414 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
415 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
416 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
417 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
418 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
419 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
420 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
421};
422SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
423 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000424
425DEF_TEST(WebP, reporter) {
426 const unsigned char encodedWebP[] = {
427 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
428 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
429 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
430 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
431 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
432 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
433 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
434 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
435 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
436 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
437 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
438 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
439 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
440 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
441 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
442 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
443 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
444 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
445 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
446 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
447 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
448 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
449 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
450 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
451 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
452 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
453 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000454 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
455 sizeof(encodedWebP)));
456 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000457
458 bool success = SkInstallDiscardablePixelRef(
459 SkDecodingImageGenerator::Create(encoded,
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000460 SkDecodingImageGenerator::Options()), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000461
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000462 REPORTER_ASSERT(reporter, success);
463 if (!success) {
464 return;
465 }
466 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000467
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000468 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000469 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000470 REPORTER_ASSERT(reporter, rightSize);
471 if (rightSize) {
472 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000473 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000474 for (int y = 0; y < bm.height(); ++y) {
475 for (int x = 0; x < bm.width(); ++x) {
476 error |= (*correctPixel != bm.getColor(x, y));
477 ++correctPixel;
478 }
479 }
480 REPORTER_ASSERT(reporter, !error);
481 }
482}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000483
484////////////////////////////////////////////////////////////////////////////////
485
486// example of how Android will do this inside their BitmapFactory
487static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
488 SkStreamRewindable* stream,
489 int sampleSize, bool ditherImage) {
490 SkASSERT(bitmap != NULL);
491 SkASSERT(stream != NULL);
492 SkASSERT(stream->rewind());
493 SkASSERT(stream->unique());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000494 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000495 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000496 if (SkInstallDiscardablePixelRef(
497 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000498 return bitmap->pixelRef();
499 }
500 return NULL;
501}
502/**
503 * A test for the SkDecodingImageGenerator::Create and
504 * SkInstallDiscardablePixelRef functions.
505 */
506DEF_TEST(ImprovedBitmapFactory, reporter) {
tfarinac846f4a2014-07-01 12:35:49 -0700507 SkString pngFilename = GetResourcePath("randPixels.png");
508 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
509 if (sk_exists(pngFilename.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000510 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000511 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000512 REPORTER_ASSERT(reporter,
bsalomon49f085d2014-09-05 13:34:00 -0700513 install_pixel_ref(&bm, stream.detach(), 1, true));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000514 SkAutoLockPixels alp(bm);
bsalomon49f085d2014-09-05 13:34:00 -0700515 REPORTER_ASSERT(reporter, bm.getPixels());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000516 }
517}
518
519
520////////////////////////////////////////////////////////////////////////////////
521
522#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
523static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000524 // returns true if the value is greater than floor(dividend/divisor)
525 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000526 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000527 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000528}
529#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
530
531
532#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
533 #define kBackwards_SkColorType kRGBA_8888_SkColorType
534#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
535 #define kBackwards_SkColorType kBGRA_8888_SkColorType
536#else
537 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
538#endif
539
540static inline const char* SkColorType_to_string(SkColorType colorType) {
541 switch(colorType) {
542 case kAlpha_8_SkColorType: return "Alpha_8";
543 case kRGB_565_SkColorType: return "RGB_565";
544 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000545 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000546 case kBackwards_SkColorType: return "Backwards";
547 case kIndex_8_SkColorType: return "Index_8";
548 default: return "ERROR";
549 }
550}
551
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000552static inline const char* options_colorType(
553 const SkDecodingImageGenerator::Options& opts) {
554 if (opts.fUseRequestedColorType) {
555 return SkColorType_to_string(opts.fRequestedColorType);
556 } else {
557 return "(none)";
558 }
559}
560
561static inline const char* yn(bool value) {
562 if (value) {
563 return "yes";
564 } else {
565 return "no";
566 }
567}
568
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000569/**
570 * Given either a SkStream or a SkData, try to decode the encoded
571 * image using the specified options and report errors.
572 */
573static void test_options(skiatest::Reporter* reporter,
574 const SkDecodingImageGenerator::Options& opts,
575 SkStreamRewindable* encodedStream,
576 SkData* encodedData,
577 bool useData,
578 const SkString& path) {
579 SkBitmap bm;
580 bool success = false;
581 if (useData) {
582 if (NULL == encodedData) {
583 return;
584 }
585 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000586 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000587 } else {
588 if (NULL == encodedStream) {
589 return;
590 }
591 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000592 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000593 }
594 if (!success) {
595 if (opts.fUseRequestedColorType
596 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
597 return; // Ignore known conversion inabilities.
598 }
599 // If we get here, it's a failure and we will need more
600 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000601 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
602 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
603 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000604 return;
605 }
606 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
607 // Android is the only system that use Skia's image decoders in
608 // production. For now, we'll only verify that samplesize works
609 // on systems where it already is known to work.
610 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
611 opts.fSampleSize));
612 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
613 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000614 // The ImageDecoder API doesn't guarantee that SampleSize does
615 // anything at all, but the decoders that this test excercises all
616 // produce an output size in the following range:
617 // (((sample_size * out_size) > (in_size - sample_size))
618 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000619 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
620 SkAutoLockPixels alp(bm);
621 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000622 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
623 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
624 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000625 return;
626 }
627
reedbfefc7c2014-06-12 17:40:00 -0700628 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000629 REPORTER_ASSERT(reporter,
630 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700631 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000632
633 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700634 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000635 && (!path.endsWith(".jpg")) // lossy
636 && (opts.fSampleSize == 1)) { // scaled
637 const SkColor* correctPixels = kExpectedPixels;
638 SkASSERT(bm.height() == kExpectedHeight);
639 SkASSERT(bm.width() == kExpectedWidth);
640 int pixelErrors = 0;
641 for (int y = 0; y < bm.height(); ++y) {
642 for (int x = 0; x < bm.width(); ++x) {
643 if (*correctPixels != bm.getColor(x, y)) {
644 ++pixelErrors;
645 }
646 ++correctPixels;
647 }
648 }
649 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000650 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
651 "[sampleSize=%d dither=%s colorType=%s %s]",
652 pixelErrors, kExpectedHeight * kExpectedWidth,
653 opts.fSampleSize, yn(opts.fDitherImage),
654 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000655 }
656 }
657}
658
659/**
660 * SkDecodingImageGenerator has an Options struct which lets the
661 * client of the generator set sample size, dithering, and bitmap
662 * config. This test loops through many possible options and tries
663 * them on a set of 5 small encoded images (each in a different
664 * format). We test both SkData and SkStreamRewindable decoding.
665 */
666DEF_TEST(ImageDecoderOptions, reporter) {
667 const char* files[] = {
668 "randPixels.bmp",
669 "randPixels.jpg",
670 "randPixels.png",
671 "randPixels.webp",
672 #if !defined(SK_BUILD_FOR_WIN)
673 // TODO(halcanary): Find out why this fails sometimes.
674 "randPixels.gif",
675 #endif
676 };
677
tfarinabcbc1782014-06-18 14:32:48 -0700678 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000679 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000680 return;
681 }
682
683 int scaleList[] = {1, 2, 3, 4};
684 bool ditherList[] = {true, false};
685 SkColorType colorList[] = {
686 kAlpha_8_SkColorType,
687 kRGB_565_SkColorType,
688 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000689 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000690 // Note that indexed color is left out of the list. Lazy
691 // decoding doesn't do indexed color.
692 };
693 const bool useDataList[] = {true, false};
694
695 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
tfarinaa8e2e152014-07-28 19:26:58 -0700696 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000697 if (!sk_exists(path.c_str())) {
698 continue;
699 }
700
701 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
702 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
703 SkAutoTUnref<SkStreamRewindable> encodedStream(
704 SkStream::NewFromFile(path.c_str()));
705 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
706
707 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
708 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
709 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
710 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
711 SkDecodingImageGenerator::Options opts(scaleList[i],
712 ditherList[j],
713 colorList[k]);
714 test_options(reporter, opts, encodedStream, encodedData,
715 useDataList[m], path);
716
717 }
718 SkDecodingImageGenerator::Options options(scaleList[i],
719 ditherList[j]);
720 test_options(reporter, options, encodedStream, encodedData,
721 useDataList[m], path);
722 }
723 }
724 }
725 }
726}
halcanary7f8aad82014-07-28 08:23:55 -0700727
728DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) {
729 SkString resourceDir = GetResourcePath();
tfarinaa8e2e152014-07-28 19:26:58 -0700730 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
halcanary7f8aad82014-07-28 08:23:55 -0700731 if (!sk_exists(path.c_str())) {
732 return;
733 }
734 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
735 SkBitmap bitmap;
736 if (!SkInstallDiscardablePixelRef(
737 SkDecodingImageGenerator::Create(
738 encoded, SkDecodingImageGenerator::Options()), &bitmap)) {
halcanary186e6992014-07-28 10:54:26 -0700739 #ifndef SK_BUILD_FOR_WIN
halcanary7f8aad82014-07-28 08:23:55 -0700740 ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed.");
halcanary186e6992014-07-28 10:54:26 -0700741 #endif
halcanary7f8aad82014-07-28 08:23:55 -0700742 return;
743 }
halcanaryb1ab5fd2014-07-28 08:56:35 -0700744 if (kIndex_8_SkColorType != bitmap.colorType()) {
745 return;
746 }
halcanary7f8aad82014-07-28 08:23:55 -0700747 {
748 SkAutoLockPixels alp(bitmap);
749 REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass");
750 }
751 {
752 SkAutoLockPixels alp(bitmap);
753 REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass");
754 }
755}
halcanarya0cc8332014-08-01 10:31:48 -0700756
757
758////////////////////////////////////////////////////////////////////////////////
759namespace {
760class SingleAllocator : public SkBitmap::Allocator {
761public:
762 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
763 ~SingleAllocator() {}
764 // If the pixels in fPixels are big enough, use them.
765 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
766 SkASSERT(bm);
767 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
768 bm->setPixels(fPixels, ct);
769 fPixels = NULL;
770 fSize = 0;
771 return true;
772 }
reed84825042014-09-02 12:50:45 -0700773 return bm->tryAllocPixels(NULL, ct);
halcanarya0cc8332014-08-01 10:31:48 -0700774 }
775 bool ready() { return fPixels != NULL; }
776private:
777 void* fPixels;
778 size_t fSize;
779};
780} // namespace
781
782/* This tests for a bug in libjpeg where INT32 is typedefed to long
783 and memory can be written to outside of the array. */
784DEF_TEST(ImageDecoding_JpegOverwrite, r) {
785 SkString resourceDir = GetResourcePath();
786 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
787 SkAutoTUnref<SkStreamAsset> stream(
788 SkStream::NewFromFile(path.c_str()));
789 if (!stream.get()) {
790 SkDebugf("\nPath '%s' missing.\n", path.c_str());
791 return;
792 }
793 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
794 if (NULL == decoder.get()) {
795 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
796 return;
797 }
798 SkAssertResult(stream->rewind());
799
800 static const uint16_t sentinal = 0xBEEF;
801 static const int pixelCount = 16;
802 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
803 // pixels.get() should be 4-byte aligned.
804 // This is necessary to reproduce the bug.
805
806 pixels[pixelCount] = sentinal; // This value should not be changed.
807
808 SkAutoTUnref<SingleAllocator> allocator(
809 SkNEW_ARGS(SingleAllocator,
810 ((void*)pixels.get(), sizeof(uint16_t) * pixelCount)));
811 decoder->setAllocator(allocator);
812 decoder->setSampleSize(2);
813 SkBitmap bitmap;
814 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700815 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanarya0cc8332014-08-01 10:31:48 -0700816 REPORTER_ASSERT(r, success);
817 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
818 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
819}