blob: c74bb35ccf43927f4e8f4544f82b5b2b7e077c50 [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
413/**
414 * Given either a SkStream or a SkData, try to decode the encoded
415 * image using the specified options and report errors.
416 */
417static void test_options(skiatest::Reporter* reporter,
418 const SkDecodingImageGenerator::Options& opts,
419 SkStreamRewindable* encodedStream,
420 SkData* encodedData,
421 bool useData,
422 const SkString& path) {
423 SkBitmap bm;
424 bool success = false;
425 if (useData) {
426 if (NULL == encodedData) {
427 return;
428 }
429 success = SkInstallDiscardablePixelRef(
430 SkDecodingImageGenerator::Create(encodedData, opts), &bm, NULL);
431 } else {
432 if (NULL == encodedStream) {
433 return;
434 }
435 success = SkInstallDiscardablePixelRef(
436 SkDecodingImageGenerator::Create(encodedStream->duplicate(), opts),
437 &bm, NULL);
438 }
439 if (!success) {
440 if (opts.fUseRequestedColorType
441 && (kARGB_4444_SkColorType == opts.fRequestedColorType)) {
442 return; // Ignore known conversion inabilities.
443 }
444 // If we get here, it's a failure and we will need more
445 // information about why it failed.
446 reporter->reportFailed(SkStringPrintf(
447 "Bounds decode failed "
448 "[sampleSize=%d dither=%s colorType=%s %s] %s:%d",
449 opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
450 (opts.fUseRequestedColorType
451 ? SkColorType_to_string(opts.fRequestedColorType) : "(none)"),
452 path.c_str(), __FILE__, __LINE__));
453 return;
454 }
455 #if defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_UNIX)
456 // Android is the only system that use Skia's image decoders in
457 // production. For now, we'll only verify that samplesize works
458 // on systems where it already is known to work.
459 REPORTER_ASSERT(reporter, check_rounding(bm.height(), kExpectedHeight,
460 opts.fSampleSize));
461 REPORTER_ASSERT(reporter, check_rounding(bm.width(), kExpectedWidth,
462 opts.fSampleSize));
463 #endif // SK_BUILD_FOR_ANDROID || SK_BUILD_FOR_UNIX
464 SkAutoLockPixels alp(bm);
465 if (bm.getPixels() == NULL) {
466 reporter->reportFailed(SkStringPrintf(
467 "Pixel decode failed "
468 "[sampleSize=%d dither=%s colorType=%s %s] %s:%d",
469 opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
470 (opts.fUseRequestedColorType
471 ? SkColorType_to_string(opts.fRequestedColorType) : "(none)"),
472 path.c_str(), __FILE__, __LINE__));
473 return;
474 }
475
476 SkBitmap::Config requestedConfig
477 = SkColorTypeToBitmapConfig(opts.fRequestedColorType);
478 REPORTER_ASSERT(reporter,
479 (!opts.fUseRequestedColorType)
480 || (bm.config() == requestedConfig));
481
482 // Condition under which we should check the decoding results:
483 if ((SkBitmap::kARGB_8888_Config == bm.config())
484 && (!path.endsWith(".jpg")) // lossy
485 && (opts.fSampleSize == 1)) { // scaled
486 const SkColor* correctPixels = kExpectedPixels;
487 SkASSERT(bm.height() == kExpectedHeight);
488 SkASSERT(bm.width() == kExpectedWidth);
489 int pixelErrors = 0;
490 for (int y = 0; y < bm.height(); ++y) {
491 for (int x = 0; x < bm.width(); ++x) {
492 if (*correctPixels != bm.getColor(x, y)) {
493 ++pixelErrors;
494 }
495 ++correctPixels;
496 }
497 }
498 if (pixelErrors != 0) {
499 reporter->reportFailed(SkStringPrintf(
500 "Pixel-level mismatch (%d of %d) [sampleSize=%d "
501 "dither=%s colorType=%s %s] %s:%d",
502 pixelErrors, kExpectedHeight * kExpectedWidth,
503 opts.fSampleSize, (opts.fDitherImage ? "yes" : "no"),
504 (opts.fUseRequestedColorType
505 ? SkColorType_to_string(opts.fRequestedColorType)
506 : "(none)"), path.c_str(), __FILE__, __LINE__));
507 }
508 }
509}
510
511/**
512 * SkDecodingImageGenerator has an Options struct which lets the
513 * client of the generator set sample size, dithering, and bitmap
514 * config. This test loops through many possible options and tries
515 * them on a set of 5 small encoded images (each in a different
516 * format). We test both SkData and SkStreamRewindable decoding.
517 */
518DEF_TEST(ImageDecoderOptions, reporter) {
519 const char* files[] = {
520 "randPixels.bmp",
521 "randPixels.jpg",
522 "randPixels.png",
523 "randPixels.webp",
524 #if !defined(SK_BUILD_FOR_WIN)
525 // TODO(halcanary): Find out why this fails sometimes.
526 "randPixels.gif",
527 #endif
528 };
529
530 SkString resourceDir = skiatest::Test::GetResourcePath();
531 SkString directory = SkOSPath::SkPathJoin(resourceDir.c_str(), "encoding");
532 if (!sk_exists(directory.c_str())) {
533 return;
534 }
535
536 int scaleList[] = {1, 2, 3, 4};
537 bool ditherList[] = {true, false};
538 SkColorType colorList[] = {
539 kAlpha_8_SkColorType,
540 kRGB_565_SkColorType,
541 kARGB_4444_SkColorType, // Most decoders will fail on 4444.
542 kPMColor_SkColorType
543 // Note that indexed color is left out of the list. Lazy
544 // decoding doesn't do indexed color.
545 };
546 const bool useDataList[] = {true, false};
547
548 for (size_t fidx = 0; fidx < SK_ARRAY_COUNT(files); ++fidx) {
549 SkString path = SkOSPath::SkPathJoin(directory.c_str(), files[fidx]);
550 if (!sk_exists(path.c_str())) {
551 continue;
552 }
553
554 SkAutoDataUnref encodedData(SkData::NewFromFileName(path.c_str()));
555 REPORTER_ASSERT(reporter, encodedData.get() != NULL);
556 SkAutoTUnref<SkStreamRewindable> encodedStream(
557 SkStream::NewFromFile(path.c_str()));
558 REPORTER_ASSERT(reporter, encodedStream.get() != NULL);
559
560 for (size_t i = 0; i < SK_ARRAY_COUNT(scaleList); ++i) {
561 for (size_t j = 0; j < SK_ARRAY_COUNT(ditherList); ++j) {
562 for (size_t m = 0; m < SK_ARRAY_COUNT(useDataList); ++m) {
563 for (size_t k = 0; k < SK_ARRAY_COUNT(colorList); ++k) {
564 SkDecodingImageGenerator::Options opts(scaleList[i],
565 ditherList[j],
566 colorList[k]);
567 test_options(reporter, opts, encodedStream, encodedData,
568 useDataList[m], path);
569
570 }
571 SkDecodingImageGenerator::Options options(scaleList[i],
572 ditherList[j]);
573 test_options(reporter, options, encodedStream, encodedData,
574 useDataList[m], path);
575 }
576 }
577 }
578 }
579}
580////////////////////////////////////////////////////////////////////////////////