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