OLD | NEW |
| (Empty) |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <math.h> | |
6 | |
7 #include "gfx/codec/png_codec.h" | |
8 #include "testing/gtest/include/gtest/gtest.h" | |
9 #include "third_party/skia/include/core/SkBitmap.h" | |
10 #include "third_party/skia/include/core/SkUnPreMultiply.h" | |
11 | |
12 namespace gfx { | |
13 | |
14 static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) { | |
15 dat->resize(w * h * 3); | |
16 for (int y = 0; y < h; y++) { | |
17 for (int x = 0; x < w; x++) { | |
18 unsigned char* org_px = &(*dat)[(y * w + x) * 3]; | |
19 org_px[0] = x * 3; // r | |
20 org_px[1] = x * 3 + 1; // g | |
21 org_px[2] = x * 3 + 2; // b | |
22 } | |
23 } | |
24 } | |
25 | |
26 // Set use_transparency to write data into the alpha channel, otherwise it will | |
27 // be filled with 0xff. With the alpha channel stripped, this should yield the | |
28 // same image as MakeRGBImage above, so the code below can make reference | |
29 // images for conversion testing. | |
30 static void MakeRGBAImage(int w, int h, bool use_transparency, | |
31 std::vector<unsigned char>* dat) { | |
32 dat->resize(w * h * 4); | |
33 for (int y = 0; y < h; y++) { | |
34 for (int x = 0; x < w; x++) { | |
35 unsigned char* org_px = &(*dat)[(y * w + x) * 4]; | |
36 org_px[0] = x * 3; // r | |
37 org_px[1] = x * 3 + 1; // g | |
38 org_px[2] = x * 3 + 2; // b | |
39 if (use_transparency) | |
40 org_px[3] = x*3 + 3; // a | |
41 else | |
42 org_px[3] = 0xFF; // a (opaque) | |
43 } | |
44 } | |
45 } | |
46 | |
47 // Returns true if each channel of the given two colors are "close." This is | |
48 // used for comparing colors where rounding errors may cause off-by-one. | |
49 bool ColorsClose(uint32_t a, uint32_t b) { | |
50 return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 && | |
51 abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 && | |
52 abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 && | |
53 abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2; | |
54 } | |
55 | |
56 // Returns true if the RGB components are "close." | |
57 bool NonAlphaColorsClose(uint32_t a, uint32_t b) { | |
58 return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 && | |
59 abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 && | |
60 abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2; | |
61 } | |
62 | |
63 void MakeTestSkBitmap(int w, int h, SkBitmap* bmp) { | |
64 bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h); | |
65 bmp->allocPixels(); | |
66 | |
67 uint32_t* src_data = bmp->getAddr32(0, 0); | |
68 for (int i = 0; i < w * h; i++) { | |
69 src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240); | |
70 } | |
71 } | |
72 | |
73 TEST(PNGCodec, EncodeDecodeRGB) { | |
74 const int w = 20, h = 20; | |
75 | |
76 // create an image with known values | |
77 std::vector<unsigned char> original; | |
78 MakeRGBImage(w, h, &original); | |
79 | |
80 // encode | |
81 std::vector<unsigned char> encoded; | |
82 EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB, w, h, | |
83 w * 3, false, &encoded)); | |
84 | |
85 // decode, it should have the same size as the original | |
86 std::vector<unsigned char> decoded; | |
87 int outw, outh; | |
88 EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(), | |
89 PNGCodec::FORMAT_RGB, &decoded, | |
90 &outw, &outh)); | |
91 ASSERT_EQ(w, outw); | |
92 ASSERT_EQ(h, outh); | |
93 ASSERT_EQ(original.size(), decoded.size()); | |
94 | |
95 // Images must be equal | |
96 ASSERT_TRUE(original == decoded); | |
97 } | |
98 | |
99 TEST(PNGCodec, EncodeDecodeRGBA) { | |
100 const int w = 20, h = 20; | |
101 | |
102 // create an image with known values, a must be opaque because it will be | |
103 // lost during encoding | |
104 std::vector<unsigned char> original; | |
105 MakeRGBAImage(w, h, true, &original); | |
106 | |
107 // encode | |
108 std::vector<unsigned char> encoded; | |
109 EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA, w, h, | |
110 w * 4, false, &encoded)); | |
111 | |
112 // decode, it should have the same size as the original | |
113 std::vector<unsigned char> decoded; | |
114 int outw, outh; | |
115 EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(), | |
116 PNGCodec::FORMAT_RGBA, &decoded, | |
117 &outw, &outh)); | |
118 ASSERT_EQ(w, outw); | |
119 ASSERT_EQ(h, outh); | |
120 ASSERT_EQ(original.size(), decoded.size()); | |
121 | |
122 // Images must be exactly equal | |
123 ASSERT_TRUE(original == decoded); | |
124 } | |
125 | |
126 // Test that corrupted data decompression causes failures. | |
127 TEST(PNGCodec, DecodeCorrupted) { | |
128 int w = 20, h = 20; | |
129 | |
130 // Make some random data (an uncompressed image). | |
131 std::vector<unsigned char> original; | |
132 MakeRGBImage(w, h, &original); | |
133 | |
134 // It should fail when given non-JPEG compressed data. | |
135 std::vector<unsigned char> output; | |
136 int outw, outh; | |
137 EXPECT_FALSE(PNGCodec::Decode(&original[0], original.size(), | |
138 PNGCodec::FORMAT_RGB, &output, | |
139 &outw, &outh)); | |
140 | |
141 // Make some compressed data. | |
142 std::vector<unsigned char> compressed; | |
143 EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB, w, h, | |
144 w * 3, false, &compressed)); | |
145 | |
146 // Try decompressing a truncated version. | |
147 EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size() / 2, | |
148 PNGCodec::FORMAT_RGB, &output, | |
149 &outw, &outh)); | |
150 | |
151 // Corrupt it and try decompressing that. | |
152 for (int i = 10; i < 30; i++) | |
153 compressed[i] = i; | |
154 EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size(), | |
155 PNGCodec::FORMAT_RGB, &output, | |
156 &outw, &outh)); | |
157 } | |
158 | |
159 TEST(PNGCodec, EncodeDecodeBGRA) { | |
160 const int w = 20, h = 20; | |
161 | |
162 // Create an image with known values, alpha must be opaque because it will be | |
163 // lost during encoding. | |
164 std::vector<unsigned char> original; | |
165 MakeRGBAImage(w, h, true, &original); | |
166 | |
167 // Encode. | |
168 std::vector<unsigned char> encoded; | |
169 EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_BGRA, w, h, | |
170 w * 4, false, &encoded)); | |
171 | |
172 // Decode, it should have the same size as the original. | |
173 std::vector<unsigned char> decoded; | |
174 int outw, outh; | |
175 EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(), | |
176 PNGCodec::FORMAT_BGRA, &decoded, | |
177 &outw, &outh)); | |
178 ASSERT_EQ(w, outw); | |
179 ASSERT_EQ(h, outh); | |
180 ASSERT_EQ(original.size(), decoded.size()); | |
181 | |
182 // Images must be exactly equal. | |
183 ASSERT_TRUE(original == decoded); | |
184 } | |
185 | |
186 TEST(PNGCodec, StripAddAlpha) { | |
187 const int w = 20, h = 20; | |
188 | |
189 // These should be the same except one has a 0xff alpha channel. | |
190 std::vector<unsigned char> original_rgb; | |
191 MakeRGBImage(w, h, &original_rgb); | |
192 std::vector<unsigned char> original_rgba; | |
193 MakeRGBAImage(w, h, false, &original_rgba); | |
194 | |
195 // Encode RGBA data as RGB. | |
196 std::vector<unsigned char> encoded; | |
197 EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], | |
198 PNGCodec::FORMAT_RGBA, | |
199 w, h, | |
200 w * 4, true, &encoded)); | |
201 | |
202 // Decode the RGB to RGBA. | |
203 std::vector<unsigned char> decoded; | |
204 int outw, outh; | |
205 EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(), | |
206 PNGCodec::FORMAT_RGBA, &decoded, | |
207 &outw, &outh)); | |
208 | |
209 // Decoded and reference should be the same (opaque alpha). | |
210 ASSERT_EQ(w, outw); | |
211 ASSERT_EQ(h, outh); | |
212 ASSERT_EQ(original_rgba.size(), decoded.size()); | |
213 ASSERT_TRUE(original_rgba == decoded); | |
214 | |
215 // Encode RGBA to RGBA. | |
216 EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], | |
217 PNGCodec::FORMAT_RGBA, | |
218 w, h, | |
219 w * 4, false, &encoded)); | |
220 | |
221 // Decode the RGBA to RGB. | |
222 EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(), | |
223 PNGCodec::FORMAT_RGB, &decoded, | |
224 &outw, &outh)); | |
225 | |
226 // It should be the same as our non-alpha-channel reference. | |
227 ASSERT_EQ(w, outw); | |
228 ASSERT_EQ(h, outh); | |
229 ASSERT_EQ(original_rgb.size(), decoded.size()); | |
230 ASSERT_TRUE(original_rgb == decoded); | |
231 } | |
232 | |
233 TEST(PNGCodec, EncodeBGRASkBitmap) { | |
234 const int w = 20, h = 20; | |
235 | |
236 SkBitmap original_bitmap; | |
237 MakeTestSkBitmap(w, h, &original_bitmap); | |
238 | |
239 // Encode the bitmap. | |
240 std::vector<unsigned char> encoded; | |
241 PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded); | |
242 | |
243 // Decode the encoded string. | |
244 SkBitmap decoded_bitmap; | |
245 EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(), | |
246 &decoded_bitmap)); | |
247 | |
248 // Compare the original bitmap and the output bitmap. We use ColorsClose | |
249 // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication | |
250 // (in Encode) and repremultiplication (in Decode) can be lossy. | |
251 for (int x = 0; x < w; x++) { | |
252 for (int y = 0; y < h; y++) { | |
253 uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x]; | |
254 uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x]; | |
255 EXPECT_TRUE(ColorsClose(original_pixel, decoded_pixel)); | |
256 } | |
257 } | |
258 } | |
259 | |
260 TEST(PNGCodec, EncodeBGRASkBitmapDiscardTransparency) { | |
261 const int w = 20, h = 20; | |
262 | |
263 SkBitmap original_bitmap; | |
264 MakeTestSkBitmap(w, h, &original_bitmap); | |
265 | |
266 // Encode the bitmap. | |
267 std::vector<unsigned char> encoded; | |
268 PNGCodec::EncodeBGRASkBitmap(original_bitmap, true, &encoded); | |
269 | |
270 // Decode the encoded string. | |
271 SkBitmap decoded_bitmap; | |
272 EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(), | |
273 &decoded_bitmap)); | |
274 | |
275 // Compare the original bitmap and the output bitmap. We need to | |
276 // unpremultiply original_pixel, as the decoded bitmap doesn't have an alpha | |
277 // channel. | |
278 for (int x = 0; x < w; x++) { | |
279 for (int y = 0; y < h; y++) { | |
280 uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x]; | |
281 uint32_t unpremultiplied = | |
282 SkUnPreMultiply::PMColorToColor(original_pixel); | |
283 uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x]; | |
284 EXPECT_TRUE(NonAlphaColorsClose(unpremultiplied, decoded_pixel)) | |
285 << "Original_pixel: (" | |
286 << SkColorGetR(unpremultiplied) << ", " | |
287 << SkColorGetG(unpremultiplied) << ", " | |
288 << SkColorGetB(unpremultiplied) << "), " | |
289 << "Decoded pixel: (" | |
290 << SkColorGetR(decoded_pixel) << ", " | |
291 << SkColorGetG(decoded_pixel) << ", " | |
292 << SkColorGetB(decoded_pixel) << ")"; | |
293 } | |
294 } | |
295 } | |
296 | |
297 } // namespace gfx | |
OLD | NEW |