blob: 2e0225a26195bdcaf1d39a9c143f6818ea53f349 [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
scroggo@google.com826d63a2013-07-18 20:06:28 +0000166#ifdef SK_DEBUG
167// Create a stream containing a bitmap encoded to Type type.
scroggo@google.comb5571b32013-09-25 21:34:24 +0000168static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000169 SkBitmap bm;
170 const int size = 50;
171 bm.setConfig(SkBitmap::kARGB_8888_Config, size, size);
172 bm.allocPixels();
173 SkCanvas canvas(bm);
174 SkPoint points[2] = {
175 { SkIntToScalar(0), SkIntToScalar(0) },
176 { SkIntToScalar(size), SkIntToScalar(size) }
177 };
178 SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
179 SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
180 SK_ARRAY_COUNT(colors),
181 SkShader::kClamp_TileMode);
182 SkPaint paint;
183 paint.setShader(shader)->unref();
184 canvas.drawPaint(paint);
185 // Now encode it to a stream.
186 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
187 if (NULL == data.get()) {
188 return NULL;
189 }
190 return SkNEW_ARGS(SkMemoryStream, (data.get()));
191}
192
193// For every format that supports tile based decoding, ensure that
194// calling decodeSubset will not fail if the caller has unreffed the
195// stream provided in buildTileIndex.
196// Only runs in debug mode since we are testing for a crash.
197static void test_stream_life() {
198 const SkImageEncoder::Type gTypes[] = {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000199#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comd79277f2013-08-07 19:53:53 +0000200 SkImageEncoder::kJPEG_Type,
scroggo@google.com826d63a2013-07-18 20:06:28 +0000201 SkImageEncoder::kPNG_Type,
202#endif
203 SkImageEncoder::kWEBP_Type,
204 };
205 for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000206 // SkDebugf("encoding to %i\n", i);
scroggo@google.comb5571b32013-09-25 21:34:24 +0000207 SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
scroggo@google.com826d63a2013-07-18 20:06:28 +0000208 if (NULL == stream.get()) {
209 SkDebugf("no stream\n");
210 continue;
211 }
212 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
213 if (NULL == decoder.get()) {
214 SkDebugf("no decoder\n");
215 continue;
216 }
217 int width, height;
218 if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
219 SkDebugf("could not build a tile index\n");
220 continue;
221 }
222 // Now unref the stream to make sure it survives
223 stream.reset(NULL);
224 SkBitmap bm;
225 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height),
226 SkBitmap::kARGB_8888_Config);
227 }
228}
scroggo@google.com8d239242013-10-01 17:27:15 +0000229
230// Test inside SkScaledBitmapSampler.cpp
231extern void test_row_proc_choice();
232
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000233#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000234
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000235DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000236 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000237#ifdef SK_DEBUG
238 test_stream_life();
scroggo@google.com8d239242013-10-01 17:27:15 +0000239 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000240#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000241}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000242
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000243// expected output for 8x8 bitmap
tfarina@chromium.org58674812014-01-21 23:39:22 +0000244static const int kExpectedWidth = 8;
245static const int kExpectedHeight = 8;
246static const SkColor kExpectedPixels[] = {
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000247 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
248 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
249 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
250 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
251 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
252 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
253 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
254 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
255 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
256 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
257 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
258 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
259 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
260 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
261 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
262 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
263};
264SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
265 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000266
267DEF_TEST(WebP, reporter) {
268 const unsigned char encodedWebP[] = {
269 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
270 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
271 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
272 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
273 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
274 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
275 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
276 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
277 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
278 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
279 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
280 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
281 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
282 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
283 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
284 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
285 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
286 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
287 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
288 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
289 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
290 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
291 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
292 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
293 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
294 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
295 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000296 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
297 sizeof(encodedWebP)));
298 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000299
300 bool success = SkInstallDiscardablePixelRef(
301 SkDecodingImageGenerator::Create(encoded,
302 SkDecodingImageGenerator::Options()), &bm, NULL);
303
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000304 REPORTER_ASSERT(reporter, success);
305 if (!success) {
306 return;
307 }
308 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000309
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000310 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000311 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000312 REPORTER_ASSERT(reporter, rightSize);
313 if (rightSize) {
314 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000315 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000316 for (int y = 0; y < bm.height(); ++y) {
317 for (int x = 0; x < bm.width(); ++x) {
318 error |= (*correctPixel != bm.getColor(x, y));
319 ++correctPixel;
320 }
321 }
322 REPORTER_ASSERT(reporter, !error);
323 }
324}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000325
326////////////////////////////////////////////////////////////////////////////////
327
328// example of how Android will do this inside their BitmapFactory
329static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
330 SkStreamRewindable* stream,
331 int sampleSize, bool ditherImage) {
332 SkASSERT(bitmap != NULL);
333 SkASSERT(stream != NULL);
334 SkASSERT(stream->rewind());
335 SkASSERT(stream->unique());
commit-bot@chromium.org61e96cd2014-02-11 18:21:45 +0000336 SkColorType colorType = bitmap->colorType();
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000337 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
338 SkAutoTDelete<SkImageGenerator> gen(
339 SkDecodingImageGenerator::Create(stream, opts));
340 SkImageInfo info;
341 if ((NULL == gen.get()) || !gen->getInfo(&info)) {
342 return NULL;
343 }
344 SkDiscardableMemory::Factory* factory = NULL;
345 if (info.getSafeSize(info.minRowBytes()) < (32 * 1024)) {
346 // only use ashmem for large images, since mmaps come at a price
347 factory = SkGetGlobalDiscardableMemoryPool();
348 }
349 if (SkInstallDiscardablePixelRef(gen.detach(), bitmap, factory)) {
350 return bitmap->pixelRef();
351 }
352 return NULL;
353}
354/**
355 * A test for the SkDecodingImageGenerator::Create and
356 * SkInstallDiscardablePixelRef functions.
357 */
358DEF_TEST(ImprovedBitmapFactory, reporter) {
359 SkString resourcePath = skiatest::Test::GetResourcePath();
360 SkString directory = SkOSPath::SkPathJoin(resourcePath.c_str(), "encoding");
361 SkString path = SkOSPath::SkPathJoin(directory.c_str(), "randPixels.png");
362 SkAutoTUnref<SkStreamRewindable> stream(
363 SkStream::NewFromFile(path.c_str()));
364 if (sk_exists(path.c_str())) {
365 SkBitmap bm;
366 SkAssertResult(bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1));
367 REPORTER_ASSERT(reporter,
368 NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
369 SkAutoLockPixels alp(bm);
370 REPORTER_ASSERT(reporter, NULL != bm.getPixels());
371 }
372}
373
374
375////////////////////////////////////////////////////////////////////////////////
376
377#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
378static inline bool check_rounding(int value, int dividend, int divisor) {
379 // returns true if (dividend/divisor) rounds up OR down to value
380 return (((divisor * value) > (dividend - divisor))
381 && ((divisor * value) < (dividend + divisor)));
382}
383#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
384
385
386#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
387 #define kBackwards_SkColorType kRGBA_8888_SkColorType
388#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
389 #define kBackwards_SkColorType kBGRA_8888_SkColorType
390#else
391 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
392#endif
393
394static inline const char* SkColorType_to_string(SkColorType colorType) {
395 switch(colorType) {
396 case kAlpha_8_SkColorType: return "Alpha_8";
397 case kRGB_565_SkColorType: return "RGB_565";
398 case kARGB_4444_SkColorType: return "ARGB_4444";
399 case kPMColor_SkColorType: return "PMColor";
400 case kBackwards_SkColorType: return "Backwards";
401 case kIndex_8_SkColorType: return "Index_8";
402 default: return "ERROR";
403 }
404}
405
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000406static inline const char* options_colorType(
407 const SkDecodingImageGenerator::Options& opts) {
408 if (opts.fUseRequestedColorType) {
409 return SkColorType_to_string(opts.fRequestedColorType);
410 } else {
411 return "(none)";
412 }
413}
414
415static inline const char* yn(bool value) {
416 if (value) {
417 return "yes";
418 } else {
419 return "no";
420 }
421}
422
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000423/**
424 * Given either a SkStream or a SkData, try to decode the encoded
425 * image using the specified options and report errors.
426 */
427static void test_options(skiatest::Reporter* reporter,
428 const SkDecodingImageGenerator::Options& opts,
429 SkStreamRewindable* encodedStream,
430 SkData* encodedData,
431 bool useData,
432 const SkString& path) {
433 SkBitmap bm;
434 bool success = false;
435 if (useData) {
436 if (NULL == encodedData) {
437 return;
438 }
439 success = SkInstallDiscardablePixelRef(
440 SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL);
441 } else {
442 if (NULL == encodedStream) {
443 return;
444 }
445 success = SkInstallDiscardablePixelRef(
446 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts),
447 &bm, NULL);
448 }
449 if (!success) {
450 if (opts.fUseRequestedColorType
451 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
452 return; // Ignore known conversion inabilities.
453 }
454 // If we get here, it's a failure and we will need more
455 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000456 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
457 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
458 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000459 return;
460 }
461 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
462 // Android is the only system that use Skia's image decoders in
463 // production. For now, we'll only verify that samplesize works
464 // on systems where it already is known to work.
465 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
466 opts.fSampleSize));
467 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
468 opts.fSampleSize));
469 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
470 SkAutoLockPixels alp(bm);
471 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000472 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
473 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
474 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000475 return;
476 }
477
478 SkBitmap::Config requestedConfig
479 = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
480 REPORTER_ASSERT(reporter,
481 (!opts.fUseRequestedColorType)
482 || (bm.config() == requestedConfig));
483
484 // Condition under which we should check the decoding results:
485 if ((SkBitmap::kARGB_8888_Config == bm.config())
486 && (!path.endsWith(".jpg")) // lossy
487 && (opts.fSampleSize == 1)) { // scaled
488 const SkColor* correctPixels = kExpectedPixels;
489 SkASSERT(bm.height() == kExpectedHeight);
490 SkASSERT(bm.width() == kExpectedWidth);
491 int pixelErrors = 0;
492 for (int y = 0; y < bm.height(); ++y) {
493 for (int x = 0; x < bm.width(); ++x) {
494 if (*correctPixels != bm.getColor(x, y)) {
495 ++pixelErrors;
496 }
497 ++correctPixels;
498 }
499 }
500 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000501 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
502 "[sampleSize=%d dither=%s colorType=%s %s]",
503 pixelErrors, kExpectedHeight * kExpectedWidth,
504 opts.fSampleSize, yn(opts.fDitherImage),
505 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000506 }
507 }
508}
509
510/**
511 * SkDecodingImageGenerator has an Options struct which lets the
512 * client of the generator set sample size, dithering, and bitmap
513 * config. This test loops through many possible options and tries
514 * them on a set of 5 small encoded images (each in a different
515 * format). We test both SkData and SkStreamRewindable decoding.
516 */
517DEF_TEST(ImageDecoderOptions, reporter) {
518 const char* files[] = {
519 "randPixels.bmp",
520 "randPixels.jpg",
521 "randPixels.png",
522 "randPixels.webp",
523 #if !defined(SK_BUILD_FOR_WIN)
524 // TODO(halcanary): Find out why this fails sometimes.
525 "randPixels.gif",
526 #endif
527 };
528
529 SkString resourceDir = skiatest::Test::GetResourcePath();
530 SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding");
531 if (!sk_exists(directory.c_str())) {
532 return;
533 }
534
535 int scaleList[] = {1, 2, 3, 4};
536 bool ditherList[] = {true, false};
537 SkColorType colorList[] = {
538 kAlpha_8_SkColorType,
539 kRGB_565_SkColorType,
540 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
541 kPMColor_SkColorType
542 // Note that indexed color is left out of the list. Lazy
543 // decoding doesn't do indexed color.
544 };
545 const bool useDataList[] = {true, false};
546
547 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
548 SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]);
549 if (!sk_exists(path.c_str())) {
550 continue;
551 }
552
553 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
554 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
555 SkAutoTUnref<SkStreamRewindable> encodedStream(
556 SkStream::NewFromFile(path.c_str()));
557 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
558
559 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
560 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
561 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
562 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
563 SkDecodingImageGenerator::Options opts(scaleList[i],
564 ditherList[j],
565 colorList[k]);
566 test_options(reporter, opts, encodedStream, encodedData,
567 useDataList[m], path);
568
569 }
570 SkDecodingImageGenerator::Options options(scaleList[i],
571 ditherList[j]);
572 test_options(reporter, options, encodedStream, encodedData,
573 useDataList[m], path);
574 }
575 }
576 }
577 }
578}