• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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