blob: 78d6b6c35f5e3a4eb915659ccf62af9340ad67a2 [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
reed5965c8a2015-01-07 18:04:45 -0800458 bool success = SkInstallDiscardablePixelRef(encoded, &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000459
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000460 REPORTER_ASSERT(reporter, success);
461 if (!success) {
462 return;
463 }
464 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000465
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000466 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000467 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000468 REPORTER_ASSERT(reporter, rightSize);
469 if (rightSize) {
470 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000471 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000472 for (int y = 0; y < bm.height(); ++y) {
473 for (int x = 0; x < bm.width(); ++x) {
474 error |= (*correctPixel != bm.getColor(x, y));
475 ++correctPixel;
476 }
477 }
478 REPORTER_ASSERT(reporter, !error);
479 }
480}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000481
482////////////////////////////////////////////////////////////////////////////////
483
484// example of how Android will do this inside their BitmapFactory
485static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
486 SkStreamRewindable* stream,
487 int sampleSize, bool ditherImage) {
488 SkASSERT(bitmap != NULL);
489 SkASSERT(stream != NULL);
490 SkASSERT(stream->rewind());
491 SkASSERT(stream->unique());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000492 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000493 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000494 if (SkInstallDiscardablePixelRef(
495 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000496 return bitmap->pixelRef();
497 }
498 return NULL;
499}
500/**
501 * A test for the SkDecodingImageGenerator::Create and
502 * SkInstallDiscardablePixelRef functions.
503 */
504DEF_TEST(ImprovedBitmapFactory, reporter) {
tfarinac846f4a2014-07-01 12:35:49 -0700505 SkString pngFilename = GetResourcePath("randPixels.png");
506 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
507 if (sk_exists(pngFilename.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000508 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000509 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000510 REPORTER_ASSERT(reporter,
bsalomon49f085d2014-09-05 13:34:00 -0700511 install_pixel_ref(&bm, stream.detach(), 1, true));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000512 SkAutoLockPixels alp(bm);
bsalomon49f085d2014-09-05 13:34:00 -0700513 REPORTER_ASSERT(reporter, bm.getPixels());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000514 }
515}
516
517
518////////////////////////////////////////////////////////////////////////////////
519
520#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
521static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000522 // returns true if the value is greater than floor(dividend/divisor)
523 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000524 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000525 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000526}
527#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
528
529
530#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
531 #define kBackwards_SkColorType kRGBA_8888_SkColorType
532#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
533 #define kBackwards_SkColorType kBGRA_8888_SkColorType
534#else
535 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
536#endif
537
538static inline const char* SkColorType_to_string(SkColorType colorType) {
539 switch(colorType) {
540 case kAlpha_8_SkColorType: return "Alpha_8";
541 case kRGB_565_SkColorType: return "RGB_565";
542 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000543 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000544 case kBackwards_SkColorType: return "Backwards";
545 case kIndex_8_SkColorType: return "Index_8";
546 default: return "ERROR";
547 }
548}
549
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000550static inline const char* options_colorType(
551 const SkDecodingImageGenerator::Options& opts) {
552 if (opts.fUseRequestedColorType) {
553 return SkColorType_to_string(opts.fRequestedColorType);
554 } else {
555 return "(none)";
556 }
557}
558
559static inline const char* yn(bool value) {
560 if (value) {
561 return "yes";
562 } else {
563 return "no";
564 }
565}
566
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000567/**
568 * Given either a SkStream or a SkData, try to decode the encoded
569 * image using the specified options and report errors.
570 */
571static void test_options(skiatest::Reporter* reporter,
572 const SkDecodingImageGenerator::Options& opts,
573 SkStreamRewindable* encodedStream,
574 SkData* encodedData,
575 bool useData,
576 const SkString& path) {
577 SkBitmap bm;
578 bool success = false;
579 if (useData) {
580 if (NULL == encodedData) {
581 return;
582 }
583 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000584 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000585 } else {
586 if (NULL == encodedStream) {
587 return;
588 }
589 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000590 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000591 }
592 if (!success) {
593 if (opts.fUseRequestedColorType
594 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
595 return; // Ignore known conversion inabilities.
596 }
597 // If we get here, it's a failure and we will need more
598 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000599 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
600 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
601 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000602 return;
603 }
604 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
605 // Android is the only system that use Skia's image decoders in
606 // production. For now, we'll only verify that samplesize works
607 // on systems where it already is known to work.
608 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
609 opts.fSampleSize));
610 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
611 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000612 // The ImageDecoder API doesn't guarantee that SampleSize does
613 // anything at all, but the decoders that this test excercises all
614 // produce an output size in the following range:
615 // (((sample_size * out_size) > (in_size - sample_size))
616 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000617 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
618 SkAutoLockPixels alp(bm);
619 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000620 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
621 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
622 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000623 return;
624 }
625
reedbfefc7c2014-06-12 17:40:00 -0700626 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000627 REPORTER_ASSERT(reporter,
628 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700629 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000630
631 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700632 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000633 && (!path.endsWith(".jpg")) // lossy
634 && (opts.fSampleSize == 1)) { // scaled
635 const SkColor* correctPixels = kExpectedPixels;
636 SkASSERT(bm.height() == kExpectedHeight);
637 SkASSERT(bm.width() == kExpectedWidth);
638 int pixelErrors = 0;
639 for (int y = 0; y < bm.height(); ++y) {
640 for (int x = 0; x < bm.width(); ++x) {
641 if (*correctPixels != bm.getColor(x, y)) {
642 ++pixelErrors;
643 }
644 ++correctPixels;
645 }
646 }
647 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000648 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
649 "[sampleSize=%d dither=%s colorType=%s %s]",
650 pixelErrors, kExpectedHeight * kExpectedWidth,
651 opts.fSampleSize, yn(opts.fDitherImage),
652 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000653 }
654 }
655}
656
657/**
658 * SkDecodingImageGenerator has an Options struct which lets the
659 * client of the generator set sample size, dithering, and bitmap
660 * config. This test loops through many possible options and tries
661 * them on a set of 5 small encoded images (each in a different
662 * format). We test both SkData and SkStreamRewindable decoding.
663 */
664DEF_TEST(ImageDecoderOptions, reporter) {
665 const char* files[] = {
666 "randPixels.bmp",
667 "randPixels.jpg",
668 "randPixels.png",
669 "randPixels.webp",
670 #if !defined(SK_BUILD_FOR_WIN)
671 // TODO(halcanary): Find out why this fails sometimes.
672 "randPixels.gif",
673 #endif
674 };
675
tfarinabcbc1782014-06-18 14:32:48 -0700676 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000677 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000678 return;
679 }
680
681 int scaleList[] = {1, 2, 3, 4};
682 bool ditherList[] = {true, false};
683 SkColorType colorList[] = {
684 kAlpha_8_SkColorType,
685 kRGB_565_SkColorType,
686 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000687 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000688 // Note that indexed color is left out of the list. Lazy
689 // decoding doesn't do indexed color.
690 };
691 const bool useDataList[] = {true, false};
692
693 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
tfarinaa8e2e152014-07-28 19:26:58 -0700694 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000695 if (!sk_exists(path.c_str())) {
696 continue;
697 }
698
699 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
700 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
701 SkAutoTUnref<SkStreamRewindable> encodedStream(
702 SkStream::NewFromFile(path.c_str()));
703 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
704
705 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
706 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
707 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
708 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
709 SkDecodingImageGenerator::Options opts(scaleList[i],
710 ditherList[j],
711 colorList[k]);
712 test_options(reporter, opts, encodedStream, encodedData,
713 useDataList[m], path);
714
715 }
716 SkDecodingImageGenerator::Options options(scaleList[i],
717 ditherList[j]);
718 test_options(reporter, options, encodedStream, encodedData,
719 useDataList[m], path);
720 }
721 }
722 }
723 }
724}
halcanary7f8aad82014-07-28 08:23:55 -0700725
726DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) {
727 SkString resourceDir = GetResourcePath();
tfarinaa8e2e152014-07-28 19:26:58 -0700728 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
halcanary7f8aad82014-07-28 08:23:55 -0700729 if (!sk_exists(path.c_str())) {
730 return;
731 }
732 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
733 SkBitmap bitmap;
734 if (!SkInstallDiscardablePixelRef(
735 SkDecodingImageGenerator::Create(
736 encoded, SkDecodingImageGenerator::Options()), &bitmap)) {
halcanary186e6992014-07-28 10:54:26 -0700737 #ifndef SK_BUILD_FOR_WIN
halcanary7f8aad82014-07-28 08:23:55 -0700738 ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed.");
halcanary186e6992014-07-28 10:54:26 -0700739 #endif
halcanary7f8aad82014-07-28 08:23:55 -0700740 return;
741 }
halcanaryb1ab5fd2014-07-28 08:56:35 -0700742 if (kIndex_8_SkColorType != bitmap.colorType()) {
743 return;
744 }
halcanary7f8aad82014-07-28 08:23:55 -0700745 {
746 SkAutoLockPixels alp(bitmap);
747 REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass");
748 }
749 {
750 SkAutoLockPixels alp(bitmap);
751 REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass");
752 }
753}
halcanarya0cc8332014-08-01 10:31:48 -0700754
755
756////////////////////////////////////////////////////////////////////////////////
757namespace {
758class SingleAllocator : public SkBitmap::Allocator {
759public:
760 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
761 ~SingleAllocator() {}
762 // If the pixels in fPixels are big enough, use them.
mtklein72c9faa2015-01-09 10:06:39 -0800763 bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
halcanarya0cc8332014-08-01 10:31:48 -0700764 SkASSERT(bm);
765 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
766 bm->setPixels(fPixels, ct);
767 fPixels = NULL;
768 fSize = 0;
769 return true;
770 }
reed84825042014-09-02 12:50:45 -0700771 return bm->tryAllocPixels(NULL, ct);
halcanarya0cc8332014-08-01 10:31:48 -0700772 }
773 bool ready() { return fPixels != NULL; }
774private:
775 void* fPixels;
776 size_t fSize;
777};
778} // namespace
779
780/* This tests for a bug in libjpeg where INT32 is typedefed to long
781 and memory can be written to outside of the array. */
782DEF_TEST(ImageDecoding_JpegOverwrite, r) {
783 SkString resourceDir = GetResourcePath();
784 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
785 SkAutoTUnref<SkStreamAsset> stream(
786 SkStream::NewFromFile(path.c_str()));
787 if (!stream.get()) {
788 SkDebugf("\nPath '%s' missing.\n", path.c_str());
789 return;
790 }
791 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
792 if (NULL == decoder.get()) {
793 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
794 return;
795 }
796 SkAssertResult(stream->rewind());
797
798 static const uint16_t sentinal = 0xBEEF;
799 static const int pixelCount = 16;
800 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
801 // pixels.get() should be 4-byte aligned.
802 // This is necessary to reproduce the bug.
803
804 pixels[pixelCount] = sentinal; // This value should not be changed.
805
806 SkAutoTUnref<SingleAllocator> allocator(
807 SkNEW_ARGS(SingleAllocator,
808 ((void*)pixels.get(), sizeof(uint16_t) * pixelCount)));
809 decoder->setAllocator(allocator);
810 decoder->setSampleSize(2);
811 SkBitmap bitmap;
812 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700813 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanarya0cc8332014-08-01 10:31:48 -0700814 REPORTER_ASSERT(r, success);
815 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
816 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
817}