1 /*
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 "Test.h"
9 #include "TestClassDef.h"
10 #include "SkBitmap.h"
11 #include "SkCanvas.h"
12 #include "SkColor.h"
13 #include "SkColorPriv.h"
14 #include "SkData.h"
15 #include "SkForceLinking.h"
16 #include "SkGradientShader.h"
17 #include "SkImageDecoder.h"
18 #include "SkImageEncoder.h"
19 #include "SkOSFile.h"
20 #include "SkPoint.h"
21 #include "SkShader.h"
22 #include "SkStream.h"
23 #include "SkString.h"
24
25 __SK_FORCE_IMAGE_DECODER_LINKING;
26
27 /**
28 * Interprets c as an unpremultiplied color, and returns the
29 * premultiplied equivalent.
30 */
premultiply_unpmcolor(SkPMColor c)31 static SkPMColor premultiply_unpmcolor(SkPMColor c) {
32 U8CPU a = SkGetPackedA32(c);
33 U8CPU r = SkGetPackedR32(c);
34 U8CPU g = SkGetPackedG32(c);
35 U8CPU b = SkGetPackedB32(c);
36 return SkPreMultiplyARGB(a, r, g, b);
37 }
38
39 /**
40 * Return true if this stream format should be skipped, due
41 * to do being an opaque format or not a valid format.
42 */
skip_image_format(SkImageDecoder::Format format)43 static bool skip_image_format(SkImageDecoder::Format format) {
44 switch (format) {
45 case SkImageDecoder::kPNG_Format:
46 case SkImageDecoder::kWEBP_Format:
47 return false;
48 // Skip unknown since it will not be decoded anyway.
49 case SkImageDecoder::kUnknown_Format:
50 // Technically ICO and BMP supports alpha channels, but our image
51 // decoders do not, so skip them as well.
52 case SkImageDecoder::kICO_Format:
53 case SkImageDecoder::kBMP_Format:
54 // The rest of these are opaque.
55 case SkImageDecoder::kWBMP_Format:
56 case SkImageDecoder::kGIF_Format:
57 case SkImageDecoder::kJPEG_Format:
58 return true;
59 }
60 SkASSERT(false);
61 return true;
62 }
63
64 /**
65 * Test decoding an image in premultiplied mode and unpremultiplied mode and compare
66 * them.
67 */
compare_unpremul(skiatest::Reporter * reporter,const SkString & filename)68 static void compare_unpremul(skiatest::Reporter* reporter, const SkString& filename) {
69 // Decode a resource:
70 SkBitmap bm8888;
71 SkBitmap bm8888Unpremul;
72
73 SkFILEStream stream(filename.c_str());
74
75 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&stream);
76 if (skip_image_format(format)) {
77 return;
78 }
79
80 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
81 if (NULL == decoder.get()) {
82 SkDebugf("couldn't decode %s\n", filename.c_str());
83 return;
84 }
85
86 bool success = decoder->decode(&stream, &bm8888, SkBitmap::kARGB_8888_Config,
87 SkImageDecoder::kDecodePixels_Mode);
88 if (!success) {
89 return;
90 }
91
92 success = stream.rewind();
93 REPORTER_ASSERT(reporter, success);
94 if (!success) {
95 return;
96 }
97
98 decoder->setRequireUnpremultipliedColors(true);
99 success = decoder->decode(&stream, &bm8888Unpremul, SkBitmap::kARGB_8888_Config,
100 SkImageDecoder::kDecodePixels_Mode);
101 if (!success) {
102 return;
103 }
104
105 bool dimensionsMatch = bm8888.width() == bm8888Unpremul.width()
106 && bm8888.height() == bm8888Unpremul.height();
107 REPORTER_ASSERT(reporter, dimensionsMatch);
108 if (!dimensionsMatch) {
109 return;
110 }
111
112 // Only do the comparison if the two bitmaps are both 8888.
113 if (bm8888.config() != SkBitmap::kARGB_8888_Config
114 || bm8888Unpremul.config() != SkBitmap::kARGB_8888_Config) {
115 return;
116 }
117
118 // Now compare the two bitmaps.
119 for (int i = 0; i < bm8888.width(); ++i) {
120 for (int j = 0; j < bm8888.height(); ++j) {
121 // "c0" is the color of the premultiplied bitmap at (i, j).
122 const SkPMColor c0 = *bm8888.getAddr32(i, j);
123 // "c1" is the result of premultiplying the color of the unpremultiplied
124 // bitmap at (i, j).
125 const SkPMColor c1 = premultiply_unpmcolor(*bm8888Unpremul.getAddr32(i, j));
126 // Compute the difference for each component.
127 int da = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
128 int dr = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
129 int dg = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
130 int db = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
131
132 // Alpha component must be exactly the same.
133 REPORTER_ASSERT(reporter, 0 == da);
134
135 // Color components may not match exactly due to rounding error.
136 REPORTER_ASSERT(reporter, dr <= 1);
137 REPORTER_ASSERT(reporter, dg <= 1);
138 REPORTER_ASSERT(reporter, db <= 1);
139 }
140 }
141 }
142
test_unpremul(skiatest::Reporter * reporter)143 static void test_unpremul(skiatest::Reporter* reporter) {
144 // This test cannot run if there is no resource path.
145 SkString resourcePath = skiatest::Test::GetResourcePath();
146 if (resourcePath.isEmpty()) {
147 SkDebugf("Could not run unpremul test because resourcePath not specified.");
148 return;
149 }
150 SkOSFile::Iter iter(resourcePath.c_str());
151 SkString basename;
152 if (iter.next(&basename)) {
153 do {
154 SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), basename.c_str());
155 //SkDebugf("about to decode \"%s\"\n", filename.c_str());
156 compare_unpremul(reporter, filename);
157 } while (iter.next(&basename));
158 } else {
159 SkDebugf("Failed to find any files :(\n");
160 }
161 }
162
163 #ifdef SK_DEBUG
164 // Create a stream containing a bitmap encoded to Type type.
create_image_stream(SkImageEncoder::Type type)165 static SkMemoryStream* create_image_stream(SkImageEncoder::Type type) {
166 SkBitmap bm;
167 const int size = 50;
168 bm.setConfig(SkBitmap::kARGB_8888_Config, size, size);
169 bm.allocPixels();
170 SkCanvas canvas(bm);
171 SkPoint points[2] = {
172 { SkIntToScalar(0), SkIntToScalar(0) },
173 { SkIntToScalar(size), SkIntToScalar(size) }
174 };
175 SkColor colors[2] = { SK_ColorWHITE, SK_ColorBLUE };
176 SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
177 SK_ARRAY_COUNT(colors),
178 SkShader::kClamp_TileMode);
179 SkPaint paint;
180 paint.setShader(shader)->unref();
181 canvas.drawPaint(paint);
182 // Now encode it to a stream.
183 SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(bm, type, 100));
184 if (NULL == data.get()) {
185 return NULL;
186 }
187 return SkNEW_ARGS(SkMemoryStream, (data.get()));
188 }
189
190 // For every format that supports tile based decoding, ensure that
191 // calling decodeSubset will not fail if the caller has unreffed the
192 // stream provided in buildTileIndex.
193 // Only runs in debug mode since we are testing for a crash.
test_stream_life()194 static void test_stream_life() {
195 const SkImageEncoder::Type gTypes[] = {
196 #ifdef SK_BUILD_FOR_ANDROID
197 SkImageEncoder::kJPEG_Type,
198 SkImageEncoder::kPNG_Type,
199 #endif
200 SkImageEncoder::kWEBP_Type,
201 };
202 for (size_t i = 0; i < SK_ARRAY_COUNT(gTypes); ++i) {
203 //SkDebugf("encoding to %i\n", i);
204 SkAutoTUnref<SkMemoryStream> stream(create_image_stream(gTypes[i]));
205 if (NULL == stream.get()) {
206 SkDebugf("no stream\n");
207 continue;
208 }
209 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
210 if (NULL == decoder.get()) {
211 SkDebugf("no decoder\n");
212 continue;
213 }
214 int width, height;
215 if (!decoder->buildTileIndex(stream.get(), &width, &height)) {
216 SkDebugf("could not build a tile index\n");
217 continue;
218 }
219 // Now unref the stream to make sure it survives
220 stream.reset(NULL);
221 SkBitmap bm;
222 decoder->decodeSubset(&bm, SkIRect::MakeWH(width, height),
223 SkBitmap::kARGB_8888_Config);
224 }
225 }
226
227 // Test inside SkScaledBitmapSampler.cpp
228 extern void test_row_proc_choice();
229
230 #endif // SK_DEBUG
231
DEF_TEST(ImageDecoding,reporter)232 DEF_TEST(ImageDecoding, reporter) {
233 test_unpremul(reporter);
234 #ifdef SK_DEBUG
235 test_stream_life();
236 test_row_proc_choice();
237 #endif
238 }
239