| 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 |