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