Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Unified Diff: ui/gfx/codec/png_codec_unittest.cc

Issue 9496004: Support for interlaced PNGs (in gfx::PNGCodec) (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/codec/png_codec_unittest.cc
===================================================================
--- ui/gfx/codec/png_codec_unittest.cc (revision 124926)
+++ ui/gfx/codec/png_codec_unittest.cc (working copy)
@@ -2,11 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#if defined(USE_SYSTEM_LIBPNG)
+#include <png.h>
+#else
+#include "third_party/libpng/png.h"
+#endif
+
#include <algorithm>
#include <cmath>
+#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkUnPreMultiply.h"
#include "third_party/zlib/zlib.h"
#include "ui/gfx/codec/png_codec.h"
@@ -14,7 +22,9 @@
namespace gfx {
-static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
+namespace {
+
+void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
dat->resize(w * h * 3);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
@@ -30,8 +40,8 @@
// be filled with 0xff. With the alpha channel stripped, this should yield the
// same image as MakeRGBImage above, so the code below can make reference
// images for conversion testing.
-static void MakeRGBAImage(int w, int h, bool use_transparency,
- std::vector<unsigned char>* dat) {
+void MakeRGBAImage(int w, int h, bool use_transparency,
+ std::vector<unsigned char>* dat) {
dat->resize(w * h * 4);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
@@ -47,6 +57,144 @@
}
}
+// User write function (to be passed to libpng by EncodeImage) which writes
+// into a buffer instead of to a file.
+void WriteImageData(png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+ std::vector<unsigned char>& v =
+ *static_cast<std::vector<unsigned char>*>(png_get_io_ptr(png_ptr));
+ v.resize(v.size() + length);
+ memcpy(&v[v.size() - length], data, length);
+}
+
+// User flush function; goes with WriteImageData, above.
+void FlushImageData(png_structp /*png_ptr*/) {
+}
+
+// Libpng user error function which allows us to print libpng errors using
+// Chrome's logging facilities instead of stderr.
+void LogLibPNGError(png_structp png_ptr,
+ png_const_charp error_msg) {
+ DLOG(ERROR) << "libpng encode error: " << error_msg;
+ longjmp(png_jmpbuf(png_ptr), 1);
+}
+
+// Goes with LogLibPNGError, above.
+void LogLibPNGWarning(png_structp png_ptr,
+ png_const_charp warning_msg) {
+ DLOG(ERROR) << "libpng encode warning: " << warning_msg;
+}
+
+// Color types supported by EncodeImage. Required because neither libpng nor
+// PNGCodec::Encode supports all of the required values.
+enum ColorType {
+ COLOR_TYPE_GRAY = PNG_COLOR_TYPE_GRAY,
+ COLOR_TYPE_GRAY_ALPHA = PNG_COLOR_TYPE_GRAY_ALPHA,
+ COLOR_TYPE_PALETTE = PNG_COLOR_TYPE_PALETTE,
+ COLOR_TYPE_RGB = PNG_COLOR_TYPE_RGB,
+ COLOR_TYPE_RGBA = PNG_COLOR_TYPE_RGBA,
+ COLOR_TYPE_BGR,
+ COLOR_TYPE_BGRA
+};
+
+// PNG encoder used for testing. Required because PNGCodec::Encode doesn't do
+// interlaced, palette-based, or grayscale images, but PNGCodec::Decode is
+// actually asked to decode these types of images by Chrome.
+bool EncodeImage(const std::vector<unsigned char>& input,
+ const int width,
+ const int height,
+ ColorType output_color_type,
+ std::vector<unsigned char>* output,
+ const int interlace_type = PNG_INTERLACE_NONE,
+ std::vector<png_color>* palette = 0,
+ std::vector<unsigned char>* palette_alpha = 0) {
+ struct ScopedPNGStructs {
+ ScopedPNGStructs(png_struct** s, png_info** i) : s_(s), i_(i) {}
+ ~ScopedPNGStructs() { png_destroy_write_struct(s_, i_); }
+ png_struct** s_;
+ png_info** i_;
+ };
+
+ DCHECK(output);
+ png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (!png_ptr)
+ return false;
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return false;
+ }
+
+ ScopedPNGStructs scoped_png_structs(&png_ptr, &info_ptr);
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ return false;
+
+ png_set_error_fn(png_ptr, NULL, LogLibPNGError, LogLibPNGWarning);
+
+ int input_rowbytes = 0;
+ int transforms = PNG_TRANSFORM_IDENTITY;
+
+ switch (output_color_type) {
+ case COLOR_TYPE_GRAY:
+ input_rowbytes = width;
+ break;
+ case COLOR_TYPE_GRAY_ALPHA:
+ input_rowbytes = width * 2;
+ break;
+ case COLOR_TYPE_PALETTE:
+ if (!palette)
+ return false;
+ input_rowbytes = width;
+ break;
+ case COLOR_TYPE_RGB:
+ input_rowbytes = width * 3;
+ break;
+ case COLOR_TYPE_RGBA:
+ input_rowbytes = width * 4;
+ break;
+ case COLOR_TYPE_BGR:
+ input_rowbytes = width * 3;
+ output_color_type = static_cast<ColorType>(PNG_COLOR_TYPE_RGB);
+ transforms |= PNG_TRANSFORM_BGR;
+ break;
+ case COLOR_TYPE_BGRA:
+ input_rowbytes = width * 4;
+ output_color_type = static_cast<ColorType>(PNG_COLOR_TYPE_RGBA);
+ transforms |= PNG_TRANSFORM_BGR;
+ break;
+ };
+
+ std::vector<png_bytep> row_pointers(height);
+ for (int y = 0 ; y < height; y++) {
+ row_pointers[y] = const_cast<unsigned char*>(&input[y * input_rowbytes]);
+ }
+ png_set_rows(png_ptr, info_ptr, &row_pointers[0]);
+ png_set_write_fn(png_ptr, output, WriteImageData, FlushImageData);
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8, output_color_type,
+ interlace_type, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ if (output_color_type == COLOR_TYPE_PALETTE) {
+ png_set_PLTE(png_ptr, info_ptr, &palette->front(), palette->size());
+ if (palette_alpha) {
+ png_set_tRNS(png_ptr,
+ info_ptr,
+ &palette_alpha->front(),
+ palette_alpha->size(),
+ NULL);
+ }
+ }
+
+ png_write_png(png_ptr, info_ptr, transforms, NULL);
+
+ return true;
+}
+
+} // namespace
+
// Returns true if each channel of the given two colors are "close." This is
// used for comparing colors where rounding errors may cause off-by-one.
bool ColorsClose(uint32_t a, uint32_t b) {
@@ -82,7 +230,7 @@
// encode
std::vector<unsigned char> encoded;
- EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
+ ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
Size(w, h), w * 3, false,
std::vector<PNGCodec::Comment>(),
&encoded));
@@ -90,7 +238,7 @@
// decode, it should have the same size as the original
std::vector<unsigned char> decoded;
int outw, outh;
- EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
PNGCodec::FORMAT_RGB, &decoded,
&outw, &outh));
ASSERT_EQ(w, outw);
@@ -111,7 +259,7 @@
// encode
std::vector<unsigned char> encoded;
- EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA,
+ ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA,
Size(w, h), w * 4, false,
std::vector<PNGCodec::Comment>(),
&encoded));
@@ -119,7 +267,7 @@
// decode, it should have the same size as the original
std::vector<unsigned char> decoded;
int outw, outh;
- EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
PNGCodec::FORMAT_RGBA, &decoded,
&outw, &outh));
ASSERT_EQ(w, outw);
@@ -130,6 +278,264 @@
ASSERT_TRUE(original == decoded);
}
+TEST(PNGCodec, EncodeDecodeBGRA) {
+ const int w = 20, h = 20;
+
+ // Create an image with known values, alpha must be opaque because it will be
+ // lost during encoding.
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, true, &original);
+
+ // Encode.
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_BGRA,
+ Size(w, h), w * 4, false,
+ std::vector<PNGCodec::Comment>(),
+ &encoded));
+
+ // Decode, it should have the same size as the original.
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_BGRA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be exactly equal.
+ ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, DecodeInterlacedRGB) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_RGB,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGB, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be equal
+ ASSERT_EQ(original, decoded);
+}
+
+TEST(PNGCodec, DecodeInterlacedRGBA) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, false, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_RGBA,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGBA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be equal
+ ASSERT_EQ(original, decoded);
+}
+
+TEST(PNGCodec, DecodeInterlacedRGBADiscardAlpha) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, false, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_RGBA,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // decode
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_RGB, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(decoded.size(), w * h * 3U);
+
+ // Images must be equal
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ unsigned char* orig_px = &original[(y * w + x) * 4];
+ unsigned char* dec_px = &decoded[(y * w + x) * 3];
+ ASSERT_EQ(dec_px[0], orig_px[0]);
+ ASSERT_EQ(dec_px[1], orig_px[1]);
+ ASSERT_EQ(dec_px[2], orig_px[2]);
+ }
+ }
+}
+
+TEST(PNGCodec, DecodeInterlacedBGR) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_BGR,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_BGRA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(decoded.size(), w * h * 4U);
+
+ // Images must be equal
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ unsigned char* orig_px = &original[(y * w + x) * 3];
+ unsigned char* dec_px = &decoded[(y * w + x) * 4];
+ ASSERT_EQ(dec_px[0], orig_px[0]);
+ ASSERT_EQ(dec_px[1], orig_px[1]);
+ ASSERT_EQ(dec_px[2], orig_px[2]);
+ }
+ }
+}
+
+TEST(PNGCodec, DecodeInterlacedBGRA) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, false, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_BGRA,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
+ PNGCodec::FORMAT_BGRA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be equal
+ ASSERT_EQ(original, decoded);
+}
+
+// Not encoding an interlaced PNG from SkBitmap because we don't do it
+// anywhere, and the ability to do that requires more code changes.
+TEST(PNGCodec, DecodeInterlacedRGBtoSkBitmap) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_RGB,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // Decode the encoded string.
+ SkBitmap decoded_bitmap;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
+ &decoded_bitmap));
+
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ const unsigned char* original_pixel = &original[(y * w + x) * 3];
+ const uint32_t original_pixel_sk = SkPackARGB32(0xFF,
+ original_pixel[0],
+ original_pixel[1],
+ original_pixel[2]);
+ const uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
+ ASSERT_EQ(original_pixel_sk, decoded_pixel);
+ }
+ }
+}
+
+TEST(PNGCodec, DecodeInterlacedRGBAtoSkBitmap) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, false, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ ASSERT_TRUE(EncodeImage(original,
+ w, h,
+ COLOR_TYPE_RGBA,
+ &encoded,
+ PNG_INTERLACE_ADAM7));
+
+ // Decode the encoded string.
+ SkBitmap decoded_bitmap;
+ ASSERT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
+ &decoded_bitmap));
+
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ const unsigned char* original_pixel = &original[(y * w + x) * 4];
+ const uint32_t original_pixel_sk = SkPackARGB32(original_pixel[3],
+ original_pixel[0],
+ original_pixel[1],
+ original_pixel[2]);
+ const uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
+ ASSERT_EQ(original_pixel_sk, decoded_pixel);
+ }
+ }
+}
+
// Test that corrupted data decompression causes failures.
TEST(PNGCodec, DecodeCorrupted) {
int w = 20, h = 20;
@@ -147,7 +553,7 @@
// Make some compressed data.
std::vector<unsigned char> compressed;
- EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
+ ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
Size(w, h), w * 3, false,
std::vector<PNGCodec::Comment>(),
&compressed));
@@ -165,35 +571,6 @@
&outw, &outh));
}
-TEST(PNGCodec, EncodeDecodeBGRA) {
- const int w = 20, h = 20;
-
- // Create an image with known values, alpha must be opaque because it will be
- // lost during encoding.
- std::vector<unsigned char> original;
- MakeRGBAImage(w, h, true, &original);
-
- // Encode.
- std::vector<unsigned char> encoded;
- EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_BGRA,
- Size(w, h), w * 4, false,
- std::vector<PNGCodec::Comment>(),
- &encoded));
-
- // Decode, it should have the same size as the original.
- std::vector<unsigned char> decoded;
- int outw, outh;
- EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
- PNGCodec::FORMAT_BGRA, &decoded,
- &outw, &outh));
- ASSERT_EQ(w, outw);
- ASSERT_EQ(h, outh);
- ASSERT_EQ(original.size(), decoded.size());
-
- // Images must be exactly equal.
- ASSERT_TRUE(original == decoded);
-}
-
TEST(PNGCodec, StripAddAlpha) {
const int w = 20, h = 20;
@@ -221,7 +598,7 @@
ASSERT_EQ(w, outw);
ASSERT_EQ(h, outh);
ASSERT_EQ(original_rgba.size(), decoded.size());
- ASSERT_TRUE(original_rgba == decoded);
+ ASSERT_EQ(original_rgba, decoded);
// Encode RGBA to RGBA.
EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], PNGCodec::FORMAT_RGBA,
@@ -238,7 +615,7 @@
ASSERT_EQ(w, outw);
ASSERT_EQ(h, outh);
ASSERT_EQ(original_rgb.size(), decoded.size());
- ASSERT_TRUE(original_rgb == decoded);
+ ASSERT_EQ(original_rgb, decoded);
}
TEST(PNGCodec, EncodeBGRASkBitmap) {
« ui/gfx/codec/png_codec.cc ('K') | « ui/gfx/codec/png_codec.cc ('k') | ui/ui_unittests.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698