blob: 21c774727d86d6286f3d2881b26ce0594644af9a [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
8#include "SkBitmap.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +00009#include "SkCanvas.h"
10#include "SkColor.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000011#include "SkColorPriv.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000012#include "SkData.h"
halcanary@google.comdedd44a2013-12-20 16:35:22 +000013#include "SkDecodingImageGenerator.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000014#include "SkDiscardableMemoryPool.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000015#include "SkForceLinking.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000016#include "SkGradientShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000017#include "SkImageDecoder.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000018#include "SkImageEncoder.h"
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +000019#include "SkImageGeneratorPriv.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000020#include "SkImagePriv.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000021#include "SkOSFile.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000022#include "SkPoint.h"
23#include "SkShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000024#include "SkStream.h"
25#include "SkString.h"
tfarina@chromium.org8f6884a2014-01-24 20:56:26 +000026#include "Test.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000027
28__SK_FORCE_IMAGE_DECODER_LINKING;
29
30/**
31 * Interprets c as an unpremultiplied color, and returns the
32 * premultiplied equivalent.
33 */
34static SkPMColor premultiply_unpmcolor(SkPMColor c) {
35 U8CPU a = SkGetPackedA32(c);
36 U8CPU r = SkGetPackedR32(c);
37 U8CPU g = SkGetPackedG32(c);
38 U8CPU b = SkGetPackedB32(c);
39 return SkPreMultiplyARGB(a, r, g, b);
40}
41
42/**
43 * Return true if this stream format should be skipped, due
44 * to do being an opaque format or not a valid format.
45 */
46static bool skip_image_format(SkImageDecoder::Format format) {
47 switch (format) {
48 case SkImageDecoder::kPNG_Format:
49 case SkImageDecoder::kWEBP_Format:
50 return false;
51 // Skip unknown since it will not be decoded anyway.
52 case SkImageDecoder::kUnknown_Format:
53 // Technically ICO and BMP supports alpha channels, but our image
54 // decoders do not, so skip them as well.
55 case SkImageDecoder::kICO_Format:
56 case SkImageDecoder::kBMP_Format:
57 // The rest of these are opaque.
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000058 case SkImageDecoder::kPKM_Format:
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000059 case SkImageDecoder::kWBMP_Format:
60 case SkImageDecoder::kGIF_Format:
61 case SkImageDecoder::kJPEG_Format:
62 return true;
63 }
64 SkASSERT(false);
65 return true;
66}
67
68/**
69 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
70 * them.
71 */
72static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
73 // Decode a resource:
74 SkBitmap bm8888;
75 SkBitmap bm8888Unpremul;
76
77 SkFILEStream stream(filename.c_str());
78
79 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
80 if (skip_image_format(format)) {
81 return;
82 }
83
84 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
85 if (NULL == decoder.get()) {
86 SkDebugf("couldn't decode %s\n", filename.c_str());
87 return;
88 }
89
90 bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config,
91 SkImageDecoder::kDecodePixels_Mode);
92 if (!success) {
93 return;
94 }
95
96 success = stream.rewind();
97 REPORTER_ASSERT(reporter, success);
98 if (!success) {
99 return;
100 }
101
102 decoder->setRequireUnpremultipliedColors(true);
103 success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config,
104 SkImageDecoder::kDecodePixels_Mode);
105 if (!success) {
106 return;
107 }
108
109 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
110 && bm8888.height() == bm8888Unpremul.height();
111 REPORTER_ASSERT(reporter, dimensionsMatch);
112 if (!dimensionsMatch) {
113 return;
114 }
115
116 // Only do the comparison if the two bitmaps are both 8888.
117 if (bm8888.config() != SkBitmap::kARGB_8888_Config
118 || bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) {
119 return;
120 }
121
122 // Now compare the two bitmaps.
123 for (int i = 0; i < bm8888.width(); ++i) {
124 for (int j = 0; j < bm8888.height(); ++j) {
125 // "c0" is the color of the premultiplied bitmap at (i, j).
126 const SkPMColor c0 = *bm8888.getAddr32(i, j);
127 // "c1" is the result of premultiplying the color of the unpremultiplied
128 // bitmap at (i, j).
129 const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
130 // Compute the difference for each component.
131 int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
132 int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
133 int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
134 int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
135
136 // Alpha component must be exactly the same.
137 REPORTER_ASSERT(reporter, 0 == da);
scroggo@google.comdaaea2d2013-06-14 20:39:48 +0000138
139 // Color components may not match exactly due to rounding error.
140 REPORTER_ASSERT(reporter, dr <= 1);
141 REPORTER_ASSERT(reporter, dg <= 1);
142 REPORTER_ASSERT(reporter, db <= 1);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000143 }
144 }
145}
146
scroggo@google.comf698c822013-07-18 19:34:49 +0000147static void test_unpremul(skiatest::Reporter* reporter) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000148 // This test cannot run if there is no resource path.
149 SkString resourcePath = skiatest::Test::GetResourcePath();
150 if (resourcePath.isEmpty()) {
bungeman@google.com3c996f82013-10-24 21:39:35 +0000151 SkDebugf("Could not run unpremul test because resourcePath not specified.");
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000152 return;
153 }
154 SkOSFile::Iter iter(resourcePath.c_str());
155 SkString basename;
156 if (iter.next(&basename)) {
157 do {
158 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000159 // SkDebugf("about to decode \"%s\"\n", filename.c_str());
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000160 compare_unpremul(reporter, filename);
161 } while (iter.next(&basename));
162 } else {
163 SkDebugf("Failed to find any files :(\n");
164 }
165}
166
commit-bot@chromium.org915b9722014-04-24 18:55:13 +0000167#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
168// Test that the alpha type is what we expect.
169static void test_alphaType(skiatest::Reporter* reporter, const SkString& filename,
170 bool requireUnpremul) {
171 SkBitmap bm;
172 SkFILEStream stream(filename.c_str());
173
174 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
175 if (NULL == decoder.get()) {
176 return;
177 }
178
179 decoder->setRequireUnpremultipliedColors(requireUnpremul);
180
181 // Decode just the bounds. This should always succeed.
182 bool success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
183 SkImageDecoder::kDecodeBounds_Mode);
184 REPORTER_ASSERT(reporter, success);
185 if (!success) {
186 return;
187 }
188
189 // Keep track of the alpha type for testing later. If the full decode
190 // succeeds, the alpha type should be the same, unless the full decode
191 // determined that the alpha type should actually be opaque, which may
192 // not be known when only decoding the bounds.
193 const SkAlphaType boundsAlphaType = bm.alphaType();
194
195 // rewind should always succeed on SkFILEStream.
196 success = stream.rewind();
197 REPORTER_ASSERT(reporter, success);
198 if (!success) {
199 return;
200 }
201
202 success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
203 SkImageDecoder::kDecodePixels_Mode);
204
205 if (!success) {
206 // When the decoder is set to require unpremul, if it does not support
207 // unpremul it will fail. This is the only reason the decode should
208 // fail (since we know the files we are using to test can be decoded).
209 REPORTER_ASSERT(reporter, requireUnpremul);
210 return;
211 }
212
213 // The bounds decode should return with either the requested
214 // premul/unpremul or opaque, if that value could be determined when only
215 // decoding the bounds.
216 if (requireUnpremul) {
217 REPORTER_ASSERT(reporter, kUnpremul_SkAlphaType == boundsAlphaType
218 || kOpaque_SkAlphaType == boundsAlphaType);
219 } else {
220 REPORTER_ASSERT(reporter, kPremul_SkAlphaType == boundsAlphaType
221 || kOpaque_SkAlphaType == boundsAlphaType);
222 }
223
224 // When decoding the full image, the alpha type should match the one
225 // returned by the bounds decode, unless the full decode determined that
226 // the alpha type is actually opaque.
227 REPORTER_ASSERT(reporter, bm.alphaType() == boundsAlphaType
228 || bm.alphaType() == kOpaque_SkAlphaType);
229}
230
231DEF_TEST(ImageDecoding_alphaType, reporter) {
232 SkString resourcePath = skiatest::Test::GetResourcePath();
233 if (resourcePath.isEmpty()) {
234 SkDebugf("Could not run alphaType test because resourcePath not specified.");
235 return;
236 }
237
238 SkOSFile::Iter iter(resourcePath.c_str());
239 SkString basename;
240 if (iter.next(&basename)) {
241 do {
242 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
243 for (int truth = 0; truth <= 1; ++truth) {
244 test_alphaType(reporter, filename, SkToBool(truth));
245 }
246 } while (iter.next(&basename));
247 } else {
248 SkDebugf("Failed to find any files :(\n");
249 }
250
251}
252
253// Using known images, test that decoding into unpremul and premul behave as expected.
254DEF_TEST(ImageDecoding_unpremul, reporter) {
255 SkString resourcePath = skiatest::Test::GetResourcePath();
256 if (resourcePath.isEmpty()) {
257 SkDebugf("Could not run unpremul test because resourcePath not specified.");
258 return;
259 }
260 const char* root = "half-transparent-white-pixel";
261 const char* suffixes[] = { ".png", ".webp" };
262
263 for (size_t i = 0; i < SK_ARRAY_COUNT(suffixes); ++i) {
264 SkString basename = SkStringPrintf("%s%s", root, suffixes[i]);
265 SkString fullName = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
266
267 SkBitmap bm;
268 SkFILEStream stream(fullName.c_str());
269
270 if (!stream.isValid()) {
271 SkDebugf("file %s missing from resource directoy %s\n",
272 basename.c_str(), resourcePath.c_str());
273 continue;
274 }
275
276 // This should never fail since we know the images we're decoding.
277 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
278 REPORTER_ASSERT(reporter, NULL != decoder.get());
279 if (NULL == decoder.get()) {
280 continue;
281 }
282
283 // Test unpremultiplied. We know what color this should result in.
284 decoder->setRequireUnpremultipliedColors(true);
285 bool success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
286 SkImageDecoder::kDecodePixels_Mode);
287 REPORTER_ASSERT(reporter, success);
288 if (!success) {
289 continue;
290 }
291
292 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
293 {
294 SkAutoLockPixels alp(bm);
295 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7fffffff);
296 }
297
298 success = stream.rewind();
299 REPORTER_ASSERT(reporter, success);
300 if (!success) {
301 continue;
302 }
303
304 // Test premultiplied. Once again, we know which color this should
305 // result in.
306 decoder->setRequireUnpremultipliedColors(false);
307 success = decoder->decode(&stream, &bm, SkBitmap::kARGB_8888_Config,
308 SkImageDecoder::kDecodePixels_Mode);
309 REPORTER_ASSERT(reporter, success);
310 if (!success) {
311 continue;
312 }
313
314 REPORTER_ASSERT(reporter, bm.width() == 1 && bm.height() == 1);
315 {
316 SkAutoLockPixels alp(bm);
317 REPORTER_ASSERT(reporter, bm.getAddr32(0, 0)[0] == 0x7f7f7f7f);
318 }
319 }
320}
321#endif // SK_BUILD_FOR_UNIX/ANDROID skbug.com/2388
322
scroggo@google.com826d63a2013-07-18 20:06:28 +0000323#ifdef SK_DEBUG
324// Create a stream containing a bitmap encoded to Type type.
scroggo@google.comb5571b32013-09-25 21:34:24 +0000325static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000326 SkBitmap bm;
327 const int size = 50;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000328 bm.allocN32Pixels(size, size);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000329 SkCanvas canvas(bm);
330 SkPoint points[2] = {
331 { SkIntToScalar(0), SkIntToScalar(0) },
332 { SkIntToScalar(size), SkIntToScalar(size) }
333 };
334 SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
335 SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
336 SK_ARRAY_COUNT(colors),
337 SkShader::kClamp_TileMode);
338 SkPaint paint;
339 paint.setShader(shader)->unref();
340 canvas.drawPaint(paint);
341 // Now encode it to a stream.
342 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
343 if (NULL == data.get()) {
344 return NULL;
345 }
346 return SkNEW_ARGS(SkMemoryStream, (data.get()));
347}
348
349// For every format that supports tile based decoding, ensure that
350// calling decodeSubset will not fail if the caller has unreffed the
351// stream provided in buildTileIndex.
352// Only runs in debug mode since we are testing for a crash.
353static void test_stream_life() {
354 const SkImageEncoder::Type gTypes[] = {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000355#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comd79277f2013-08-07 19:53:53 +0000356 SkImageEncoder::kJPEG_Type,
scroggo@google.com826d63a2013-07-18 20:06:28 +0000357 SkImageEncoder::kPNG_Type,
358#endif
359 SkImageEncoder::kWEBP_Type,
360 };
361 for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000362 // SkDebugf("encoding to %i\n", i);
scroggo@google.comb5571b32013-09-25 21:34:24 +0000363 SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
scroggo@google.com826d63a2013-07-18 20:06:28 +0000364 if (NULL == stream.get()) {
365 SkDebugf("no stream\n");
366 continue;
367 }
368 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
369 if (NULL == decoder.get()) {
370 SkDebugf("no decoder\n");
371 continue;
372 }
373 int width, height;
374 if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
375 SkDebugf("could not build a tile index\n");
376 continue;
377 }
378 // Now unref the stream to make sure it survives
379 stream.reset(NULL);
380 SkBitmap bm;
381 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height),
382 SkBitmap::kARGB_8888_Config);
383 }
384}
scroggo@google.com8d239242013-10-01 17:27:15 +0000385
386// Test inside SkScaledBitmapSampler.cpp
387extern void test_row_proc_choice();
388
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000389#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000390
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000391DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000392 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000393#ifdef SK_DEBUG
394 test_stream_life();
scroggo@google.com8d239242013-10-01 17:27:15 +0000395 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000396#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000397}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000398
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000399// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000400static const int kExpectedWidth = 8;
401static const int kExpectedHeight = 8;
402static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000403 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
404 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
405 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
406 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
407 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
408 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
409 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
410 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
411 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
412 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
413 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
414 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
415 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
416 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
417 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
418 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
419};
420SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
421 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000422
423DEF_TEST(WebP, reporter) {
424 const unsigned char encodedWebP[] = {
425 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
426 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
427 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
428 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
429 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
430 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
431 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
432 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
433 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
434 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
435 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
436 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
437 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
438 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
439 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
440 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
441 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
442 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
443 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
444 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
445 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
446 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
447 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
448 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
449 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
450 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
451 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000452 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
453 sizeof(encodedWebP)));
454 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000455
456 bool success = SkInstallDiscardablePixelRef(
457 SkDecodingImageGenerator::Create(encoded,
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000458 SkDecodingImageGenerator::Options()), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000459
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000460 REPORTER_ASSERT(reporter, success);
461 if (!success) {
462 return;
463 }
464 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000465
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000466 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000467 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000468 REPORTER_ASSERT(reporter, rightSize);
469 if (rightSize) {
470 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000471 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000472 for (int y = 0; y < bm.height(); ++y) {
473 for (int x = 0; x < bm.width(); ++x) {
474 error |= (*correctPixel != bm.getColor(x, y));
475 ++correctPixel;
476 }
477 }
478 REPORTER_ASSERT(reporter, !error);
479 }
480}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000481
482////////////////////////////////////////////////////////////////////////////////
483
484// example of how Android will do this inside their BitmapFactory
485static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
486 SkStreamRewindable* stream,
487 int sampleSize, bool ditherImage) {
488 SkASSERT(bitmap != NULL);
489 SkASSERT(stream != NULL);
490 SkASSERT(stream->rewind());
491 SkASSERT(stream->unique());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000492 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000493 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
commit-bot@chromium.orge5a8e662014-05-23 20:25:15 +0000494 if (SkInstallDiscardablePixelRef(
495 SkDecodingImageGenerator::Create(stream, opts), bitmap)) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000496 return bitmap->pixelRef();
497 }
498 return NULL;
499}
500/**
501 * A test for the SkDecodingImageGenerator::Create and
502 * SkInstallDiscardablePixelRef functions.
503 */
504DEF_TEST(ImprovedBitmapFactory, reporter) {
505 SkString resourcePath = skiatest::Test::GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000506 SkString path = SkOSPath::SkPathJoin(
507 resourcePath.c_str(), "randPixels.png");
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000508 SkAutoTUnref<SkStreamRewindable> stream(
509 SkStream::NewFromFile(path.c_str()));
510 if (sk_exists(path.c_str())) {
511 SkBitmap bm;
mike@reedtribe.orgdeee4962014-02-13 14:41:43 +0000512 SkAssertResult(bm.setConfig(SkImageInfo::MakeN32Premul(1, 1)));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000513 REPORTER_ASSERT(reporter,
514 NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
515 SkAutoLockPixels alp(bm);
516 REPORTER_ASSERT(reporter, NULL != bm.getPixels());
517 }
518}
519
520
521////////////////////////////////////////////////////////////////////////////////
522
523#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
524static inline bool check_rounding(int value, int dividend, int divisor) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000525 // returns true if the value is greater than floor(dividend/divisor)
526 // and less than SkNextPow2(ceil(dividend - divisor))
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000527 return (((divisor * value) > (dividend - divisor))
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000528 && value <= SkNextPow2(((dividend - 1) / divisor) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000529}
530#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
531
532
533#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
534 #define kBackwards_SkColorType kRGBA_8888_SkColorType
535#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
536 #define kBackwards_SkColorType kBGRA_8888_SkColorType
537#else
538 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
539#endif
540
541static inline const char* SkColorType_to_string(SkColorType colorType) {
542 switch(colorType) {
543 case kAlpha_8_SkColorType: return "Alpha_8";
544 case kRGB_565_SkColorType: return "RGB_565";
545 case kARGB_4444_SkColorType: return "ARGB_4444";
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000546 case kN32_SkColorType: return "N32";
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000547 case kBackwards_SkColorType: return "Backwards";
548 case kIndex_8_SkColorType: return "Index_8";
549 default: return "ERROR";
550 }
551}
552
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000553static inline const char* options_colorType(
554 const SkDecodingImageGenerator::Options& opts) {
555 if (opts.fUseRequestedColorType) {
556 return SkColorType_to_string(opts.fRequestedColorType);
557 } else {
558 return "(none)";
559 }
560}
561
562static inline const char* yn(bool value) {
563 if (value) {
564 return "yes";
565 } else {
566 return "no";
567 }
568}
569
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000570/**
571 * Given either a SkStream or a SkData, try to decode the encoded
572 * image using the specified options and report errors.
573 */
574static void test_options(skiatest::Reporter* reporter,
575 const SkDecodingImageGenerator::Options& opts,
576 SkStreamRewindable* encodedStream,
577 SkData* encodedData,
578 bool useData,
579 const SkString& path) {
580 SkBitmap bm;
581 bool success = false;
582 if (useData) {
583 if (NULL == encodedData) {
584 return;
585 }
586 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000587 SkDecodingImageGenerator::Create(encodedData, opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000588 } else {
589 if (NULL == encodedStream) {
590 return;
591 }
592 success = SkInstallDiscardablePixelRef(
commit-bot@chromium.org2d970b52014-05-27 14:14:22 +0000593 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts), &bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000594 }
595 if (!success) {
596 if (opts.fUseRequestedColorType
597 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
598 return; // Ignore known conversion inabilities.
599 }
600 // If we get here, it's a failure and we will need more
601 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000602 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
603 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
604 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000605 return;
606 }
607 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
608 // Android is the only system that use Skia's image decoders in
609 // production. For now, we'll only verify that samplesize works
610 // on systems where it already is known to work.
611 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
612 opts.fSampleSize));
613 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
614 opts.fSampleSize));
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000615 // The ImageDecoder API doesn't guarantee that SampleSize does
616 // anything at all, but the decoders that this test excercises all
617 // produce an output size in the following range:
618 // (((sample_size * out_size) > (in_size - sample_size))
619 // && out_size <= SkNextPow2(((in_size - 1) / sample_size) + 1));
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000620 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
621 SkAutoLockPixels alp(bm);
622 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000623 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
624 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
625 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000626 return;
627 }
628
629 SkBitmap::Config requestedConfig
630 = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
631 REPORTER_ASSERT(reporter,
632 (!opts.fUseRequestedColorType)
633 || (bm.config() == requestedConfig));
634
635 // Condition under which we should check the decoding results:
636 if ((SkBitmap::kARGB_8888_Config == bm.config())
637 && (!path.endsWith(".jpg")) // lossy
638 && (opts.fSampleSize == 1)) { // scaled
639 const SkColor* correctPixels = kExpectedPixels;
640 SkASSERT(bm.height() == kExpectedHeight);
641 SkASSERT(bm.width() == kExpectedWidth);
642 int pixelErrors = 0;
643 for (int y = 0; y < bm.height(); ++y) {
644 for (int x = 0; x < bm.width(); ++x) {
645 if (*correctPixels != bm.getColor(x, y)) {
646 ++pixelErrors;
647 }
648 ++correctPixels;
649 }
650 }
651 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000652 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
653 "[sampleSize=%d dither=%s colorType=%s %s]",
654 pixelErrors, kExpectedHeight * kExpectedWidth,
655 opts.fSampleSize, yn(opts.fDitherImage),
656 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000657 }
658 }
659}
660
661/**
662 * SkDecodingImageGenerator has an Options struct which lets the
663 * client of the generator set sample size, dithering, and bitmap
664 * config. This test loops through many possible options and tries
665 * them on a set of 5 small encoded images (each in a different
666 * format). We test both SkData and SkStreamRewindable decoding.
667 */
668DEF_TEST(ImageDecoderOptions, reporter) {
669 const char* files[] = {
670 "randPixels.bmp",
671 "randPixels.jpg",
672 "randPixels.png",
673 "randPixels.webp",
674 #if !defined(SK_BUILD_FOR_WIN)
675 // TODO(halcanary): Find out why this fails sometimes.
676 "randPixels.gif",
677 #endif
678 };
679
680 SkString resourceDir = skiatest::Test::GetResourcePath();
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000681 if (!sk_exists(resourceDir.c_str())) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000682 return;
683 }
684
685 int scaleList[] = {1, 2, 3, 4};
686 bool ditherList[] = {true, false};
687 SkColorType colorList[] = {
688 kAlpha_8_SkColorType,
689 kRGB_565_SkColorType,
690 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000691 kN32_SkColorType
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000692 // Note that indexed color is left out of the list. Lazy
693 // decoding doesn't do indexed color.
694 };
695 const bool useDataList[] = {true, false};
696
697 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
halcanary@google.comb7acbfe2014-05-09 18:08:24 +0000698 SkString path = SkOSPath::SkPathJoin(resourceDir.c_str(), files[fidx]);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000699 if (!sk_exists(path.c_str())) {
700 continue;
701 }
702
703 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
704 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
705 SkAutoTUnref<SkStreamRewindable> encodedStream(
706 SkStream::NewFromFile(path.c_str()));
707 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
708
709 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
710 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
711 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
712 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
713 SkDecodingImageGenerator::Options opts(scaleList[i],
714 ditherList[j],
715 colorList[k]);
716 test_options(reporter, opts, encodedStream, encodedData,
717 useDataList[m], path);
718
719 }
720 SkDecodingImageGenerator::Options options(scaleList[i],
721 ditherList[j]);
722 test_options(reporter, options, encodedStream, encodedData,
723 useDataList[m], path);
724 }
725 }
726 }
727 }
728}