blob: 61c89cbd4a5f9c465dfaf1b2cd081d5bb1d706e3 [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;
reed48392b72015-09-14 13:41:23 -0700440 SkAutoTDelete<SkImageGenerator> gen(SkDecodingImageGenerator::Create(stream.detach(),
441 opts));
reed74bd9532015-09-14 08:52:12 -0700442 REPORTER_ASSERT(reporter, gen->tryGenerateBitmap(&bm));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000443 }
444}
445
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000446////////////////////////////////////////////////////////////////////////////////
447
448#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
449static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000450 // returns true if the value is greater than floor(dividend/divisor)
451 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000452 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000453 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000454}
455#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
456
457
458#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
459 #define kBackwards_SkColorType kRGBA_8888_SkColorType
460#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
461 #define kBackwards_SkColorType kBGRA_8888_SkColorType
462#else
463 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
464#endif
465
466static inline const char* SkColorType_to_string(SkColorType colorType) {
467 switch(colorType) {
468 case kAlpha_8_SkColorType: return "Alpha_8";
469 case kRGB_565_SkColorType: return "RGB_565";
470 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000471 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000472 case kBackwards_SkColorType: return "Backwards";
473 case kIndex_8_SkColorType: return "Index_8";
474 default: return "ERROR";
475 }
476}
477
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000478static inline const char* options_colorType(
479 const SkDecodingImageGenerator::Options& opts) {
480 if (opts.fUseRequestedColorType) {
481 return SkColorType_to_string(opts.fRequestedColorType);
482 } else {
483 return "(none)";
484 }
485}
486
487static inline const char* yn(bool value) {
488 if (value) {
489 return "yes";
490 } else {
491 return "no";
492 }
493}
494
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000495/**
496 * Given either a SkStream or a SkData, try to decode the encoded
497 * image using the specified options and report errors.
498 */
499static void test_options(skiatest::Reporter* reporter,
500 const SkDecodingImageGenerator::Options& opts,
501 SkStreamRewindable* encodedStream,
502 SkData* encodedData,
503 bool useData,
504 const SkString& path) {
505 SkBitmap bm;
reed8725e532015-09-14 10:53:24 -0700506 SkAutoTDelete<SkImageGenerator> gen;
reed74bd9532015-09-14 08:52:12 -0700507
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000508 if (useData) {
halcanary96fcdcc2015-08-27 07:41:13 -0700509 if (nullptr == encodedData) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000510 return;
511 }
reed8725e532015-09-14 10:53:24 -0700512 gen.reset(SkDecodingImageGenerator::Create(encodedData, opts));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000513 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700514 if (nullptr == encodedStream) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000515 return;
516 }
reed8725e532015-09-14 10:53:24 -0700517 gen.reset(SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000518 }
reed74bd9532015-09-14 08:52:12 -0700519 if (!gen) {
520 if (opts.fUseRequestedColorType && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000521 return; // Ignore known conversion inabilities.
522 }
523 // If we get here, it's a failure and we will need more
524 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000525 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
526 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
527 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000528 return;
529 }
reed74bd9532015-09-14 08:52:12 -0700530 if (!gen->tryGenerateBitmap(&bm)) {
531 return;
532 }
533
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000534 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
535 // Android is the only system that use Skia's image decoders in
536 // production. For now, we'll only verify that samplesize works
537 // on systems where it already is known to work.
reed74bd9532015-09-14 08:52:12 -0700538 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight, opts.fSampleSize));
539 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth, opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000540 // The ImageDecoder API doesn't guarantee that SampleSize does
541 // anything at all, but the decoders that this test excercises all
542 // produce an output size in the following range:
543 // (((sample_size * out_size) > (in_size - sample_size))
544 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000545 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000546
reedbfefc7c2014-06-12 17:40:00 -0700547 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000548 REPORTER_ASSERT(reporter,
549 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700550 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000551
552 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700553 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000554 && (!path.endsWith(".jpg")) // lossy
555 && (opts.fSampleSize == 1)) { // scaled
556 const SkColor* correctPixels = kExpectedPixels;
557 SkASSERT(bm.height() == kExpectedHeight);
558 SkASSERT(bm.width() == kExpectedWidth);
559 int pixelErrors = 0;
560 for (int y = 0; y < bm.height(); ++y) {
561 for (int x = 0; x < bm.width(); ++x) {
562 if (*correctPixels != bm.getColor(x, y)) {
563 ++pixelErrors;
564 }
565 ++correctPixels;
566 }
567 }
568 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000569 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
570 "[sampleSize=%d dither=%s colorType=%s %s]",
571 pixelErrors, kExpectedHeight * kExpectedWidth,
572 opts.fSampleSize, yn(opts.fDitherImage),
573 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000574 }
575 }
576}
577
578/**
579 * SkDecodingImageGenerator has an Options struct which lets the
580 * client of the generator set sample size, dithering, and bitmap
581 * config. This test loops through many possible options and tries
582 * them on a set of 5 small encoded images (each in a different
583 * format). We test both SkData and SkStreamRewindable decoding.
584 */
585DEF_TEST(ImageDecoderOptions, reporter) {
586 const char* files[] = {
587 "randPixels.bmp",
588 "randPixels.jpg",
589 "randPixels.png",
590 "randPixels.webp",
591 #if !defined(SK_BUILD_FOR_WIN)
592 // TODO(halcanary): Find out why this fails sometimes.
593 "randPixels.gif",
594 #endif
595 };
596
tfarinabcbc1782014-06-18 14:32:48 -0700597 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000598 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000599 return;
600 }
601
602 int scaleList[] = {1, 2, 3, 4};
603 bool ditherList[] = {true, false};
604 SkColorType colorList[] = {
605 kAlpha_8_SkColorType,
606 kRGB_565_SkColorType,
607 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000608 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000609 // Note that indexed color is left out of the list. Lazy
610 // decoding doesn't do indexed color.
611 };
612 const bool useDataList[] = {true, false};
613
614 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
tfarinaa8e2e152014-07-28 19:26:58 -0700615 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000616 if (!sk_exists(path.c_str())) {
617 continue;
618 }
619
620 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700621 REPORTER_ASSERT(reporter, encodedData.get() != nullptr);
scroggoa1193e42015-01-21 12:09:53 -0800622 SkAutoTDelete<SkStreamRewindable> encodedStream(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000623 SkStream::NewFromFile(path.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700624 REPORTER_ASSERT(reporter, encodedStream.get() != nullptr);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000625
626 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
627 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
628 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
629 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
630 SkDecodingImageGenerator::Options opts(scaleList[i],
631 ditherList[j],
632 colorList[k]);
633 test_options(reporter, opts, encodedStream, encodedData,
634 useDataList[m], path);
635
636 }
637 SkDecodingImageGenerator::Options options(scaleList[i],
638 ditherList[j]);
639 test_options(reporter, options, encodedStream, encodedData,
640 useDataList[m], path);
641 }
642 }
643 }
644 }
645}
halcanary7f8aad82014-07-28 08:23:55 -0700646
reed74bd9532015-09-14 08:52:12 -0700647DEF_TEST(DecodingImageGenerator_ColorTableCheck, r) {
halcanary7f8aad82014-07-28 08:23:55 -0700648 SkString resourceDir = GetResourcePath();
tfarinaa8e2e152014-07-28 19:26:58 -0700649 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
halcanary7f8aad82014-07-28 08:23:55 -0700650 if (!sk_exists(path.c_str())) {
651 return;
652 }
653 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
654 SkBitmap bitmap;
reed8725e532015-09-14 10:53:24 -0700655 SkAutoTDelete<SkImageGenerator> gen(SkDecodingImageGenerator::Create(encoded,
656 SkDecodingImageGenerator::Options()));
reed74bd9532015-09-14 08:52:12 -0700657 if (!gen) {
658 REPORTER_ASSERT(r, false);
659 return;
660 }
661 if (!gen->tryGenerateBitmap(&bitmap)) {
662 REPORTER_ASSERT(r, false);
halcanary7f8aad82014-07-28 08:23:55 -0700663 return;
664 }
halcanaryb1ab5fd2014-07-28 08:56:35 -0700665 if (kIndex_8_SkColorType != bitmap.colorType()) {
666 return;
667 }
reed74bd9532015-09-14 08:52:12 -0700668 REPORTER_ASSERT(r, bitmap.getColorTable());
halcanary7f8aad82014-07-28 08:23:55 -0700669}
halcanarya0cc8332014-08-01 10:31:48 -0700670
671
672////////////////////////////////////////////////////////////////////////////////
673namespace {
674class SingleAllocator : public SkBitmap::Allocator {
675public:
676 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
677 ~SingleAllocator() {}
678 // If the pixels in fPixels are big enough, use them.
mtklein36352bf2015-03-25 18:17:31 -0700679 bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) override {
halcanarya0cc8332014-08-01 10:31:48 -0700680 SkASSERT(bm);
681 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
682 bm->setPixels(fPixels, ct);
halcanary96fcdcc2015-08-27 07:41:13 -0700683 fPixels = nullptr;
halcanarya0cc8332014-08-01 10:31:48 -0700684 fSize = 0;
685 return true;
686 }
halcanary96fcdcc2015-08-27 07:41:13 -0700687 return bm->tryAllocPixels(nullptr, ct);
halcanarya0cc8332014-08-01 10:31:48 -0700688 }
halcanary96fcdcc2015-08-27 07:41:13 -0700689 bool ready() { return fPixels != nullptr; }
halcanarya0cc8332014-08-01 10:31:48 -0700690private:
691 void* fPixels;
692 size_t fSize;
693};
694} // namespace
695
696/* This tests for a bug in libjpeg where INT32 is typedefed to long
697 and memory can be written to outside of the array. */
698DEF_TEST(ImageDecoding_JpegOverwrite, r) {
699 SkString resourceDir = GetResourcePath();
700 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
scroggoa1193e42015-01-21 12:09:53 -0800701 SkAutoTDelete<SkStreamAsset> stream(
halcanarya0cc8332014-08-01 10:31:48 -0700702 SkStream::NewFromFile(path.c_str()));
703 if (!stream.get()) {
704 SkDebugf("\nPath '%s' missing.\n", path.c_str());
705 return;
706 }
707 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
halcanary96fcdcc2015-08-27 07:41:13 -0700708 if (nullptr == decoder.get()) {
halcanarya0cc8332014-08-01 10:31:48 -0700709 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
710 return;
711 }
712 SkAssertResult(stream->rewind());
713
714 static const uint16_t sentinal = 0xBEEF;
715 static const int pixelCount = 16;
716 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
717 // pixels.get() should be 4-byte aligned.
718 // This is necessary to reproduce the bug.
719
720 pixels[pixelCount] = sentinal; // This value should not be changed.
721
722 SkAutoTUnref<SingleAllocator> allocator(
halcanary385fe4d2015-08-26 13:07:48 -0700723 new SingleAllocator((void*)pixels.get(), sizeof(uint16_t) * pixelCount));
halcanarya0cc8332014-08-01 10:31:48 -0700724 decoder->setAllocator(allocator);
725 decoder->setSampleSize(2);
726 SkBitmap bitmap;
727 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700728 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanarya0cc8332014-08-01 10:31:48 -0700729 REPORTER_ASSERT(r, success);
730 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
731 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
732}