This source file includes following definitions.
- MakeRGBImage
- MakeRGBAImage
- MakePaletteImage
- MakeGrayscaleImage
- MakeGrayscaleAlphaImage
- WriteImageData
- FlushImageData
- LogLibPNGError
- LogLibPNGWarning
- EncodeImage
- ColorsClose
- NonAlphaColorsClose
- BGRAGrayEqualsA8Gray
- MakeTestBGRASkBitmap
- MakeTestA8SkBitmap
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
#include <algorithm>
#include <cmath>
#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libpng/png.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"
#include "ui/gfx/size.h"
#include "ui/gfx/skia_util.h"
namespace gfx {
namespace {
void MakeRGBImage(int w, int h, std::vector<unsigned char>* data) {
data->resize(w * h * 3);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
unsigned char* org_px = &(*data)[(y * w + x) * 3];
org_px[0] = x * 3;
org_px[1] = x * 3 + 1;
org_px[2] = x * 3 + 2;
}
}
}
void MakeRGBAImage(int w, int h, bool use_transparency,
std::vector<unsigned char>* data) {
data->resize(w * h * 4);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
unsigned char* org_px = &(*data)[(y * w + x) * 4];
org_px[0] = x * 3;
org_px[1] = x * 3 + 1;
org_px[2] = x * 3 + 2;
if (use_transparency)
org_px[3] = x*3 + 3;
else
org_px[3] = 0xFF;
}
}
}
void MakePaletteImage(int w, int h,
std::vector<unsigned char>* data,
std::vector<png_color>* palette,
std::vector<unsigned char>* trans_chunk = 0) {
data->resize(w * h);
palette->resize(w);
for (int i = 0; i < w; ++i) {
png_color& color = (*palette)[i];
color.red = i * 3;
color.green = color.red + 1;
color.blue = color.red + 2;
}
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
(*data)[y * w + x] = x;
}
}
if (trans_chunk) {
trans_chunk->resize(palette->size());
for (std::size_t i = 0; i < trans_chunk->size(); ++i) {
(*trans_chunk)[i] = i % 256;
}
}
}
void MakeGrayscaleImage(int w, int h,
std::vector<unsigned char>* data) {
data->resize(w * h);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
(*data)[y * w + x] = x;
}
}
}
void MakeGrayscaleAlphaImage(int w, int h,
std::vector<unsigned char>* data) {
data->resize(w * h * 2);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
unsigned char* px = &(*data)[(y * w + x) * 2];
px[0] = x;
px[1] = x % 256;
}
}
}
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);
}
void FlushImageData(png_structp ) {
}
void LogLibPNGError(png_structp png_ptr,
png_const_charp error_msg) {
DLOG(ERROR) << "libpng encode error: " << error_msg;
longjmp(png_jmpbuf(png_ptr), 1);
}
void LogLibPNGWarning(png_structp png_ptr,
png_const_charp warning_msg) {
DLOG(ERROR) << "libpng encode warning: " << warning_msg;
}
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
};
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) {
DCHECK(output);
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;
};
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;
}
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]);
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
return false;
}
png_set_error_fn(png_ptr, NULL, LogLibPNGError, LogLibPNGWarning);
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) {
unsigned char* alpha_data = &palette_alpha->front();
size_t alpha_size = palette_alpha->size();
png_set_tRNS(png_ptr, info_ptr, alpha_data, alpha_size, NULL);
}
}
png_write_png(png_ptr, info_ptr, transforms, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
return true;
}
}
bool ColorsClose(uint32_t a, uint32_t b) {
return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 &&
abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2;
}
bool NonAlphaColorsClose(uint32_t a, uint32_t b) {
return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2;
}
bool BGRAGrayEqualsA8Gray(uint32_t a, uint8_t b) {
return SkColorGetB(a) == b && SkColorGetG(a) == b &&
SkColorGetR(a) == b && SkColorGetA(a) == 255;
}
void MakeTestBGRASkBitmap(int w, int h, SkBitmap* bmp) {
bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
bmp->allocPixels();
uint32_t* src_data = bmp->getAddr32(0, 0);
for (int i = 0; i < w * h; i++)
src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
}
void MakeTestA8SkBitmap(int w, int h, SkBitmap* bmp) {
bmp->setConfig(SkBitmap::kA8_Config, w, h);
bmp->allocPixels();
uint8_t* src_data = bmp->getAddr8(0, 0);
for (int i = 0; i < w * h; i++)
src_data[i] = i % 255;
}
TEST(PNGCodec, EncodeDecodeRGB) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
Size(w, h), w * 3, false,
std::vector<PNGCodec::Comment>(),
&encoded));
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());
ASSERT_TRUE(original == decoded);
}
TEST(PNGCodec, EncodeDecodeRGBA) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBAImage(w, h, true, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA,
Size(w, h), w * 4, false,
std::vector<PNGCodec::Comment>(),
&encoded));
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());
ASSERT_TRUE(original == decoded);
}
TEST(PNGCodec, EncodeDecodeBGRA) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBAImage(w, h, true, &original);
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));
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());
ASSERT_TRUE(original == decoded);
}
TEST(PNGCodec, DecodePalette) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
std::vector<png_color> original_palette;
std::vector<unsigned char> original_trans_chunk;
MakePaletteImage(w, h, &original, &original_palette, &original_trans_chunk);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_PALETTE,
&encoded,
PNG_INTERLACE_NONE,
&original_palette,
&original_trans_chunk));
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(decoded.size(), w * h * 4U);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char palette_pixel = original[y * w + x];
png_color& palette_color = original_palette[palette_pixel];
int alpha = original_trans_chunk[palette_pixel];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
EXPECT_EQ(palette_color.red, rgba_pixel[0]);
EXPECT_EQ(palette_color.green, rgba_pixel[1]);
EXPECT_EQ(palette_color.blue, rgba_pixel[2]);
EXPECT_EQ(alpha, rgba_pixel[3]);
}
}
}
TEST(PNGCodec, DecodePaletteDiscardAlpha) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
std::vector<png_color> original_palette;
std::vector<unsigned char> original_trans_chunk;
MakePaletteImage(w, h, &original, &original_palette, &original_trans_chunk);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_PALETTE,
&encoded,
PNG_INTERLACE_NONE,
&original_palette,
&original_trans_chunk));
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);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char palette_pixel = original[y * w + x];
png_color& palette_color = original_palette[palette_pixel];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 3];
EXPECT_EQ(palette_color.red, rgba_pixel[0]);
EXPECT_EQ(palette_color.green, rgba_pixel[1]);
EXPECT_EQ(palette_color.blue, rgba_pixel[2]);
}
}
}
TEST(PNGCodec, DecodeInterlacedPalette) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
std::vector<png_color> original_palette;
std::vector<unsigned char> original_trans_chunk;
MakePaletteImage(w, h, &original, &original_palette, &original_trans_chunk);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_PALETTE,
&encoded,
PNG_INTERLACE_ADAM7,
&original_palette,
&original_trans_chunk));
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(decoded.size(), w * h * 4U);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char palette_pixel = original[y * w + x];
png_color& palette_color = original_palette[palette_pixel];
int alpha = original_trans_chunk[palette_pixel];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
EXPECT_EQ(palette_color.red, rgba_pixel[0]);
EXPECT_EQ(palette_color.green, rgba_pixel[1]);
EXPECT_EQ(palette_color.blue, rgba_pixel[2]);
EXPECT_EQ(alpha, rgba_pixel[3]);
}
}
}
TEST(PNGCodec, DecodeGrayscale) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeGrayscaleImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original, w, h, COLOR_TYPE_GRAY, &encoded));
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(), original.size() * 3);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char gray_pixel = original[(y * w + x)];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 3];
EXPECT_EQ(rgba_pixel[0], gray_pixel);
EXPECT_EQ(rgba_pixel[1], gray_pixel);
EXPECT_EQ(rgba_pixel[2], gray_pixel);
}
}
}
TEST(PNGCodec, DecodeGrayscaleWithAlpha) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeGrayscaleAlphaImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_GRAY_ALPHA,
&encoded));
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(decoded.size(), original.size() * 2);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char* gray_pixel = &original[(y * w + x) * 2];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
EXPECT_EQ(rgba_pixel[0], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[1], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[2], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[3], gray_pixel[1]);
}
}
}
TEST(PNGCodec, DecodeGrayscaleWithAlphaDiscardAlpha) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeGrayscaleAlphaImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_GRAY_ALPHA,
&encoded));
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);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char* gray_pixel = &original[(y * w + x) * 2];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 3];
EXPECT_EQ(rgba_pixel[0], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[1], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[2], gray_pixel[0]);
}
}
}
TEST(PNGCodec, DecodeInterlacedGrayscale) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeGrayscaleImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_GRAY,
&encoded,
PNG_INTERLACE_ADAM7));
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(decoded.size(), original.size() * 4);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char gray_pixel = original[(y * w + x)];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
EXPECT_EQ(rgba_pixel[0], gray_pixel);
EXPECT_EQ(rgba_pixel[1], gray_pixel);
EXPECT_EQ(rgba_pixel[2], gray_pixel);
EXPECT_EQ(rgba_pixel[3], 0xFF);
}
}
}
TEST(PNGCodec, DecodeInterlacedGrayscaleWithAlpha) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeGrayscaleAlphaImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_GRAY_ALPHA,
&encoded,
PNG_INTERLACE_ADAM7));
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(decoded.size(), original.size() * 2);
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
unsigned char* gray_pixel = &original[(y * w + x) * 2];
unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
EXPECT_EQ(rgba_pixel[0], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[1], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[2], gray_pixel[0]);
EXPECT_EQ(rgba_pixel[3], gray_pixel[1]);
}
}
}
TEST(PNGCodec, DecodeInterlacedRGB) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_RGB,
&encoded,
PNG_INTERLACE_ADAM7));
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());
ASSERT_EQ(original, decoded);
}
TEST(PNGCodec, DecodeInterlacedRGBA) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBAImage(w, h, false, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_RGBA,
&encoded,
PNG_INTERLACE_ADAM7));
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());
ASSERT_EQ(original, decoded);
}
TEST(PNGCodec, DecodeInterlacedRGBADiscardAlpha) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBAImage(w, h, false, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_RGBA,
&encoded,
PNG_INTERLACE_ADAM7));
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);
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];
EXPECT_EQ(dec_px[0], orig_px[0]);
EXPECT_EQ(dec_px[1], orig_px[1]);
EXPECT_EQ(dec_px[2], orig_px[2]);
}
}
}
TEST(PNGCodec, DecodeInterlacedBGR) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_BGR,
&encoded,
PNG_INTERLACE_ADAM7));
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);
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];
EXPECT_EQ(dec_px[0], orig_px[0]);
EXPECT_EQ(dec_px[1], orig_px[1]);
EXPECT_EQ(dec_px[2], orig_px[2]);
}
}
}
TEST(PNGCodec, DecodeInterlacedBGRA) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBAImage(w, h, false, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_BGRA,
&encoded,
PNG_INTERLACE_ADAM7));
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());
ASSERT_EQ(original, decoded);
}
TEST(PNGCodec, DecodeInterlacedRGBtoSkBitmap) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBImage(w, h, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_RGB,
&encoded,
PNG_INTERLACE_ADAM7));
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];
EXPECT_EQ(original_pixel_sk, decoded_pixel);
}
}
}
TEST(PNGCodec, DecodeInterlacedRGBAtoSkBitmap) {
const int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBAImage(w, h, false, &original);
std::vector<unsigned char> encoded;
ASSERT_TRUE(EncodeImage(original,
w, h,
COLOR_TYPE_RGBA,
&encoded,
PNG_INTERLACE_ADAM7));
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];
EXPECT_EQ(original_pixel_sk, decoded_pixel);
}
}
}
TEST(PNGCodec, DecodeCorrupted) {
int w = 20, h = 20;
std::vector<unsigned char> original;
MakeRGBImage(w, h, &original);
std::vector<unsigned char> output;
int outw, outh;
EXPECT_FALSE(PNGCodec::Decode(&original[0], original.size(),
PNGCodec::FORMAT_RGB, &output,
&outw, &outh));
std::vector<unsigned char> compressed;
ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
Size(w, h), w * 3, false,
std::vector<PNGCodec::Comment>(),
&compressed));
EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size() / 2,
PNGCodec::FORMAT_RGB, &output,
&outw, &outh));
for (int i = 10; i < 30; i++)
compressed[i] = i;
EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size(),
PNGCodec::FORMAT_RGB, &output,
&outw, &outh));
}
TEST(PNGCodec, StripAddAlpha) {
const int w = 20, h = 20;
std::vector<unsigned char> original_rgb;
MakeRGBImage(w, h, &original_rgb);
std::vector<unsigned char> original_rgba;
MakeRGBAImage(w, h, false, &original_rgba);
std::vector<unsigned char> encoded;
EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], PNGCodec::FORMAT_RGBA,
Size(w, h), w * 4, true,
std::vector<PNGCodec::Comment>(),
&encoded));
std::vector<unsigned char> decoded;
int outw, outh;
EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
PNGCodec::FORMAT_RGBA, &decoded,
&outw, &outh));
ASSERT_EQ(w, outw);
ASSERT_EQ(h, outh);
ASSERT_EQ(original_rgba.size(), decoded.size());
ASSERT_EQ(original_rgba, decoded);
EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], PNGCodec::FORMAT_RGBA,
Size(w, h), w * 4, false,
std::vector<PNGCodec::Comment>(),
&encoded));
EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
PNGCodec::FORMAT_RGB, &decoded,
&outw, &outh));
ASSERT_EQ(w, outw);
ASSERT_EQ(h, outh);
ASSERT_EQ(original_rgb.size(), decoded.size());
ASSERT_EQ(original_rgb, decoded);
}
TEST(PNGCodec, EncodeBGRASkBitmapStridePadded) {
const int kWidth = 20;
const int kHeight = 20;
const int kPaddedWidth = 32;
const int kBytesPerPixel = 4;
const int kPaddedSize = kPaddedWidth * kHeight;
const int kRowBytes = kPaddedWidth * kBytesPerPixel;
SkBitmap original_bitmap;
original_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
kWidth, kHeight, kRowBytes);
original_bitmap.allocPixels();
uint32_t* src_data = original_bitmap.getAddr32(0, 0);
for (int i = 0; i < kPaddedSize; i++) {
src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
}
std::vector<unsigned char> encoded;
PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
SkBitmap decoded_bitmap;
EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
&decoded_bitmap));
for (int x = 0; x < kWidth; x++) {
for (int y = 0; y < kHeight; y++) {
uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
EXPECT_TRUE(ColorsClose(original_pixel, decoded_pixel));
}
}
}
TEST(PNGCodec, EncodeBGRASkBitmap) {
const int w = 20, h = 20;
SkBitmap original_bitmap;
MakeTestBGRASkBitmap(w, h, &original_bitmap);
std::vector<unsigned char> encoded;
PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
SkBitmap decoded_bitmap;
EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
&decoded_bitmap));
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
EXPECT_TRUE(ColorsClose(original_pixel, decoded_pixel));
}
}
}
TEST(PNGCodec, EncodeA8SkBitmap) {
const int w = 20, h = 20;
SkBitmap original_bitmap;
MakeTestA8SkBitmap(w, h, &original_bitmap);
std::vector<unsigned char> encoded;
EXPECT_TRUE(PNGCodec::EncodeA8SkBitmap(original_bitmap, &encoded));
SkBitmap decoded_bitmap;
EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
&decoded_bitmap));
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
uint8_t original_pixel = *original_bitmap.getAddr8(x, y);
uint32_t decoded_pixel = *decoded_bitmap.getAddr32(x, y);
EXPECT_TRUE(BGRAGrayEqualsA8Gray(decoded_pixel, original_pixel));
}
}
}
TEST(PNGCodec, EncodeBGRASkBitmapDiscardTransparency) {
const int w = 20, h = 20;
SkBitmap original_bitmap;
MakeTestBGRASkBitmap(w, h, &original_bitmap);
std::vector<unsigned char> encoded;
PNGCodec::EncodeBGRASkBitmap(original_bitmap, true, &encoded);
SkBitmap decoded_bitmap;
EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
&decoded_bitmap));
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
uint32_t unpremultiplied =
SkUnPreMultiply::PMColorToColor(original_pixel);
uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
uint32_t unpremultiplied_decoded =
SkUnPreMultiply::PMColorToColor(decoded_pixel);
EXPECT_TRUE(NonAlphaColorsClose(unpremultiplied, unpremultiplied_decoded))
<< "Original_pixel: ("
<< SkColorGetR(unpremultiplied) << ", "
<< SkColorGetG(unpremultiplied) << ", "
<< SkColorGetB(unpremultiplied) << "), "
<< "Decoded pixel: ("
<< SkColorGetR(unpremultiplied_decoded) << ", "
<< SkColorGetG(unpremultiplied_decoded) << ", "
<< SkColorGetB(unpremultiplied_decoded) << ")";
}
}
}
TEST(PNGCodec, EncodeWithComment) {
const int w = 10, h = 10;
std::vector<unsigned char> original;
MakeRGBImage(w, h, &original);
std::vector<unsigned char> encoded;
std::vector<PNGCodec::Comment> comments;
comments.push_back(PNGCodec::Comment("key", "text"));
comments.push_back(PNGCodec::Comment("test", "something"));
comments.push_back(PNGCodec::Comment("have some", "spaces in both"));
EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
Size(w, h), w * 3, false, comments, &encoded));
const unsigned char kExpected1[] =
"\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51";
const unsigned char kExpected2[] =
"\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac";
const unsigned char kExpected3[] =
"\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d";
EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected1,
kExpected1 + arraysize(kExpected1)),
encoded.end());
EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected2,
kExpected2 + arraysize(kExpected2)),
encoded.end());
EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected3,
kExpected3 + arraysize(kExpected3)),
encoded.end());
}
TEST(PNGCodec, EncodeDecodeWithVaryingCompressionLevels) {
const int w = 20, h = 20;
SkBitmap original_bitmap;
MakeTestBGRASkBitmap(w, h, &original_bitmap);
std::vector<unsigned char> encoded_normal;
EXPECT_TRUE(
PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded_normal));
std::vector<unsigned char> encoded_fast;
EXPECT_TRUE(
PNGCodec::FastEncodeBGRASkBitmap(original_bitmap, false, &encoded_fast));
EXPECT_NE(encoded_normal.size(), encoded_fast.size());
SkBitmap decoded;
EXPECT_TRUE(
PNGCodec::Decode(&encoded_normal[0], encoded_normal.size(), &decoded));
EXPECT_TRUE(BitmapsAreEqual(decoded, original_bitmap));
EXPECT_TRUE(
PNGCodec::Decode(&encoded_fast[0], encoded_fast.size(), &decoded));
EXPECT_TRUE(BitmapsAreEqual(decoded, original_bitmap));
}
}