blob: c1ff39fa4e1e80f4bced140853a0c8f5fa00bb7b [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));
halcanary96fcdcc2015-08-27 07:41:13 -070090 if (nullptr == decoder.get()) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000091 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));
halcanary96fcdcc2015-08-27 07:41:13 -0700179 if (nullptr == decoder.get()) {
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000180 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
halcanarya096d7a2015-03-27 12:16:53 -0700221 || kOpaque_SkAlphaType == boundsAlphaType
222 || filename.endsWith(".ico"));
223 // TODO(halcanary): Find out why color_wheel.ico fails this test.
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000224 } else {
225 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
226 || kOpaque_SkAlphaType == boundsAlphaType);
227 }
228
229 // When decoding the full image, the alpha type should match the one
230 // returned by the bounds decode, unless the full decode determined that
231 // the alpha type is actually opaque.
232 REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
233 || bm.alphaType() == kOpaque_SkAlphaType);
234}
235
236DEF_TEST(ImageDecoding_alphaType, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700237 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000238 if (resourcePath.isEmpty()) {
239 SkDebugf("Could not run alphaType test because resourcePath not specified.");
240 return;
241 }
242
243 SkOSFile::Iter iter(resourcePath.c_str());
244 SkString basename;
245 if (iter.next(&basename)) {
246 do {
tfarinaa8e2e152014-07-28 19:26:58 -0700247 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000248 for (int truth = 0; truth <= 1; ++truth) {
249 test_alphaType(reporter, filename, SkToBool(truth));
250 }
251 } while (iter.next(&basename));
252 } else {
253 SkDebugf("Failed to find any files :(\n");
254 }
255
256}
257
258// Using known images, test that decoding into unpremul and premul behave as expected.
259DEF_TEST(ImageDecoding_unpremul, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700260 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000261 if (resourcePath.isEmpty()) {
262 SkDebugf("Could not run unpremul test because resourcePath not specified.");
263 return;
264 }
265 const char* root = "half-transparent-white-pixel";
266 const char* suffixes[] = { ".png", ".webp" };
267
268 for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
269 SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
tfarinaa8e2e152014-07-28 19:26:58 -0700270 SkString fullName = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000271
272 SkBitmap bm;
273 SkFILEStream stream(fullName.c_str());
274
275 if (!stream.isValid()) {
276 SkDebugf("file %s missing from resource directoy %s\n",
277 basename.c_str(), resourcePath.c_str());
278 continue;
279 }
280
281 // This should never fail since we know the images we're decoding.
282 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
bsalomon49f085d2014-09-05 13:34:00 -0700283 REPORTER_ASSERT(reporter, decoder.get());
halcanary96fcdcc2015-08-27 07:41:13 -0700284 if (nullptr == decoder.get()) {
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000285 continue;
286 }
287
288 // Test unpremultiplied. We know what color this should result in.
289 decoder->setRequireUnpremultipliedColors(true);
reedbfefc7c2014-06-12 17:40:00 -0700290 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000291 SkImageDecoder::kDecodePixels_Mode);
292 REPORTER_ASSERT(reporter, success);
293 if (!success) {
294 continue;
295 }
296
297 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
298 {
299 SkAutoLockPixels alp(bm);
300 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
301 }
302
303 success = stream.rewind();
304 REPORTER_ASSERT(reporter, success);
305 if (!success) {
306 continue;
307 }
308
309 // Test premultiplied. Once again, we know which color this should
310 // result in.
311 decoder->setRequireUnpremultipliedColors(false);
reedbfefc7c2014-06-12 17:40:00 -0700312 success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000313 SkImageDecoder::kDecodePixels_Mode);
314 REPORTER_ASSERT(reporter, success);
315 if (!success) {
316 continue;
317 }
318
319 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
320 {
321 SkAutoLockPixels alp(bm);
322 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
323 }
324 }
325}
326#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
327
scroggo@google.com826d63a2013-07-18 20:06:28 +0000328#ifdef SK_DEBUG
scroggo@google.com8d239242013-10-01 17:27:15 +0000329// Test inside SkScaledBitmapSampler.cpp
330extern void test_row_proc_choice();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000331#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000332
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000333DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000334 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000335#ifdef SK_DEBUG
scroggo@google.com8d239242013-10-01 17:27:15 +0000336 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000337#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000338}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000339
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000340// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000341static const int kExpectedWidth = 8;
342static const int kExpectedHeight = 8;
343static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000344 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
345 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
346 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
347 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
348 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
349 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
350 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
351 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
352 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
353 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
354 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
355 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
356 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
357 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
358 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
359 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
360};
bungeman99fe8222015-08-20 07:57:51 -0700361static_assert((kExpectedWidth * kExpectedHeight) == SK_ARRAY_COUNT(kExpectedPixels),
362 "array_size_mismatch");
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000363
reed74bd9532015-09-14 08:52:12 -0700364static bool decode_into_bitmap(skiatest::Reporter* r, SkBitmap* bm, SkData* encoded) {
365 SkAutoTDelete<SkImageGenerator> gen(SkImageGenerator::NewFromEncoded(encoded));
366 if (!gen) {
367 REPORTER_ASSERT(r, false);
368 return false;
369 }
370 if (!gen->tryGenerateBitmap(bm)) {
371 REPORTER_ASSERT(r, false);
372 return false;
373 }
374 return true;
375}
376
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000377DEF_TEST(WebP, reporter) {
378 const unsigned char encodedWebP[] = {
379 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
380 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
381 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
382 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
383 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
384 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
385 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
386 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
387 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
388 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
389 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
390 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
391 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
392 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
393 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
394 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
395 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
396 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
397 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
398 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
399 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
400 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
401 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
402 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
403 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
404 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
405 };
reed74bd9532015-09-14 08:52:12 -0700406
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000407 SkBitmap bm;
reed74bd9532015-09-14 08:52:12 -0700408 SkAutoDataUnref encoded(SkData::NewWithoutCopy(encodedWebP, sizeof(encodedWebP)));
409 if (!decode_into_bitmap(reporter, &bm, encoded)) {
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000410 return;
411 }
reed74bd9532015-09-14 08:52:12 -0700412 if (kExpectedWidth != bm.width() || kExpectedHeight != bm.height()) {
413 REPORTER_ASSERT(reporter, false);
414 return;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000415 }
reed74bd9532015-09-14 08:52:12 -0700416
417 bool error = false;
418 const SkColor* correctPixel = kExpectedPixels;
419 for (int y = 0; y < bm.height(); ++y) {
420 for (int x = 0; x < bm.width(); ++x) {
421 error |= (*correctPixel != bm.getColor(x, y));
422 ++correctPixel;
423 }
424 }
425 REPORTER_ASSERT(reporter, !error);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000426}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000427
428////////////////////////////////////////////////////////////////////////////////
429
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000430/**
reed74bd9532015-09-14 08:52:12 -0700431 * A test for the SkDecodingImageGenerator::Create
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000432 */
433DEF_TEST(ImprovedBitmapFactory, reporter) {
tfarinac846f4a2014-07-01 12:35:49 -0700434 SkString pngFilename = GetResourcePath("randPixels.png");
scroggoa1193e42015-01-21 12:09:53 -0800435 SkAutoTDelete<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
tfarinac846f4a2014-07-01 12:35:49 -0700436 if (sk_exists(pngFilename.c_str())) {
reed74bd9532015-09-14 08:52:12 -0700437 // example of how Android will do this inside their BitmapFactory
438 SkDecodingImageGenerator::Options opts(1, true, kN32_SkColorType);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000439 SkBitmap bm;
reed74bd9532015-09-14 08:52:12 -0700440 SkImageGenerator* gen = SkDecodingImageGenerator::Create(stream, opts);
441 REPORTER_ASSERT(reporter, gen->tryGenerateBitmap(&bm));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000442 }
443}
444
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000445////////////////////////////////////////////////////////////////////////////////
446
447#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
448static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000449 // returns true if the value is greater than floor(dividend/divisor)
450 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000451 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000452 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000453}
454#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
455
456
457#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
458 #define kBackwards_SkColorType kRGBA_8888_SkColorType
459#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
460 #define kBackwards_SkColorType kBGRA_8888_SkColorType
461#else
462 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
463#endif
464
465static inline const char* SkColorType_to_string(SkColorType colorType) {
466 switch(colorType) {
467 case kAlpha_8_SkColorType: return "Alpha_8";
468 case kRGB_565_SkColorType: return "RGB_565";
469 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000470 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000471 case kBackwards_SkColorType: return "Backwards";
472 case kIndex_8_SkColorType: return "Index_8";
473 default: return "ERROR";
474 }
475}
476
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000477static inline const char* options_colorType(
478 const SkDecodingImageGenerator::Options& opts) {
479 if (opts.fUseRequestedColorType) {
480 return SkColorType_to_string(opts.fRequestedColorType);
481 } else {
482 return "(none)";
483 }
484}
485
486static inline const char* yn(bool value) {
487 if (value) {
488 return "yes";
489 } else {
490 return "no";
491 }
492}
493
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000494/**
495 * Given either a SkStream or a SkData, try to decode the encoded
496 * image using the specified options and report errors.
497 */
498static void test_options(skiatest::Reporter* reporter,
499 const SkDecodingImageGenerator::Options& opts,
500 SkStreamRewindable* encodedStream,
501 SkData* encodedData,
502 bool useData,
503 const SkString& path) {
504 SkBitmap bm;
reed8725e532015-09-14 10:53:24 -0700505 SkAutoTDelete<SkImageGenerator> gen;
reed74bd9532015-09-14 08:52:12 -0700506
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000507 if (useData) {
halcanary96fcdcc2015-08-27 07:41:13 -0700508 if (nullptr == encodedData) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000509 return;
510 }
reed8725e532015-09-14 10:53:24 -0700511 gen.reset(SkDecodingImageGenerator::Create(encodedData, opts));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000512 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700513 if (nullptr == encodedStream) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000514 return;
515 }
reed8725e532015-09-14 10:53:24 -0700516 gen.reset(SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000517 }
reed74bd9532015-09-14 08:52:12 -0700518 if (!gen) {
519 if (opts.fUseRequestedColorType && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000520 return; // Ignore known conversion inabilities.
521 }
522 // If we get here, it's a failure and we will need more
523 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000524 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
525 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
526 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000527 return;
528 }
reed74bd9532015-09-14 08:52:12 -0700529 if (!gen->tryGenerateBitmap(&bm)) {
530 return;
531 }
532
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000533 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
534 // Android is the only system that use Skia's image decoders in
535 // production. For now, we'll only verify that samplesize works
536 // on systems where it already is known to work.
reed74bd9532015-09-14 08:52:12 -0700537 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight, opts.fSampleSize));
538 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth, opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000539 // The ImageDecoder API doesn't guarantee that SampleSize does
540 // anything at all, but the decoders that this test excercises all
541 // produce an output size in the following range:
542 // (((sample_size * out_size) > (in_size - sample_size))
543 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000544 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000545
reedbfefc7c2014-06-12 17:40:00 -0700546 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000547 REPORTER_ASSERT(reporter,
548 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700549 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000550
551 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700552 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000553 && (!path.endsWith(".jpg")) // lossy
554 && (opts.fSampleSize == 1)) { // scaled
555 const SkColor* correctPixels = kExpectedPixels;
556 SkASSERT(bm.height() == kExpectedHeight);
557 SkASSERT(bm.width() == kExpectedWidth);
558 int pixelErrors = 0;
559 for (int y = 0; y < bm.height(); ++y) {
560 for (int x = 0; x < bm.width(); ++x) {
561 if (*correctPixels != bm.getColor(x, y)) {
562 ++pixelErrors;
563 }
564 ++correctPixels;
565 }
566 }
567 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000568 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
569 "[sampleSize=%d dither=%s colorType=%s %s]",
570 pixelErrors, kExpectedHeight * kExpectedWidth,
571 opts.fSampleSize, yn(opts.fDitherImage),
572 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000573 }
574 }
575}
576
577/**
578 * SkDecodingImageGenerator has an Options struct which lets the
579 * client of the generator set sample size, dithering, and bitmap
580 * config. This test loops through many possible options and tries
581 * them on a set of 5 small encoded images (each in a different
582 * format). We test both SkData and SkStreamRewindable decoding.
583 */
584DEF_TEST(ImageDecoderOptions, reporter) {
585 const char* files[] = {
586 "randPixels.bmp",
587 "randPixels.jpg",
588 "randPixels.png",
589 "randPixels.webp",
590 #if !defined(SK_BUILD_FOR_WIN)
591 // TODO(halcanary): Find out why this fails sometimes.
592 "randPixels.gif",
593 #endif
594 };
595
tfarinabcbc1782014-06-18 14:32:48 -0700596 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000597 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000598 return;
599 }
600
601 int scaleList[] = {1, 2, 3, 4};
602 bool ditherList[] = {true, false};
603 SkColorType colorList[] = {
604 kAlpha_8_SkColorType,
605 kRGB_565_SkColorType,
606 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000607 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000608 // Note that indexed color is left out of the list. Lazy
609 // decoding doesn't do indexed color.
610 };
611 const bool useDataList[] = {true, false};
612
613 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
tfarinaa8e2e152014-07-28 19:26:58 -0700614 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000615 if (!sk_exists(path.c_str())) {
616 continue;
617 }
618
619 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700620 REPORTER_ASSERT(reporter, encodedData.get() != nullptr);
scroggoa1193e42015-01-21 12:09:53 -0800621 SkAutoTDelete<SkStreamRewindable> encodedStream(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000622 SkStream::NewFromFile(path.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700623 REPORTER_ASSERT(reporter, encodedStream.get() != nullptr);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000624
625 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
626 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
627 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
628 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
629 SkDecodingImageGenerator::Options opts(scaleList[i],
630 ditherList[j],
631 colorList[k]);
632 test_options(reporter, opts, encodedStream, encodedData,
633 useDataList[m], path);
634
635 }
636 SkDecodingImageGenerator::Options options(scaleList[i],
637 ditherList[j]);
638 test_options(reporter, options, encodedStream, encodedData,
639 useDataList[m], path);
640 }
641 }
642 }
643 }
644}
halcanary7f8aad82014-07-28 08:23:55 -0700645
reed74bd9532015-09-14 08:52:12 -0700646DEF_TEST(DecodingImageGenerator_ColorTableCheck, r) {
halcanary7f8aad82014-07-28 08:23:55 -0700647 SkString resourceDir = GetResourcePath();
tfarinaa8e2e152014-07-28 19:26:58 -0700648 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
halcanary7f8aad82014-07-28 08:23:55 -0700649 if (!sk_exists(path.c_str())) {
650 return;
651 }
652 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
653 SkBitmap bitmap;
reed8725e532015-09-14 10:53:24 -0700654 SkAutoTDelete<SkImageGenerator> gen(SkDecodingImageGenerator::Create(encoded,
655 SkDecodingImageGenerator::Options()));
reed74bd9532015-09-14 08:52:12 -0700656 if (!gen) {
657 REPORTER_ASSERT(r, false);
658 return;
659 }
660 if (!gen->tryGenerateBitmap(&bitmap)) {
661 REPORTER_ASSERT(r, false);
halcanary7f8aad82014-07-28 08:23:55 -0700662 return;
663 }
halcanaryb1ab5fd2014-07-28 08:56:35 -0700664 if (kIndex_8_SkColorType != bitmap.colorType()) {
665 return;
666 }
reed74bd9532015-09-14 08:52:12 -0700667 REPORTER_ASSERT(r, bitmap.getColorTable());
halcanary7f8aad82014-07-28 08:23:55 -0700668}
halcanarya0cc8332014-08-01 10:31:48 -0700669
670
671////////////////////////////////////////////////////////////////////////////////
672namespace {
673class SingleAllocator : public SkBitmap::Allocator {
674public:
675 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
676 ~SingleAllocator() {}
677 // If the pixels in fPixels are big enough, use them.
mtklein36352bf2015-03-25 18:17:31 -0700678 bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) override {
halcanarya0cc8332014-08-01 10:31:48 -0700679 SkASSERT(bm);
680 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
681 bm->setPixels(fPixels, ct);
halcanary96fcdcc2015-08-27 07:41:13 -0700682 fPixels = nullptr;
halcanarya0cc8332014-08-01 10:31:48 -0700683 fSize = 0;
684 return true;
685 }
halcanary96fcdcc2015-08-27 07:41:13 -0700686 return bm->tryAllocPixels(nullptr, ct);
halcanarya0cc8332014-08-01 10:31:48 -0700687 }
halcanary96fcdcc2015-08-27 07:41:13 -0700688 bool ready() { return fPixels != nullptr; }
halcanarya0cc8332014-08-01 10:31:48 -0700689private:
690 void* fPixels;
691 size_t fSize;
692};
693} // namespace
694
695/* This tests for a bug in libjpeg where INT32 is typedefed to long
696 and memory can be written to outside of the array. */
697DEF_TEST(ImageDecoding_JpegOverwrite, r) {
698 SkString resourceDir = GetResourcePath();
699 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
scroggoa1193e42015-01-21 12:09:53 -0800700 SkAutoTDelete<SkStreamAsset> stream(
halcanarya0cc8332014-08-01 10:31:48 -0700701 SkStream::NewFromFile(path.c_str()));
702 if (!stream.get()) {
703 SkDebugf("\nPath '%s' missing.\n", path.c_str());
704 return;
705 }
706 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
halcanary96fcdcc2015-08-27 07:41:13 -0700707 if (nullptr == decoder.get()) {
halcanarya0cc8332014-08-01 10:31:48 -0700708 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
709 return;
710 }
711 SkAssertResult(stream->rewind());
712
713 static const uint16_t sentinal = 0xBEEF;
714 static const int pixelCount = 16;
715 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
716 // pixels.get() should be 4-byte aligned.
717 // This is necessary to reproduce the bug.
718
719 pixels[pixelCount] = sentinal; // This value should not be changed.
720
721 SkAutoTUnref<SingleAllocator> allocator(
halcanary385fe4d2015-08-26 13:07:48 -0700722 new SingleAllocator((void*)pixels.get(), sizeof(uint16_t) * pixelCount));
halcanarya0cc8332014-08-01 10:31:48 -0700723 decoder->setAllocator(allocator);
724 decoder->setSampleSize(2);
725 SkBitmap bitmap;
726 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700727 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanarya0cc8332014-08-01 10:31:48 -0700728 REPORTER_ASSERT(r, success);
729 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
730 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
731}