blob: b94e29c11d6fd6b7464dc6d4f787c2bf1b21b769 [file] [log] [blame]
scroggo@google.com2bbc2c92013-06-14 15:33:20 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
tfarinabcbc1782014-06-18 14:32:48 -07008#include "Resources.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +00009#include "SkBitmap.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000010#include "SkCanvas.h"
11#include "SkColor.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000012#include "SkColorPriv.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000013#include "SkData.h"
halcanary@google.comdedd44a2013-12-20 16:35:22 +000014#include "SkDecodingImageGenerator.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000015#include "SkDiscardableMemoryPool.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000016#include "SkForceLinking.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000017#include "SkGradientShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000018#include "SkImageDecoder.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000019#include "SkImageEncoder.h"
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +000020#include "SkImageGeneratorPriv.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000021#include "SkImagePriv.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000022#include "SkOSFile.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000023#include "SkPoint.h"
24#include "SkShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000025#include "SkStream.h"
26#include "SkString.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000027#include "Test.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000028
29__SK_FORCE_IMAGE_DECODER_LINKING;
30
31/**
32 * Interprets c as an unpremultiplied color, and returns the
33 * premultiplied equivalent.
34 */
35static SkPMColor premultiply_unpmcolor(SkPMColor c) {
36 U8CPU a = SkGetPackedA32(c);
37 U8CPU r = SkGetPackedR32(c);
38 U8CPU g = SkGetPackedG32(c);
39 U8CPU b = SkGetPackedB32(c);
40 return SkPreMultiplyARGB(a, r, g, b);
41}
42
43/**
44 * Return true if this stream format should be skipped, due
45 * to do being an opaque format or not a valid format.
46 */
47static bool skip_image_format(SkImageDecoder::Format format) {
48 switch (format) {
49 case SkImageDecoder::kPNG_Format:
50 case SkImageDecoder::kWEBP_Format:
51 return false;
52 // Skip unknown since it will not be decoded anyway.
53 case SkImageDecoder::kUnknown_Format:
54 // Technically ICO and BMP supports alpha channels, but our image
55 // decoders do not, so skip them as well.
56 case SkImageDecoder::kICO_Format:
57 case SkImageDecoder::kBMP_Format:
krajcevski95b1b3d2014-08-07 12:58:38 -070058 // KTX and ASTC are texture formats so it's not particularly clear how to
59 // decode the alpha from them.
krajcevski99ffe242014-06-03 13:04:35 -070060 case SkImageDecoder::kKTX_Format:
krajcevski95b1b3d2014-08-07 12:58:38 -070061 case SkImageDecoder::kASTC_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000062 // The rest of these are opaque.
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000063 case SkImageDecoder::kPKM_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000064 case SkImageDecoder::kWBMP_Format:
65 case SkImageDecoder::kGIF_Format:
66 case SkImageDecoder::kJPEG_Format:
67 return true;
68 }
69 SkASSERT(false);
70 return true;
71}
72
73/**
74 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
75 * them.
76 */
77static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
78 // Decode a resource:
79 SkBitmap bm8888;
80 SkBitmap bm8888Unpremul;
81
82 SkFILEStream stream(filename.c_str());
83
84 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
85 if (skip_image_format(format)) {
86 return;
87 }
88
89 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
90 if (NULL == decoder.get()) {
91 SkDebugf("couldn't decode %s\n", filename.c_str());
92 return;
93 }
94
reedbfefc7c2014-06-12 17:40:00 -070095 bool success = decoder->decode(&stream, &bm8888, kN32_SkColorType,
scroggo2a120802014-10-22 12:07:00 -070096 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000097 if (!success) {
98 return;
99 }
100
101 success = stream.rewind();
102 REPORTER_ASSERT(reporter, success);
103 if (!success) {
104 return;
105 }
106
107 decoder->setRequireUnpremultipliedColors(true);
reedbfefc7c2014-06-12 17:40:00 -0700108 success = decoder->decode(&stream, &bm8888Unpremul, kN32_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700109 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000110 if (!success) {
111 return;
112 }
113
114 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
115 && bm8888.height() == bm8888Unpremul.height();
116 REPORTER_ASSERT(reporter, dimensionsMatch);
117 if (!dimensionsMatch) {
118 return;
119 }
120
121 // Only do the comparison if the two bitmaps are both 8888.
reedbfefc7c2014-06-12 17:40:00 -0700122 if (bm8888.colorType() != kN32_SkColorType || bm8888Unpremul.colorType() != kN32_SkColorType) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000123 return;
124 }
125
126 // Now compare the two bitmaps.
127 for (int i = 0; i < bm8888.width(); ++i) {
128 for (int j = 0; j < bm8888.height(); ++j) {
129 // "c0" is the color of the premultiplied bitmap at (i, j).
130 const SkPMColor c0 = *bm8888.getAddr32(i, j);
131 // "c1" is the result of premultiplying the color of the unpremultiplied
132 // bitmap at (i, j).
133 const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
134 // Compute the difference for each component.
135 int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
136 int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
137 int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
138 int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
139
140 // Alpha component must be exactly the same.
141 REPORTER_ASSERT(reporter, 0 == da);
scroggo@google.comdaaea2d2013-06-14 20:39:48 +0000142
143 // Color components may not match exactly due to rounding error.
144 REPORTER_ASSERT(reporter, dr <= 1);
145 REPORTER_ASSERT(reporter, dg <= 1);
146 REPORTER_ASSERT(reporter, db <= 1);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000147 }
148 }
149}
150
scroggo@google.comf698c822013-07-18 19:34:49 +0000151static void test_unpremul(skiatest::Reporter* reporter) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000152 // This test cannot run if there is no resource path.
tfarinabcbc1782014-06-18 14:32:48 -0700153 SkString resourcePath = GetResourcePath();
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000154 if (resourcePath.isEmpty()) {
bungeman@google.com3c996f82013-10-24 21:39:35 +0000155 SkDebugf("Could not run unpremul test because resourcePath not specified.");
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000156 return;
157 }
158 SkOSFile::Iter iter(resourcePath.c_str());
159 SkString basename;
160 if (iter.next(&basename)) {
161 do {
tfarinaa8e2e152014-07-28 19:26:58 -0700162 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000163 // SkDebugf("about to decode \"%s\"\n", filename.c_str());
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000164 compare_unpremul(reporter, filename);
165 } while (iter.next(&basename));
166 } else {
167 SkDebugf("Failed to find any files :(\n");
168 }
169}
170
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000171#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
172// Test that the alpha type is what we expect.
173static void test_alphaType(skiatest::Reporter* reporter, const SkString& filename,
174 bool requireUnpremul) {
175 SkBitmap bm;
176 SkFILEStream stream(filename.c_str());
177
178 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
179 if (NULL == decoder.get()) {
180 return;
181 }
182
183 decoder->setRequireUnpremultipliedColors(requireUnpremul);
184
185 // Decode just the bounds. This should always succeed.
reedbfefc7c2014-06-12 17:40:00 -0700186 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000187 SkImageDecoder::kDecodeBounds_Mode);
188 REPORTER_ASSERT(reporter, success);
189 if (!success) {
190 return;
191 }
192
193 // Keep track of the alpha type for testing later. If the full decode
194 // succeeds, the alpha type should be the same, unless the full decode
195 // determined that the alpha type should actually be opaque, which may
196 // not be known when only decoding the bounds.
197 const SkAlphaType boundsAlphaType = bm.alphaType();
198
199 // rewind should always succeed on SkFILEStream.
200 success = stream.rewind();
201 REPORTER_ASSERT(reporter, success);
202 if (!success) {
203 return;
204 }
205
reedbfefc7c2014-06-12 17:40:00 -0700206 success = decoder->decode(&stream, &bm, kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode);
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000207
208 if (!success) {
209 // When the decoder is set to require unpremul, if it does not support
210 // unpremul it will fail. This is the only reason the decode should
211 // fail (since we know the files we are using to test can be decoded).
212 REPORTER_ASSERT(reporter, requireUnpremul);
213 return;
214 }
215
216 // The bounds decode should return with either the requested
217 // premul/unpremul or opaque, if that value could be determined when only
218 // decoding the bounds.
219 if (requireUnpremul) {
220 REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType
221 || kOpaque_SkAlphaType == boundsAlphaType);
222 } else {
223 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
224 || kOpaque_SkAlphaType == boundsAlphaType);
225 }
226
227 // When decoding the full image, the alpha type should match the one
228 // returned by the bounds decode, unless the full decode determined that
229 // the alpha type is actually opaque.
230 REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
231 || bm.alphaType() == kOpaque_SkAlphaType);
232}
233
234DEF_TEST(ImageDecoding_alphaType, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700235 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000236 if (resourcePath.isEmpty()) {
237 SkDebugf("Could not run alphaType test because resourcePath not specified.");
238 return;
239 }
240
241 SkOSFile::Iter iter(resourcePath.c_str());
242 SkString basename;
243 if (iter.next(&basename)) {
244 do {
tfarinaa8e2e152014-07-28 19:26:58 -0700245 SkString filename = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000246 for (int truth = 0; truth <= 1; ++truth) {
247 test_alphaType(reporter, filename, SkToBool(truth));
248 }
249 } while (iter.next(&basename));
250 } else {
251 SkDebugf("Failed to find any files :(\n");
252 }
253
254}
255
256// Using known images, test that decoding into unpremul and premul behave as expected.
257DEF_TEST(ImageDecoding_unpremul, reporter) {
tfarinabcbc1782014-06-18 14:32:48 -0700258 SkString resourcePath = GetResourcePath();
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000259 if (resourcePath.isEmpty()) {
260 SkDebugf("Could not run unpremul test because resourcePath not specified.");
261 return;
262 }
263 const char* root = "half-transparent-white-pixel";
264 const char* suffixes[] = { ".png", ".webp" };
265
266 for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
267 SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
tfarinaa8e2e152014-07-28 19:26:58 -0700268 SkString fullName = SkOSPath::Join(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000269
270 SkBitmap bm;
271 SkFILEStream stream(fullName.c_str());
272
273 if (!stream.isValid()) {
274 SkDebugf("file %s missing from resource directoy %s\n",
275 basename.c_str(), resourcePath.c_str());
276 continue;
277 }
278
279 // This should never fail since we know the images we're decoding.
280 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
bsalomon49f085d2014-09-05 13:34:00 -0700281 REPORTER_ASSERT(reporter, decoder.get());
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000282 if (NULL == decoder.get()) {
283 continue;
284 }
285
286 // Test unpremultiplied. We know what color this should result in.
287 decoder->setRequireUnpremultipliedColors(true);
reedbfefc7c2014-06-12 17:40:00 -0700288 bool success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000289 SkImageDecoder::kDecodePixels_Mode);
290 REPORTER_ASSERT(reporter, success);
291 if (!success) {
292 continue;
293 }
294
295 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
296 {
297 SkAutoLockPixels alp(bm);
298 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
299 }
300
301 success = stream.rewind();
302 REPORTER_ASSERT(reporter, success);
303 if (!success) {
304 continue;
305 }
306
307 // Test premultiplied. Once again, we know which color this should
308 // result in.
309 decoder->setRequireUnpremultipliedColors(false);
reedbfefc7c2014-06-12 17:40:00 -0700310 success = decoder->decode(&stream, &bm, kN32_SkColorType,
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000311 SkImageDecoder::kDecodePixels_Mode);
312 REPORTER_ASSERT(reporter, success);
313 if (!success) {
314 continue;
315 }
316
317 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
318 {
319 SkAutoLockPixels alp(bm);
320 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
321 }
322 }
323}
324#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
325
scroggo@google.com826d63a2013-07-18 20:06:28 +0000326#ifdef SK_DEBUG
scroggo@google.com8d239242013-10-01 17:27:15 +0000327// Test inside SkScaledBitmapSampler.cpp
328extern void test_row_proc_choice();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000329#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000330
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000331DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000332 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000333#ifdef SK_DEBUG
scroggo@google.com8d239242013-10-01 17:27:15 +0000334 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000335#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000336}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000337
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000338// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000339static const int kExpectedWidth = 8;
340static const int kExpectedHeight = 8;
341static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000342 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
343 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
344 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
345 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
346 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
347 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
348 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
349 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
350 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
351 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
352 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
353 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
354 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
355 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
356 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
357 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
358};
359SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
360 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000361
362DEF_TEST(WebP, reporter) {
363 const unsigned char encodedWebP[] = {
364 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
365 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
366 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
367 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
368 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
369 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
370 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
371 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
372 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
373 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
374 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
375 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
376 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
377 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
378 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
379 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
380 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
381 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
382 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
383 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
384 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
385 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
386 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
387 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
388 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
389 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
390 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000391 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
392 sizeof(encodedWebP)));
393 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000394
reed5965c8a2015-01-07 18:04:45 -0800395 bool success = SkInstallDiscardablePixelRef(encoded, &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000396
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000397 REPORTER_ASSERT(reporter, success);
398 if (!success) {
399 return;
400 }
401 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000402
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000403 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000404 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000405 REPORTER_ASSERT(reporter, rightSize);
406 if (rightSize) {
407 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000408 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000409 for (int y = 0; y < bm.height(); ++y) {
410 for (int x = 0; x < bm.width(); ++x) {
411 error |= (*correctPixel != bm.getColor(x, y));
412 ++correctPixel;
413 }
414 }
415 REPORTER_ASSERT(reporter, !error);
416 }
417}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000418
419////////////////////////////////////////////////////////////////////////////////
420
421// example of how Android will do this inside their BitmapFactory
422static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
423 SkStreamRewindable* stream,
424 int sampleSize, bool ditherImage) {
425 SkASSERT(bitmap != NULL);
426 SkASSERT(stream != NULL);
427 SkASSERT(stream->rewind());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000428 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000429 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000430 if (SkInstallDiscardablePixelRef(
431 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000432 return bitmap->pixelRef();
433 }
434 return NULL;
435}
436/**
437 * A test for the SkDecodingImageGenerator::Create and
438 * SkInstallDiscardablePixelRef functions.
439 */
440DEF_TEST(ImprovedBitmapFactory, reporter) {
tfarinac846f4a2014-07-01 12:35:49 -0700441 SkString pngFilename = GetResourcePath("randPixels.png");
scroggoa1193e42015-01-21 12:09:53 -0800442 SkAutoTDelete<SkStreamRewindable> stream(SkStream::NewFromFile(pngFilename.c_str()));
tfarinac846f4a2014-07-01 12:35:49 -0700443 if (sk_exists(pngFilename.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000444 SkBitmap bm;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000445 SkAssertResult(bm.setInfo(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000446 REPORTER_ASSERT(reporter,
bsalomon49f085d2014-09-05 13:34:00 -0700447 install_pixel_ref(&bm, stream.detach(), 1, true));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000448 SkAutoLockPixels alp(bm);
bsalomon49f085d2014-09-05 13:34:00 -0700449 REPORTER_ASSERT(reporter, bm.getPixels());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000450 }
451}
452
453
454////////////////////////////////////////////////////////////////////////////////
455
456#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
457static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000458 // returns true if the value is greater than floor(dividend/divisor)
459 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000460 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000461 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000462}
463#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
464
465
466#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
467 #define kBackwards_SkColorType kRGBA_8888_SkColorType
468#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
469 #define kBackwards_SkColorType kBGRA_8888_SkColorType
470#else
471 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
472#endif
473
474static inline const char* SkColorType_to_string(SkColorType colorType) {
475 switch(colorType) {
476 case kAlpha_8_SkColorType: return "Alpha_8";
477 case kRGB_565_SkColorType: return "RGB_565";
478 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000479 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000480 case kBackwards_SkColorType: return "Backwards";
481 case kIndex_8_SkColorType: return "Index_8";
482 default: return "ERROR";
483 }
484}
485
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000486static inline const char* options_colorType(
487 const SkDecodingImageGenerator::Options& opts) {
488 if (opts.fUseRequestedColorType) {
489 return SkColorType_to_string(opts.fRequestedColorType);
490 } else {
491 return "(none)";
492 }
493}
494
495static inline const char* yn(bool value) {
496 if (value) {
497 return "yes";
498 } else {
499 return "no";
500 }
501}
502
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000503/**
504 * Given either a SkStream or a SkData, try to decode the encoded
505 * image using the specified options and report errors.
506 */
507static void test_options(skiatest::Reporter* reporter,
508 const SkDecodingImageGenerator::Options& opts,
509 SkStreamRewindable* encodedStream,
510 SkData* encodedData,
511 bool useData,
512 const SkString& path) {
513 SkBitmap bm;
514 bool success = false;
515 if (useData) {
516 if (NULL == encodedData) {
517 return;
518 }
519 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000520 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000521 } else {
522 if (NULL == encodedStream) {
523 return;
524 }
525 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000526 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000527 }
528 if (!success) {
529 if (opts.fUseRequestedColorType
530 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
531 return; // Ignore known conversion inabilities.
532 }
533 // If we get here, it's a failure and we will need more
534 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000535 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
536 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
537 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000538 return;
539 }
540 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
541 // Android is the only system that use Skia's image decoders in
542 // production. For now, we'll only verify that samplesize works
543 // on systems where it already is known to work.
544 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
545 opts.fSampleSize));
546 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
547 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000548 // The ImageDecoder API doesn't guarantee that SampleSize does
549 // anything at all, but the decoders that this test excercises all
550 // produce an output size in the following range:
551 // (((sample_size * out_size) > (in_size - sample_size))
552 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000553 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
554 SkAutoLockPixels alp(bm);
555 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000556 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
557 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
558 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000559 return;
560 }
561
reedbfefc7c2014-06-12 17:40:00 -0700562 SkColorType requestedColorType = opts.fRequestedColorType;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000563 REPORTER_ASSERT(reporter,
564 (!opts.fUseRequestedColorType)
reedbfefc7c2014-06-12 17:40:00 -0700565 || (bm.colorType() == requestedColorType));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000566
567 // Condition under which we should check the decoding results:
reedbfefc7c2014-06-12 17:40:00 -0700568 if ((kN32_SkColorType == bm.colorType())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000569 && (!path.endsWith(".jpg")) // lossy
570 && (opts.fSampleSize == 1)) { // scaled
571 const SkColor* correctPixels = kExpectedPixels;
572 SkASSERT(bm.height() == kExpectedHeight);
573 SkASSERT(bm.width() == kExpectedWidth);
574 int pixelErrors = 0;
575 for (int y = 0; y < bm.height(); ++y) {
576 for (int x = 0; x < bm.width(); ++x) {
577 if (*correctPixels != bm.getColor(x, y)) {
578 ++pixelErrors;
579 }
580 ++correctPixels;
581 }
582 }
583 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000584 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
585 "[sampleSize=%d dither=%s colorType=%s %s]",
586 pixelErrors, kExpectedHeight * kExpectedWidth,
587 opts.fSampleSize, yn(opts.fDitherImage),
588 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000589 }
590 }
591}
592
593/**
594 * SkDecodingImageGenerator has an Options struct which lets the
595 * client of the generator set sample size, dithering, and bitmap
596 * config. This test loops through many possible options and tries
597 * them on a set of 5 small encoded images (each in a different
598 * format). We test both SkData and SkStreamRewindable decoding.
599 */
600DEF_TEST(ImageDecoderOptions, reporter) {
601 const char* files[] = {
602 "randPixels.bmp",
603 "randPixels.jpg",
604 "randPixels.png",
605 "randPixels.webp",
606 #if !defined(SK_BUILD_FOR_WIN)
607 // TODO(halcanary): Find out why this fails sometimes.
608 "randPixels.gif",
609 #endif
610 };
611
tfarinabcbc1782014-06-18 14:32:48 -0700612 SkString resourceDir = GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000613 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000614 return;
615 }
616
617 int scaleList[] = {1, 2, 3, 4};
618 bool ditherList[] = {true, false};
619 SkColorType colorList[] = {
620 kAlpha_8_SkColorType,
621 kRGB_565_SkColorType,
622 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000623 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000624 // Note that indexed color is left out of the list. Lazy
625 // decoding doesn't do indexed color.
626 };
627 const bool useDataList[] = {true, false};
628
629 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
tfarinaa8e2e152014-07-28 19:26:58 -0700630 SkString path = SkOSPath::Join(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000631 if (!sk_exists(path.c_str())) {
632 continue;
633 }
634
635 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
636 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
scroggoa1193e42015-01-21 12:09:53 -0800637 SkAutoTDelete<SkStreamRewindable> encodedStream(
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000638 SkStream::NewFromFile(path.c_str()));
639 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
640
641 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
642 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
643 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
644 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
645 SkDecodingImageGenerator::Options opts(scaleList[i],
646 ditherList[j],
647 colorList[k]);
648 test_options(reporter, opts, encodedStream, encodedData,
649 useDataList[m], path);
650
651 }
652 SkDecodingImageGenerator::Options options(scaleList[i],
653 ditherList[j]);
654 test_options(reporter, options, encodedStream, encodedData,
655 useDataList[m], path);
656 }
657 }
658 }
659 }
660}
halcanary7f8aad82014-07-28 08:23:55 -0700661
662DEF_TEST(DiscardablePixelRef_SecondLockColorTableCheck, r) {
663 SkString resourceDir = GetResourcePath();
tfarinaa8e2e152014-07-28 19:26:58 -0700664 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.gif");
halcanary7f8aad82014-07-28 08:23:55 -0700665 if (!sk_exists(path.c_str())) {
666 return;
667 }
668 SkAutoDataUnref encoded(SkData::NewFromFileName(path.c_str()));
669 SkBitmap bitmap;
670 if (!SkInstallDiscardablePixelRef(
671 SkDecodingImageGenerator::Create(
672 encoded, SkDecodingImageGenerator::Options()), &bitmap)) {
halcanary186e6992014-07-28 10:54:26 -0700673 #ifndef SK_BUILD_FOR_WIN
halcanary7f8aad82014-07-28 08:23:55 -0700674 ERRORF(r, "SkInstallDiscardablePixelRef [randPixels.gif] failed.");
halcanary186e6992014-07-28 10:54:26 -0700675 #endif
halcanary7f8aad82014-07-28 08:23:55 -0700676 return;
677 }
halcanaryb1ab5fd2014-07-28 08:56:35 -0700678 if (kIndex_8_SkColorType != bitmap.colorType()) {
679 return;
680 }
halcanary7f8aad82014-07-28 08:23:55 -0700681 {
682 SkAutoLockPixels alp(bitmap);
683 REPORTER_ASSERT(r, bitmap.getColorTable() && "first pass");
684 }
685 {
686 SkAutoLockPixels alp(bitmap);
687 REPORTER_ASSERT(r, bitmap.getColorTable() && "second pass");
688 }
689}
halcanarya0cc8332014-08-01 10:31:48 -0700690
691
692////////////////////////////////////////////////////////////////////////////////
693namespace {
694class SingleAllocator : public SkBitmap::Allocator {
695public:
696 SingleAllocator(void* p, size_t s) : fPixels(p), fSize(s) { }
697 ~SingleAllocator() {}
698 // If the pixels in fPixels are big enough, use them.
mtklein72c9faa2015-01-09 10:06:39 -0800699 bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
halcanarya0cc8332014-08-01 10:31:48 -0700700 SkASSERT(bm);
701 if (bm->info().getSafeSize(bm->rowBytes()) <= fSize) {
702 bm->setPixels(fPixels, ct);
703 fPixels = NULL;
704 fSize = 0;
705 return true;
706 }
reed84825042014-09-02 12:50:45 -0700707 return bm->tryAllocPixels(NULL, ct);
halcanarya0cc8332014-08-01 10:31:48 -0700708 }
709 bool ready() { return fPixels != NULL; }
710private:
711 void* fPixels;
712 size_t fSize;
713};
714} // namespace
715
716/* This tests for a bug in libjpeg where INT32 is typedefed to long
717 and memory can be written to outside of the array. */
718DEF_TEST(ImageDecoding_JpegOverwrite, r) {
719 SkString resourceDir = GetResourcePath();
720 SkString path = SkOSPath::Join(resourceDir.c_str(), "randPixels.jpg");
scroggoa1193e42015-01-21 12:09:53 -0800721 SkAutoTDelete<SkStreamAsset> stream(
halcanarya0cc8332014-08-01 10:31:48 -0700722 SkStream::NewFromFile(path.c_str()));
723 if (!stream.get()) {
724 SkDebugf("\nPath '%s' missing.\n", path.c_str());
725 return;
726 }
727 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
728 if (NULL == decoder.get()) {
729 ERRORF(r, "\nSkImageDecoder::Factory failed.\n");
730 return;
731 }
732 SkAssertResult(stream->rewind());
733
734 static const uint16_t sentinal = 0xBEEF;
735 static const int pixelCount = 16;
736 SkAutoTMalloc<uint16_t> pixels(pixelCount + 1);
737 // pixels.get() should be 4-byte aligned.
738 // This is necessary to reproduce the bug.
739
740 pixels[pixelCount] = sentinal; // This value should not be changed.
741
742 SkAutoTUnref<SingleAllocator> allocator(
743 SkNEW_ARGS(SingleAllocator,
744 ((void*)pixels.get(), sizeof(uint16_t) * pixelCount)));
745 decoder->setAllocator(allocator);
746 decoder->setSampleSize(2);
747 SkBitmap bitmap;
748 bool success = decoder->decode(stream, &bitmap, kRGB_565_SkColorType,
scroggo2a120802014-10-22 12:07:00 -0700749 SkImageDecoder::kDecodePixels_Mode) != SkImageDecoder::kFailure;
halcanarya0cc8332014-08-01 10:31:48 -0700750 REPORTER_ASSERT(r, success);
751 REPORTER_ASSERT(r, !allocator->ready()); // Decoder used correct memory
752 REPORTER_ASSERT(r, sentinal == pixels[pixelCount]);
753}