blob: b4aaa9c5a24e00a7c9b19bb331c6a737d043fe99 [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"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +00009#include "SkBitmap.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000010#include "SkCanvas.h"
11#include "SkColor.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000012#include "SkColorPriv.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000013#include "SkData.h"
halcanary@google.comdedd44a2013-12-20 16:35:22 +000014#include "SkDecodingImageGenerator.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000015#include "SkDiscardableMemoryPool.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000016#include "SkForceLinking.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000017#include "SkGradientShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000018#include "SkImageDecoder.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000019#include "SkImageEncoder.h"
halcanary@google.com3d50ea12014-01-02 13:15:13 +000020#include "SkImageGenerator.h"
21#include "SkImagePriv.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000022#include "SkOSFile.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000023#include "SkPoint.h"
24#include "SkShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000025#include "SkStream.h"
26#include "SkString.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
243////////////////////////////////////////////////////////////////////////////////
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000244namespace {
245// expected output for 8x8 bitmap
246const int kExpectedWidth = 8;
247const int kExpectedHeight = 8;
248const SkColor kExpectedPixels[] = {
249 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
250 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
251 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
252 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
253 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
254 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
255 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
256 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
257 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
258 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
259 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
260 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
261 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
262 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
263 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
264 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
265};
266SK_COMPILE_ASSERT((kExpectedWidth * kExpectedHeight)
267 == SK_ARRAY_COUNT(kExpectedPixels), array_size_mismatch);
268} // namespace
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000269
270DEF_TEST(WebP, reporter) {
271 const unsigned char encodedWebP[] = {
272 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
273 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
274 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
275 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
276 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
277 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
278 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
279 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
280 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
281 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
282 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
283 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
284 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
285 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
286 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
287 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
288 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
289 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
290 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
291 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
292 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
293 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
294 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
295 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
296 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
297 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
298 };
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000299 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
300 sizeof(encodedWebP)));
301 SkBitmap bm;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000302
303 bool success = SkInstallDiscardablePixelRef(
304 SkDecodingImageGenerator::Create(encoded,
305 SkDecodingImageGenerator::Options()), &bm, NULL);
306
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000307 REPORTER_ASSERT(reporter, success);
308 if (!success) {
309 return;
310 }
311 SkAutoLockPixels alp(bm);
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000312
skia.committer@gmail.com86202072014-01-03 07:01:45 +0000313 bool rightSize = ((kExpectedWidth == bm.width())
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000314 && (kExpectedHeight == bm.height()));
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000315 REPORTER_ASSERT(reporter, rightSize);
316 if (rightSize) {
317 bool error = false;
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000318 const SkColor* correctPixel = kExpectedPixels;
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000319 for (int y = 0; y < bm.height(); ++y) {
320 for (int x = 0; x < bm.width(); ++x) {
321 error |= (*correctPixel != bm.getColor(x, y));
322 ++correctPixel;
323 }
324 }
325 REPORTER_ASSERT(reporter, !error);
326 }
327}
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000328
329////////////////////////////////////////////////////////////////////////////////
330
331// example of how Android will do this inside their BitmapFactory
332static SkPixelRef* install_pixel_ref(SkBitmap* bitmap,
333 SkStreamRewindable* stream,
334 int sampleSize, bool ditherImage) {
335 SkASSERT(bitmap != NULL);
336 SkASSERT(stream != NULL);
337 SkASSERT(stream->rewind());
338 SkASSERT(stream->unique());
339 SkColorType colorType;
340 if (!SkBitmapConfigToColorType(bitmap->config(), &colorType)) {
341 return NULL;
342 }
343 SkDecodingImageGenerator::Options opts(sampleSize, ditherImage, colorType);
344 SkAutoTDelete<SkImageGenerator> gen(
345 SkDecodingImageGenerator::Create(stream, opts));
346 SkImageInfo info;
347 if ((NULL == gen.get()) || !gen->getInfo(&info)) {
348 return NULL;
349 }
350 SkDiscardableMemory::Factory* factory = NULL;
351 if (info.getSafeSize(info.minRowBytes()) < (32 * 1024)) {
352 // only use ashmem for large images, since mmaps come at a price
353 factory = SkGetGlobalDiscardableMemoryPool();
354 }
355 if (SkInstallDiscardablePixelRef(gen.detach(), bitmap, factory)) {
356 return bitmap->pixelRef();
357 }
358 return NULL;
359}
360/**
361 * A test for the SkDecodingImageGenerator::Create and
362 * SkInstallDiscardablePixelRef functions.
363 */
364DEF_TEST(ImprovedBitmapFactory, reporter) {
365 SkString resourcePath = skiatest::Test::GetResourcePath();
366 SkString directory = SkOSPath::SkPathJoin(resourcePath.c_str(), "encoding");
367 SkString path = SkOSPath::SkPathJoin(directory.c_str(), "randPixels.png");
368 SkAutoTUnref<SkStreamRewindable> stream(
369 SkStream::NewFromFile(path.c_str()));
370 if (sk_exists(path.c_str())) {
371 SkBitmap bm;
372 SkAssertResult(bm.setConfig(SkBitmap::kARGB_8888_Config, 1, 1));
373 REPORTER_ASSERT(reporter,
374 NULL != install_pixel_ref(&bm, stream.detach(), 1, true));
375 SkAutoLockPixels alp(bm);
376 REPORTER_ASSERT(reporter, NULL != bm.getPixels());
377 }
378}
379
380
381////////////////////////////////////////////////////////////////////////////////
382
383#if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
384static inline bool check_rounding(int value, int dividend, int divisor) {
385 // returns true if (dividend/divisor) rounds up OR down to value
386 return (((divisor * value) > (dividend - divisor))
387 && ((divisor * value) < (dividend + divisor)));
388}
389#endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
390
391
392#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
393 #define kBackwards_SkColorType kRGBA_8888_SkColorType
394#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
395 #define kBackwards_SkColorType kBGRA_8888_SkColorType
396#else
397 #error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
398#endif
399
400static inline const char* SkColorType_to_string(SkColorType colorType) {
401 switch(colorType) {
402 case kAlpha_8_SkColorType: return "Alpha_8";
403 case kRGB_565_SkColorType: return "RGB_565";
404 case kARGB_4444_SkColorType: return "ARGB_4444";
405 case kPMColor_SkColorType: return "PMColor";
406 case kBackwards_SkColorType: return "Backwards";
407 case kIndex_8_SkColorType: return "Index_8";
408 default: return "ERROR";
409 }
410}
411
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000412static inline const char* options_colorType(
413 const SkDecodingImageGenerator::Options& opts) {
414 if (opts.fUseRequestedColorType) {
415 return SkColorType_to_string(opts.fRequestedColorType);
416 } else {
417 return "(none)";
418 }
419}
420
421static inline const char* yn(bool value) {
422 if (value) {
423 return "yes";
424 } else {
425 return "no";
426 }
427}
428
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000429/**
430 * Given either a SkStream or a SkData, try to decode the encoded
431 * image using the specified options and report errors.
432 */
433static void test_options(skiatest::Reporter* reporter,
434 const SkDecodingImageGenerator::Options& opts,
435 SkStreamRewindable* encodedStream,
436 SkData* encodedData,
437 bool useData,
438 const SkString& path) {
439 SkBitmap bm;
440 bool success = false;
441 if (useData) {
442 if (NULL == encodedData) {
443 return;
444 }
445 success = SkInstallDiscardablePixelRef(
446 SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL);
447 } else {
448 if (NULL == encodedStream) {
449 return;
450 }
451 success = SkInstallDiscardablePixelRef(
452 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts),
453 &bm, NULL);
454 }
455 if (!success) {
456 if (opts.fUseRequestedColorType
457 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
458 return; // Ignore known conversion inabilities.
459 }
460 // If we get here, it's a failure and we will need more
461 // information about why it failed.
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000462 ERRORF(reporter, "Bounds decode failed [sampleSize=%d dither=%s "
463 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
464 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000465 return;
466 }
467 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
468 // Android is the only system that use Skia's image decoders in
469 // production. For now, we'll only verify that samplesize works
470 // on systems where it already is known to work.
471 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
472 opts.fSampleSize));
473 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
474 opts.fSampleSize));
475 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
476 SkAutoLockPixels alp(bm);
477 if (bm.getPixels() == NULL) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000478 ERRORF(reporter, "Pixel decode failed [sampleSize=%d dither=%s "
479 "colorType=%s %s]", opts.fSampleSize, yn(opts.fDitherImage),
480 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000481 return;
482 }
483
484 SkBitmap::Config requestedConfig
485 = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
486 REPORTER_ASSERT(reporter,
487 (!opts.fUseRequestedColorType)
488 || (bm.config() == requestedConfig));
489
490 // Condition under which we should check the decoding results:
491 if ((SkBitmap::kARGB_8888_Config == bm.config())
492 && (!path.endsWith(".jpg")) // lossy
493 && (opts.fSampleSize == 1)) { // scaled
494 const SkColor* correctPixels = kExpectedPixels;
495 SkASSERT(bm.height() == kExpectedHeight);
496 SkASSERT(bm.width() == kExpectedWidth);
497 int pixelErrors = 0;
498 for (int y = 0; y < bm.height(); ++y) {
499 for (int x = 0; x < bm.width(); ++x) {
500 if (*correctPixels != bm.getColor(x, y)) {
501 ++pixelErrors;
502 }
503 ++correctPixels;
504 }
505 }
506 if (pixelErrors != 0) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000507 ERRORF(reporter, "Pixel-level mismatch (%d of %d) "
508 "[sampleSize=%d dither=%s colorType=%s %s]",
509 pixelErrors, kExpectedHeight * kExpectedWidth,
510 opts.fSampleSize, yn(opts.fDitherImage),
511 options_colorType(opts), path.c_str());
halcanary@google.com3d50ea12014-01-02 13:15:13 +0000512 }
513 }
514}
515
516/**
517 * SkDecodingImageGenerator has an Options struct which lets the
518 * client of the generator set sample size, dithering, and bitmap
519 * config. This test loops through many possible options and tries
520 * them on a set of 5 small encoded images (each in a different
521 * format). We test both SkData and SkStreamRewindable decoding.
522 */
523DEF_TEST(ImageDecoderOptions, reporter) {
524 const char* files[] = {
525 "randPixels.bmp",
526 "randPixels.jpg",
527 "randPixels.png",
528 "randPixels.webp",
529 #if !defined(SK_BUILD_FOR_WIN)
530 // TODO(halcanary): Find out why this fails sometimes.
531 "randPixels.gif",
532 #endif
533 };
534
535 SkString resourceDir = skiatest::Test::GetResourcePath();
536 SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding");
537 if (!sk_exists(directory.c_str())) {
538 return;
539 }
540
541 int scaleList[] = {1, 2, 3, 4};
542 bool ditherList[] = {true, false};
543 SkColorType colorList[] = {
544 kAlpha_8_SkColorType,
545 kRGB_565_SkColorType,
546 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
547 kPMColor_SkColorType
548 // Note that indexed color is left out of the list. Lazy
549 // decoding doesn't do indexed color.
550 };
551 const bool useDataList[] = {true, false};
552
553 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
554 SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]);
555 if (!sk_exists(path.c_str())) {
556 continue;
557 }
558
559 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
560 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
561 SkAutoTUnref<SkStreamRewindable> encodedStream(
562 SkStream::NewFromFile(path.c_str()));
563 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
564
565 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
566 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
567 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
568 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
569 SkDecodingImageGenerator::Options opts(scaleList[i],
570 ditherList[j],
571 colorList[k]);
572 test_options(reporter, opts, encodedStream, encodedData,
573 useDataList[m], path);
574
575 }
576 SkDecodingImageGenerator::Options options(scaleList[i],
577 ditherList[j]);
578 test_options(reporter, options, encodedStream, encodedData,
579 useDataList[m], path);
580 }
581 }
582 }
583 }
584}
585////////////////////////////////////////////////////////////////////////////////