blob: 81f900660d6d5b259fcf261ad8ffb4ca024efa6e [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());
336 SkColorType colorType;
337 if (!SkBitmapConfigToColorType(bitmap->config(), &colorType)) {
338 return NULL;
339 }
340 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
341 SkAutoTDelete<SkImageGenerator> gen(
342 SkDecodingImageGenerator::Create(stream, opts));
343 SkImageInfo info;
344 if ((NULL == gen.get()) || !gen->getInfo(&info)) {
345 return NULL;
346 }
347 SkDiscardableMemory::Factory* factory = NULL;
348 if (info.getSafeSize(info.minRowBytes()) < (32 * 1024)) {
349 // only use ashmem for large images, since mmaps come at a price
350 factory = SkGetGlobalDiscardableMemoryPool();
351 }
352 if (SkInstallDiscardablePixelRef(gen.detach(), bitmap, factory)) {
353 return bitmap->pixelRef();
354 }
355 return NULL;
356}
357/**
358 * A test for the SkDecodingImageGenerator::Create and
359 * SkInstallDiscardablePixelRef functions.
360 */
361DEF_TEST(ImprovedBitmapFactory, reporter) {
362 SkString resourcePath = skiatest::Test::GetResourcePath();
363 SkString directory = SkOSPath::SkPathJoin(resourcePath.c_str(), "encoding");
364 SkString path = SkOSPath::SkPathJoin(directory.c_str(), "randPixels.png");
365 SkAutoTUnref<SkStreamRewindable> stream(
366 SkStream::NewFromFile(path.c_str()));
367 if (sk_exists(path.c_str())) {
368 SkBitmap bm;
369 SkAssertResult(bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1));
370 REPORTER_ASSERT(reporter,
371 NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
372 SkAutoLockPixels alp(bm);
373 REPORTER_ASSERT(reporter, NULL != bm.getPixels());
374 }
375}
376
377
378////////////////////////////////////////////////////////////////////////////////
379
380#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
381static inline bool check_rounding(int value, int dividend, int divisor) {
382 // returns true if (dividend/divisor) rounds up OR down to value
383 return (((divisor * value) > (dividend - divisor))
384 && ((divisor * value) < (dividend + divisor)));
385}
386#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
387
388
389#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
390 #define kBackwards_SkColorType kRGBA_8888_SkColorType
391#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
392 #define kBackwards_SkColorType kBGRA_8888_SkColorType
393#else
394 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
395#endif
396
397static inline const char* SkColorType_to_string(SkColorType colorType) {
398 switch(colorType) {
399 case kAlpha_8_SkColorType: return "Alpha_8";
400 case kRGB_565_SkColorType: return "RGB_565";
401 case kARGB_4444_SkColorType: return "ARGB_4444";
402 case kPMColor_SkColorType: return "PMColor";
403 case kBackwards_SkColorType: return "Backwards";
404 case kIndex_8_SkColorType: return "Index_8";
405 default: return "ERROR";
406 }
407}
408
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000409static inline const char* options_colorType(
410 const SkDecodingImageGenerator::Options& opts) {
411 if (opts.fUseRequestedColorType) {
412 return SkColorType_to_string(opts.fRequestedColorType);
413 } else {
414 return "(none)";
415 }
416}
417
418static inline const char* yn(bool value) {
419 if (value) {
420 return "yes";
421 } else {
422 return "no";
423 }
424}
425
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000426/**
427 * Given either a SkStream or a SkData, try to decode the encoded
428 * image using the specified options and report errors.
429 */
430static void test_options(skiatest::Reporter* reporter,
431 const SkDecodingImageGenerator::Options& opts,
432 SkStreamRewindable* encodedStream,
433 SkData* encodedData,
434 bool useData,
435 const SkString& path) {
436 SkBitmap bm;
437 bool success = false;
438 if (useData) {
439 if (NULL == encodedData) {
440 return;
441 }
442 success = SkInstallDiscardablePixelRef(
443 SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL);
444 } else {
445 if (NULL == encodedStream) {
446 return;
447 }
448 success = SkInstallDiscardablePixelRef(
449 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts),
450 &bm, NULL);
451 }
452 if (!success) {
453 if (opts.fUseRequestedColorType
454 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
455 return; // Ignore known conversion inabilities.
456 }
457 // If we get here, it's a failure and we will need more
458 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000459 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
460 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
461 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000462 return;
463 }
464 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
465 // Android is the only system that use Skia's image decoders in
466 // production. For now, we'll only verify that samplesize works
467 // on systems where it already is known to work.
468 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
469 opts.fSampleSize));
470 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
471 opts.fSampleSize));
472 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
473 SkAutoLockPixels alp(bm);
474 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000475 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
476 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
477 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000478 return;
479 }
480
481 SkBitmap::Config requestedConfig
482 = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
483 REPORTER_ASSERT(reporter,
484 (!opts.fUseRequestedColorType)
485 || (bm.config() == requestedConfig));
486
487 // Condition under which we should check the decoding results:
488 if ((SkBitmap::kARGB_8888_Config == bm.config())
489 && (!path.endsWith(".jpg")) // lossy
490 && (opts.fSampleSize == 1)) { // scaled
491 const SkColor* correctPixels = kExpectedPixels;
492 SkASSERT(bm.height() == kExpectedHeight);
493 SkASSERT(bm.width() == kExpectedWidth);
494 int pixelErrors = 0;
495 for (int y = 0; y < bm.height(); ++y) {
496 for (int x = 0; x < bm.width(); ++x) {
497 if (*correctPixels != bm.getColor(x, y)) {
498 ++pixelErrors;
499 }
500 ++correctPixels;
501 }
502 }
503 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000504 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
505 "[sampleSize=%d dither=%s colorType=%s %s]",
506 pixelErrors, kExpectedHeight * kExpectedWidth,
507 opts.fSampleSize, yn(opts.fDitherImage),
508 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000509 }
510 }
511}
512
513/**
514 * SkDecodingImageGenerator has an Options struct which lets the
515 * client of the generator set sample size, dithering, and bitmap
516 * config. This test loops through many possible options and tries
517 * them on a set of 5 small encoded images (each in a different
518 * format). We test both SkData and SkStreamRewindable decoding.
519 */
520DEF_TEST(ImageDecoderOptions, reporter) {
521 const char* files[] = {
522 "randPixels.bmp",
523 "randPixels.jpg",
524 "randPixels.png",
525 "randPixels.webp",
526 #if !defined(SK_BUILD_FOR_WIN)
527 // TODO(halcanary): Find out why this fails sometimes.
528 "randPixels.gif",
529 #endif
530 };
531
532 SkString resourceDir = skiatest::Test::GetResourcePath();
533 SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding");
534 if (!sk_exists(directory.c_str())) {
535 return;
536 }
537
538 int scaleList[] = {1, 2, 3, 4};
539 bool ditherList[] = {true, false};
540 SkColorType colorList[] = {
541 kAlpha_8_SkColorType,
542 kRGB_565_SkColorType,
543 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
544 kPMColor_SkColorType
545 // Note that indexed color is left out of the list. Lazy
546 // decoding doesn't do indexed color.
547 };
548 const bool useDataList[] = {true, false};
549
550 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
551 SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]);
552 if (!sk_exists(path.c_str())) {
553 continue;
554 }
555
556 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
557 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
558 SkAutoTUnref<SkStreamRewindable> encodedStream(
559 SkStream::NewFromFile(path.c_str()));
560 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
561
562 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
563 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
564 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
565 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
566 SkDecodingImageGenerator::Options opts(scaleList[i],
567 ditherList[j],
568 colorList[k]);
569 test_options(reporter, opts, encodedStream, encodedData,
570 useDataList[m], path);
571
572 }
573 SkDecodingImageGenerator::Options options(scaleList[i],
574 ditherList[j]);
575 test_options(reporter, options, encodedStream, encodedData,
576 useDataList[m], path);
577 }
578 }
579 }
580 }
581}