blob: 69ed7dedad41c9ee76cdf9f5d91396f8927de4fa [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
364DEF_TEST(WebP, reporter) {
365 const unsigned char encodedWebP[] = {
366 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
367 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
368 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
369 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
370 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
371 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
372 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
373 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
374 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
375 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
376 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
377 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
378 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
379 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
380 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
381 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
382 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
383 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
384 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
385 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
386 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
387 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
388 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
389 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
390 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
391 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
392 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000393 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
394 sizeof(encodedWebP)));
395 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000396
reed5965c8a2015-01-07 18:04:45 -0800397 bool success = SkInstallDiscardablePixelRef(encoded, &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000398
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000399 REPORTER_ASSERT(reporter, success);
400 if (!success) {
401 return;
402 }
403 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000404
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000405 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000406 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000407 REPORTER_ASSERT(reporter, rightSize);
408 if (rightSize) {
409 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000410 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000411 for (int y = 0; y < bm.height(); ++y) {
412 for (int x = 0; x < bm.width(); ++x) {
413 error |= (*correctPixel != bm.getColor(x, y));
414 ++correctPixel;
415 }
416 }
417 REPORTER_ASSERT(reporter, !error);
418 }
419}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000420
421////////////////////////////////////////////////////////////////////////////////
422
423// example of how Android will do this inside their BitmapFactory
424static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
425 SkStreamRewindable* stream,
426 int sampleSize, bool ditherImage) {
halcanary96fcdcc2015-08-27 07:41:13 -0700427 SkASSERT(bitmap != nullptr);
428 SkASSERT(stream != nullptr);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000429 SkASSERT(stream->rewind());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000430 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000431 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000432 if (SkInstallDiscardablePixelRef(
433 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000434 return bitmap->pixelRef();
435 }
halcanary96fcdcc2015-08-27 07:41:13 -0700436 return nullptr;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000437}
438/**
439 * A test for the SkDecodingImageGenerator::Create and
440 * SkInstallDiscardablePixelRef functions.
441 */
442DEF_TEST(ImprovedBitmapFactory, reporter) {
tfarinac846f4a2014-07-01 12:35:49 -0700443 SkString pngFilename = GetResourcePath("randPixels.png");
scroggoa1193e42015-01-21 12:09:53 -0800444 SkAutoTDelete<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
tfarinac846f4a2014-07-01 12:35:49 -0700445 if (sk_exists(pngFilename.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000446 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000447 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000448 REPORTER_ASSERT(reporter,
bsalomon49f085d2014-09-05 13:34:00 -0700449 install_pixel_ref(&bm, stream.detach(), 1, true));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000450 SkAutoLockPixels alp(bm);
bsalomon49f085d2014-09-05 13:34:00 -0700451 REPORTER_ASSERT(reporter, bm.getPixels());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000452 }
453}
454
455
456////////////////////////////////////////////////////////////////////////////////
457
458#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
459static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000460 // returns true if the value is greater than floor(dividend/divisor)
461 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000462 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000463 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000464}
465#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
466
467
468#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
469 #define kBackwards_SkColorType kRGBA_8888_SkColorType
470#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
471 #define kBackwards_SkColorType kBGRA_8888_SkColorType
472#else
473 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
474#endif
475
476static inline const char* SkColorType_to_string(SkColorType colorType) {
477 switch(colorType) {
478 case kAlpha_8_SkColorType: return "Alpha_8";
479 case kRGB_565_SkColorType: return "RGB_565";
480 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000481 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000482 case kBackwards_SkColorType: return "Backwards";
483 case kIndex_8_SkColorType: return "Index_8";
484 default: return "ERROR";
485 }
486}
487
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000488static inline const char* options_colorType(
489 const SkDecodingImageGenerator::Options& opts) {
490 if (opts.fUseRequestedColorType) {
491 return SkColorType_to_string(opts.fRequestedColorType);
492 } else {
493 return "(none)";
494 }
495}
496
497static inline const char* yn(bool value) {
498 if (value) {
499 return "yes";
500 } else {
501 return "no";
502 }
503}
504
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000505/**
506 * Given either a SkStream or a SkData, try to decode the encoded
507 * image using the specified options and report errors.
508 */
509static void test_options(skiatest::Reporter* reporter,
510 const SkDecodingImageGenerator::Options& opts,
511 SkStreamRewindable* encodedStream,
512 SkData* encodedData,
513 bool useData,
514 const SkString& path) {
515 SkBitmap bm;
516 bool success = false;
517 if (useData) {
halcanary96fcdcc2015-08-27 07:41:13 -0700518 if (nullptr == encodedData) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000519 return;
520 }
521 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000522 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000523 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700524 if (nullptr == encodedStream) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000525 return;
526 }
527 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000528 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000529 }
530 if (!success) {
531 if (opts.fUseRequestedColorType
532 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
533 return; // Ignore known conversion inabilities.
534 }
535 // If we get here, it's a failure and we will need more
536 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000537 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
538 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
539 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000540 return;
541 }
542 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
543 // Android is the only system that use Skia's image decoders in
544 // production. For now, we'll only verify that samplesize works
545 // on systems where it already is known to work.
546 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
547 opts.fSampleSize));
548 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
549 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000550 // The ImageDecoder API doesn't guarantee that SampleSize does
551 // anything at all, but the decoders that this test excercises all
552 // produce an output size in the following range:
553 // (((sample_size * out_size) > (in_size - sample_size))
554 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000555 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
556 SkAutoLockPixels alp(bm);
halcanary96fcdcc2015-08-27 07:41:13 -0700557 if (bm.getPixels() == nullptr) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000558 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
559 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
560 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000561 return;
562 }
563
reedbfefc7c2014-06-12 17:40:00 -0700564 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000565 REPORTER_ASSERT(reporter,
566 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700567 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000568
569 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700570 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000571 && (!path.endsWith(".jpg")) // lossy
572 && (opts.fSampleSize == 1)) { // scaled
573 const SkColor* correctPixels = kExpectedPixels;
574 SkASSERT(bm.height() == kExpectedHeight);
575 SkASSERT(bm.width() == kExpectedWidth);
576 int pixelErrors = 0;
577 for (int y = 0; y < bm.height(); ++y) {
578 for (int x = 0; x < bm.width(); ++x) {
579 if (*correctPixels != bm.getColor(x, y)) {
580 ++pixelErrors;
581 }
582 ++correctPixels;
583 }
584 }
585 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000586 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
587 "[sampleSize=%d dither=%s colorType=%s %s]",
588 pixelErrors, kExpectedHeight * kExpectedWidth,
589 opts.fSampleSize, yn(opts.fDitherImage),
590 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000591 }
592 }
593}
594
595/**
596 * SkDecodingImageGenerator has an Options struct which lets the
597 * client of the generator set sample size, dithering, and bitmap
598 * config. This test loops through many possible options and tries
599 * them on a set of 5 small encoded images (each in a different
600 * format). We test both SkData and SkStreamRewindable decoding.
601 */
602DEF_TEST(ImageDecoderOptions, reporter) {
603 const char* files[] = {
604 "randPixels.bmp",
605 "randPixels.jpg",
606 "randPixels.png",
607 "randPixels.webp",
608 #if !defined(SK_BUILD_FOR_WIN)
609 // TODO(halcanary): Find out why this fails sometimes.
610 "randPixels.gif",
611 #endif
612 };
613
tfarinabcbc1782014-06-18 14:32:48 -0700614 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000615 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000616 return;
617 }
618
619 int scaleList[] = {1, 2, 3, 4};
620 bool ditherList[] = {true, false};
621 SkColorType colorList[] = {
622 kAlpha_8_SkColorType,
623 kRGB_565_SkColorType,
624 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000625 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000626 // Note that indexed color is left out of the list. Lazy
627 // decoding doesn't do indexed color.
628 };
629 const bool useDataList[] = {true, false};
630
631 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
tfarinaa8e2e152014-07-28 19:26:58 -0700632 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000633 if (!sk_exists(path.c_str())) {
634 continue;
635 }
636
637 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700638 REPORTER_ASSERT(reporter, encodedData.get() != nullptr);
scroggoa1193e42015-01-21 12:09:53 -0800639 SkAutoTDelete<SkStreamRewindable> encodedStream(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000640 SkStream::NewFromFile(path.c_str()));
halcanary96fcdcc2015-08-27 07:41:13 -0700641 REPORTER_ASSERT(reporter, encodedStream.get() != nullptr);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000642
643 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
644 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
645 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
646 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
647 SkDecodingImageGenerator::Options opts(scaleList[i],
648 ditherList[j],
649 colorList[k]);
650 test_options(reporter, opts, encodedStream, encodedData,
651 useDataList[m], path);
652
653 }
654 SkDecodingImageGenerator::Options options(scaleList[i],
655 ditherList[j]);
656 test_options(reporter, options, encodedStream, encodedData,
657 useDataList[m], path);
658 }
659 }
660 }
661 }
662}
halcanary7f8aad82014-07-28 08:23:55 -0700663
664DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) {
665 SkString resourceDir = GetResourcePath();
tfarinaa8e2e152014-07-28 19:26:58 -0700666 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
halcanary7f8aad82014-07-28 08:23:55 -0700667 if (!sk_exists(path.c_str())) {
668 return;
669 }
670 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
671 SkBitmap bitmap;
672 if (!SkInstallDiscardablePixelRef(
673 SkDecodingImageGenerator::Create(
674 encoded, SkDecodingImageGenerator::Options()), &bitmap)) {
halcanary186e6992014-07-28 10:54:26 -0700675 #ifndef SK_BUILD_FOR_WIN
halcanary7f8aad82014-07-28 08:23:55 -0700676 ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed.");
halcanary186e6992014-07-28 10:54:26 -0700677 #endif
halcanary7f8aad82014-07-28 08:23:55 -0700678 return;
679 }
halcanaryb1ab5fd2014-07-28 08:56:35 -0700680 if (kIndex_8_SkColorType != bitmap.colorType()) {
681 return;
682 }
halcanary7f8aad82014-07-28 08:23:55 -0700683 {
684 SkAutoLockPixels alp(bitmap);
685 REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass");
686 }
687 {
688 SkAutoLockPixels alp(bitmap);
689 REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass");
690 }
691}
halcanarya0cc8332014-08-01 10:31:48 -0700692
693
694////////////////////////////////////////////////////////////////////////////////
695namespace {
696class SingleAllocator : public SkBitmap::Allocator {
697public:
698 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
699 ~SingleAllocator() {}
700 // If the pixels in fPixels are big enough, use them.
mtklein36352bf2015-03-25 18:17:31 -0700701 bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) override {
halcanarya0cc8332014-08-01 10:31:48 -0700702 SkASSERT(bm);
703 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
704 bm->setPixels(fPixels, ct);
halcanary96fcdcc2015-08-27 07:41:13 -0700705 fPixels = nullptr;
halcanarya0cc8332014-08-01 10:31:48 -0700706 fSize = 0;
707 return true;
708 }
halcanary96fcdcc2015-08-27 07:41:13 -0700709 return bm->tryAllocPixels(nullptr, ct);
halcanarya0cc8332014-08-01 10:31:48 -0700710 }
halcanary96fcdcc2015-08-27 07:41:13 -0700711 bool ready() { return fPixels != nullptr; }
halcanarya0cc8332014-08-01 10:31:48 -0700712private:
713 void* fPixels;
714 size_t fSize;
715};
716} // namespace
717
718/* This tests for a bug in libjpeg where INT32 is typedefed to long
719 and memory can be written to outside of the array. */
720DEF_TEST(ImageDecoding_JpegOverwrite, r) {
721 SkString resourceDir = GetResourcePath();
722 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
scroggoa1193e42015-01-21 12:09:53 -0800723 SkAutoTDelete<SkStreamAsset> stream(
halcanarya0cc8332014-08-01 10:31:48 -0700724 SkStream::NewFromFile(path.c_str()));
725 if (!stream.get()) {
726 SkDebugf("\nPath '%s' missing.\n", path.c_str());
727 return;
728 }
729 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
halcanary96fcdcc2015-08-27 07:41:13 -0700730 if (nullptr == decoder.get()) {
halcanarya0cc8332014-08-01 10:31:48 -0700731 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
732 return;
733 }
734 SkAssertResult(stream->rewind());
735
736 static const uint16_t sentinal = 0xBEEF;
737 static const int pixelCount = 16;
738 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
739 // pixels.get() should be 4-byte aligned.
740 // This is necessary to reproduce the bug.
741
742 pixels[pixelCount] = sentinal; // This value should not be changed.
743
744 SkAutoTUnref<SingleAllocator> allocator(
halcanary385fe4d2015-08-26 13:07:48 -0700745 new SingleAllocator((void*)pixels.get(), sizeof(uint16_t) * pixelCount));
halcanarya0cc8332014-08-01 10:31:48 -0700746 decoder->setAllocator(allocator);
747 decoder->setSampleSize(2);
748 SkBitmap bitmap;
749 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700750 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanarya0cc8332014-08-01 10:31:48 -0700751 REPORTER_ASSERT(r, success);
752 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
753 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
754}