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