blob: bbc31cc81fed21663885bccc497bee0c3534f72c [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"
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"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000020#include "SkOSFile.h"
scroggo@google.com826d63a2013-07-18 20:06:28 +000021#include "SkPoint.h"
22#include "SkShader.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000023#include "SkStream.h"
24#include "SkString.h"
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000025
26__SK_FORCE_IMAGE_DECODER_LINKING;
27
28/**
29 * Interprets c as an unpremultiplied color, and returns the
30 * premultiplied equivalent.
31 */
32static SkPMColor premultiply_unpmcolor(SkPMColor c) {
33 U8CPU a = SkGetPackedA32(c);
34 U8CPU r = SkGetPackedR32(c);
35 U8CPU g = SkGetPackedG32(c);
36 U8CPU b = SkGetPackedB32(c);
37 return SkPreMultiplyARGB(a, r, g, b);
38}
39
40/**
41 * Return true if this stream format should be skipped, due
42 * to do being an opaque format or not a valid format.
43 */
44static bool skip_image_format(SkImageDecoder::Format format) {
45 switch (format) {
46 case SkImageDecoder::kPNG_Format:
47 case SkImageDecoder::kWEBP_Format:
48 return false;
49 // Skip unknown since it will not be decoded anyway.
50 case SkImageDecoder::kUnknown_Format:
51 // Technically ICO and BMP supports alpha channels, but our image
52 // decoders do not, so skip them as well.
53 case SkImageDecoder::kICO_Format:
54 case SkImageDecoder::kBMP_Format:
55 // The rest of these are opaque.
56 case SkImageDecoder::kWBMP_Format:
57 case SkImageDecoder::kGIF_Format:
58 case SkImageDecoder::kJPEG_Format:
59 return true;
60 }
61 SkASSERT(false);
62 return true;
63}
64
65/**
66 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
67 * them.
68 */
69static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
70 // Decode a resource:
71 SkBitmap bm8888;
72 SkBitmap bm8888Unpremul;
73
74 SkFILEStream stream(filename.c_str());
75
76 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
77 if (skip_image_format(format)) {
78 return;
79 }
80
81 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
82 if (NULL == decoder.get()) {
83 SkDebugf("couldn't decode %s\n", filename.c_str());
84 return;
85 }
86
87 bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config,
88 SkImageDecoder::kDecodePixels_Mode);
89 if (!success) {
90 return;
91 }
92
93 success = stream.rewind();
94 REPORTER_ASSERT(reporter, success);
95 if (!success) {
96 return;
97 }
98
99 decoder->setRequireUnpremultipliedColors(true);
100 success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config,
101 SkImageDecoder::kDecodePixels_Mode);
102 if (!success) {
103 return;
104 }
105
106 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
107 && bm8888.height() == bm8888Unpremul.height();
108 REPORTER_ASSERT(reporter, dimensionsMatch);
109 if (!dimensionsMatch) {
110 return;
111 }
112
113 // Only do the comparison if the two bitmaps are both 8888.
114 if (bm8888.config() != SkBitmap::kARGB_8888_Config
115 || bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) {
116 return;
117 }
118
119 // Now compare the two bitmaps.
120 for (int i = 0; i < bm8888.width(); ++i) {
121 for (int j = 0; j < bm8888.height(); ++j) {
122 // "c0" is the color of the premultiplied bitmap at (i, j).
123 const SkPMColor c0 = *bm8888.getAddr32(i, j);
124 // "c1" is the result of premultiplying the color of the unpremultiplied
125 // bitmap at (i, j).
126 const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
127 // Compute the difference for each component.
128 int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
129 int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
130 int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
131 int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
132
133 // Alpha component must be exactly the same.
134 REPORTER_ASSERT(reporter, 0 == da);
scroggo@google.comdaaea2d2013-06-14 20:39:48 +0000135
136 // Color components may not match exactly due to rounding error.
137 REPORTER_ASSERT(reporter, dr <= 1);
138 REPORTER_ASSERT(reporter, dg <= 1);
139 REPORTER_ASSERT(reporter, db <= 1);
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000140 }
141 }
142}
143
scroggo@google.comf698c822013-07-18 19:34:49 +0000144static void test_unpremul(skiatest::Reporter* reporter) {
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000145 // This test cannot run if there is no resource path.
146 SkString resourcePath = skiatest::Test::GetResourcePath();
147 if (resourcePath.isEmpty()) {
bungeman@google.com3c996f82013-10-24 21:39:35 +0000148 SkDebugf("Could not run unpremul test because resourcePath not specified.");
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000149 return;
150 }
151 SkOSFile::Iter iter(resourcePath.c_str());
152 SkString basename;
153 if (iter.next(&basename)) {
154 do {
155 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000156 //SkDebugf("about to decode \"%s\"\n", filename.c_str());
scroggo@google.com2bbc2c92013-06-14 15:33:20 +0000157 compare_unpremul(reporter, filename);
158 } while (iter.next(&basename));
159 } else {
160 SkDebugf("Failed to find any files :(\n");
161 }
162}
163
scroggo@google.com826d63a2013-07-18 20:06:28 +0000164#ifdef SK_DEBUG
165// Create a stream containing a bitmap encoded to Type type.
scroggo@google.comb5571b32013-09-25 21:34:24 +0000166static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000167 SkBitmap bm;
168 const int size = 50;
169 bm.setConfig(SkBitmap::kARGB_8888_Config, size, size);
170 bm.allocPixels();
171 SkCanvas canvas(bm);
172 SkPoint points[2] = {
173 { SkIntToScalar(0), SkIntToScalar(0) },
174 { SkIntToScalar(size), SkIntToScalar(size) }
175 };
176 SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
177 SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
178 SK_ARRAY_COUNT(colors),
179 SkShader::kClamp_TileMode);
180 SkPaint paint;
181 paint.setShader(shader)->unref();
182 canvas.drawPaint(paint);
183 // Now encode it to a stream.
184 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
185 if (NULL == data.get()) {
186 return NULL;
187 }
188 return SkNEW_ARGS(SkMemoryStream, (data.get()));
189}
190
191// For every format that supports tile based decoding, ensure that
192// calling decodeSubset will not fail if the caller has unreffed the
193// stream provided in buildTileIndex.
194// Only runs in debug mode since we are testing for a crash.
195static void test_stream_life() {
196 const SkImageEncoder::Type gTypes[] = {
scroggo@google.com826d63a2013-07-18 20:06:28 +0000197#ifdef SK_BUILD_FOR_ANDROID
scroggo@google.comd79277f2013-08-07 19:53:53 +0000198 SkImageEncoder::kJPEG_Type,
scroggo@google.com826d63a2013-07-18 20:06:28 +0000199 SkImageEncoder::kPNG_Type,
200#endif
201 SkImageEncoder::kWEBP_Type,
202 };
203 for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000204 //SkDebugf("encoding to %i\n", i);
scroggo@google.comb5571b32013-09-25 21:34:24 +0000205 SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
scroggo@google.com826d63a2013-07-18 20:06:28 +0000206 if (NULL == stream.get()) {
207 SkDebugf("no stream\n");
208 continue;
209 }
210 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
211 if (NULL == decoder.get()) {
212 SkDebugf("no decoder\n");
213 continue;
214 }
215 int width, height;
216 if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
217 SkDebugf("could not build a tile index\n");
218 continue;
219 }
220 // Now unref the stream to make sure it survives
221 stream.reset(NULL);
222 SkBitmap bm;
223 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height),
224 SkBitmap::kARGB_8888_Config);
225 }
226}
scroggo@google.com8d239242013-10-01 17:27:15 +0000227
228// Test inside SkScaledBitmapSampler.cpp
229extern void test_row_proc_choice();
230
commit-bot@chromium.org1ad518b2013-12-18 18:33:15 +0000231#endif // SK_DEBUG
scroggo@google.com826d63a2013-07-18 20:06:28 +0000232
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000233DEF_TEST(ImageDecoding, reporter) {
scroggo@google.comf698c822013-07-18 19:34:49 +0000234 test_unpremul(reporter);
scroggo@google.com826d63a2013-07-18 20:06:28 +0000235#ifdef SK_DEBUG
236 test_stream_life();
scroggo@google.com8d239242013-10-01 17:27:15 +0000237 test_row_proc_choice();
scroggo@google.com826d63a2013-07-18 20:06:28 +0000238#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000239}
halcanary@google.comdedd44a2013-12-20 16:35:22 +0000240
241////////////////////////////////////////////////////////////////////////////////
242
243DEF_TEST(WebP, reporter) {
244 const unsigned char encodedWebP[] = {
245 0x52, 0x49, 0x46, 0x46, 0x2c, 0x01, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50,
246 0x56, 0x50, 0x38, 0x4c, 0x20, 0x01, 0x00, 0x00, 0x2f, 0x07, 0xc0, 0x01,
247 0x00, 0xff, 0x01, 0x45, 0x03, 0x00, 0xe2, 0xd5, 0xae, 0x60, 0x2b, 0xad,
248 0xd9, 0x68, 0x76, 0xb6, 0x8d, 0x6a, 0x1d, 0xc0, 0xe6, 0x19, 0xd6, 0x16,
249 0xb7, 0xb4, 0xef, 0xcf, 0xc3, 0x15, 0x6c, 0xb3, 0xbd, 0x77, 0x0d, 0x85,
250 0x6d, 0x1b, 0xa9, 0xb1, 0x2b, 0xdc, 0x3d, 0x83, 0xdb, 0x00, 0x00, 0xc8,
251 0x26, 0xe5, 0x01, 0x99, 0x8a, 0xd5, 0xdd, 0xfc, 0x82, 0xcd, 0xcd, 0x9a,
252 0x8c, 0x13, 0xcc, 0x1b, 0xba, 0xf5, 0x05, 0xdb, 0xee, 0x6a, 0xdb, 0x38,
253 0x60, 0xfe, 0x43, 0x2c, 0xd4, 0x6a, 0x99, 0x4d, 0xc6, 0xc0, 0xd3, 0x28,
254 0x1b, 0xc1, 0xb1, 0x17, 0x4e, 0x43, 0x0e, 0x3d, 0x27, 0xe9, 0xe4, 0x84,
255 0x4f, 0x24, 0x62, 0x69, 0x85, 0x43, 0x8d, 0xc2, 0x04, 0x00, 0x07, 0x59,
256 0x60, 0xfd, 0x8b, 0x4d, 0x60, 0x32, 0x72, 0xcf, 0x88, 0x0c, 0x2f, 0x2f,
257 0xad, 0x62, 0xbd, 0x27, 0x09, 0x16, 0x70, 0x78, 0x6c, 0xd9, 0x82, 0xef,
258 0x1a, 0xa2, 0xcc, 0xf0, 0xf1, 0x6f, 0xd8, 0x78, 0x2e, 0x39, 0xa1, 0xcf,
259 0x14, 0x4b, 0x89, 0xb4, 0x1b, 0x48, 0x15, 0x7c, 0x48, 0x6f, 0x8c, 0x20,
260 0xb7, 0x00, 0xcf, 0xfc, 0xdb, 0xd0, 0xe9, 0xe7, 0x42, 0x09, 0xa4, 0x03,
261 0x40, 0xac, 0xda, 0x40, 0x01, 0x00, 0x5f, 0xa1, 0x3d, 0x64, 0xe1, 0xf4,
262 0x03, 0x45, 0x29, 0xe0, 0xe2, 0x4a, 0xc3, 0xa2, 0xe8, 0xe0, 0x25, 0x12,
263 0x74, 0xc6, 0xe8, 0xfb, 0x93, 0x4f, 0x9f, 0x5e, 0xc0, 0xa6, 0x91, 0x1b,
264 0xa4, 0x24, 0x82, 0xc3, 0x61, 0x07, 0x4c, 0x49, 0x4f, 0x53, 0xae, 0x5f,
265 0x5d, 0x39, 0x36, 0xc0, 0x5b, 0x57, 0x54, 0x60, 0x10, 0x00, 0x00, 0xd1,
266 0x68, 0xb6, 0x6d, 0xdb, 0x36, 0x22, 0xfa, 0x1f, 0x35, 0x75, 0x22, 0xec,
267 0x31, 0xbc, 0x5d, 0x8f, 0x87, 0x53, 0xa2, 0x05, 0x8c, 0x2f, 0xcd, 0xa8,
268 0xa7, 0xf3, 0xa3, 0xbd, 0x83, 0x8b, 0x2a, 0xc8, 0x58, 0xf5, 0xac, 0x80,
269 0xe3, 0xfe, 0x66, 0xa4, 0x7c, 0x1b, 0x6c, 0xd1, 0xa9, 0xd8, 0x14, 0xd0,
270 0xc5, 0xb5, 0x39, 0x71, 0x97, 0x19, 0x19, 0x1b
271 };
272 const SkColor thePixels[] = {
273 0xffbba570, 0xff395f5d, 0xffe25c39, 0xff197666,
274 0xff3cba27, 0xffdefcb0, 0xffc13874, 0xfffa0093,
275 0xffbda60e, 0xffc01db6, 0xff2bd688, 0xff9362d4,
276 0xffc641b2, 0xffa5cede, 0xff606eba, 0xff8f4bf3,
277 0xff3bf742, 0xff8f02a8, 0xff5509df, 0xffc7027e,
278 0xff24aa8a, 0xff886c96, 0xff625481, 0xff403689,
279 0xffc52152, 0xff78ccd6, 0xffdcb4ab, 0xff09d27d,
280 0xffca00f3, 0xff605d47, 0xff446fb2, 0xff576e46,
281 0xff273df9, 0xffb41a83, 0xfff812c3, 0xffccab67,
282 0xff034218, 0xff7db9a7, 0xff821048, 0xfffe4ab4,
283 0xff6fac98, 0xff941d27, 0xff5fe411, 0xfffbb283,
284 0xffd86e99, 0xff169162, 0xff71128c, 0xff39cab4,
285 0xffa7fe63, 0xff4c956b, 0xffbc22e0, 0xffb272e4,
286 0xff129f4a, 0xffe34513, 0xff3d3742, 0xffbd190a,
287 0xffb07222, 0xff2e23f8, 0xfff089d9, 0xffb35738,
288 0xffa86022, 0xff3340fe, 0xff95fe71, 0xff6a71df
289 };
290 SkAutoDataUnref encoded(SkData::NewWithCopy(encodedWebP,
291 sizeof(encodedWebP)));
292 SkBitmap bm;
293 bool success = SkDecodingImageGenerator::Install(encoded, &bm, NULL);
294 REPORTER_ASSERT(reporter, success);
295 if (!success) {
296 return;
297 }
298 SkAutoLockPixels alp(bm);
299 bool rightSize = SK_ARRAY_COUNT(thePixels) == bm.width() * bm.height();
300 REPORTER_ASSERT(reporter, rightSize);
301 if (rightSize) {
302 bool error = false;
303 const SkColor* correctPixel = thePixels;
304 for (int y = 0; y < bm.height(); ++y) {
305 for (int x = 0; x < bm.width(); ++x) {
306 error |= (*correctPixel != bm.getColor(x, y));
307 ++correctPixel;
308 }
309 }
310 REPORTER_ASSERT(reporter, !error);
311 }
312}