123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/gfx/codec/png_codec.h"
- #include <stddef.h>
- #include <stdint.h>
- #include <algorithm>
- #include <cmath>
- #include "base/check.h"
- #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/geometry/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; // r
- org_px[1] = x * 3 + 1; // g
- org_px[2] = x * 3 + 2; // b
- }
- }
- }
- // Set use_transparency to write data into the alpha channel, otherwise it will
- // 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.
- 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; // r
- org_px[1] = x * 3 + 1; // g
- org_px[2] = x * 3 + 2; // b
- if (use_transparency)
- org_px[3] = x*3 + 3; // a
- else
- org_px[3] = 0xFF; // a (opaque)
- }
- }
- }
- // Creates a palette-based image.
- 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; // palette index
- }
- }
- if (trans_chunk) {
- trans_chunk->resize(palette->size());
- for (std::size_t i = 0; i < trans_chunk->size(); ++i) {
- (*trans_chunk)[i] = i % 256;
- }
- }
- }
- // Creates a grayscale image without an alpha channel.
- 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; // gray value
- }
- }
- }
- // Creates a grayscale image with an alpha channel.
- 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; // gray value
- px[1] = x % 256; // alpha
- }
- }
- }
- // 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) {
- 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;
- }
- } // 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) {
- 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;
- }
- // Returns true if the RGB components are "close."
- 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;
- }
- // Returns true if the BGRA 32-bit SkColor specified by |a| is equivalent to the
- // 8-bit Gray color specified by |b|.
- 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->allocN32Pixels(w, h);
- 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->allocPixels(SkImageInfo::MakeA8(w, h));
- uint8_t* src_data = bmp->getAddr8(0, 0);
- for (int i = 0; i < w * h; i++)
- src_data[i] = i % 255;
- }
- TEST(PNGCodec, EncodeDecodeRGBA) {
- const int w = 20, h = 20;
- // create an image with known values, a 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_RGBA,
- 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_RGBA, &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, 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, DecodePalette) {
- const int w = 20, h = 20;
- // create an image with known values
- 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);
- // encode
- std::vector<unsigned char> encoded;
- ASSERT_TRUE(EncodeImage(original,
- w, h,
- COLOR_TYPE_PALETTE,
- &encoded,
- PNG_INTERLACE_NONE,
- &original_palette,
- &original_trans_chunk));
- // decode
- 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);
- // Images must be equal
- 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, DecodeInterlacedPalette) {
- const int w = 20, h = 20;
- // create an image with known values
- 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);
- // encode
- std::vector<unsigned char> encoded;
- ASSERT_TRUE(EncodeImage(original,
- w, h,
- COLOR_TYPE_PALETTE,
- &encoded,
- PNG_INTERLACE_ADAM7,
- &original_palette,
- &original_trans_chunk));
- // decode
- 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);
- // Images must be equal
- 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;
- // create an image with known values
- std::vector<unsigned char> original;
- MakeGrayscaleImage(w, h, &original);
- // encode
- std::vector<unsigned char> encoded;
- ASSERT_TRUE(EncodeImage(original, w, h, COLOR_TYPE_GRAY, &encoded));
- // decode
- 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);
- // Images must be equal
- 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, DecodeGrayscaleWithAlpha) {
- const int w = 20, h = 20;
- // create an image with known values
- std::vector<unsigned char> original;
- MakeGrayscaleAlphaImage(w, h, &original);
- // encode
- std::vector<unsigned char> encoded;
- ASSERT_TRUE(EncodeImage(original,
- w, h,
- COLOR_TYPE_GRAY_ALPHA,
- &encoded));
- // decode
- 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);
- // Images must be equal
- 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, DecodeInterlacedGrayscale) {
- const int w = 20, h = 20;
- // create an image with known values
- std::vector<unsigned char> original;
- MakeGrayscaleImage(w, h, &original);
- // encode
- std::vector<unsigned char> encoded;
- ASSERT_TRUE(EncodeImage(original,
- w, h,
- COLOR_TYPE_GRAY,
- &encoded,
- PNG_INTERLACE_ADAM7));
- // decode
- 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);
- // Images must be equal
- 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;
- // create an image with known values
- std::vector<unsigned char> original;
- MakeGrayscaleAlphaImage(w, h, &original);
- // encode
- std::vector<unsigned char> encoded;
- ASSERT_TRUE(EncodeImage(original,
- w, h,
- COLOR_TYPE_GRAY_ALPHA,
- &encoded,
- PNG_INTERLACE_ADAM7));
- // decode
- 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);
- // Images must be equal
- 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, 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, 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];
- 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;
- // 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];
- EXPECT_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];
- EXPECT_EQ(original_pixel_sk, decoded_pixel);
- }
- }
- }
- // Test that corrupted data decompression causes failures.
- TEST(PNGCodec, DecodeCorrupted) {
- int w = 20, h = 20;
- // Make some random data (an uncompressed image).
- std::vector<unsigned char> original;
- MakeRGBAImage(w, h, false, &original);
- // It should fail when given non-PNG compressed data.
- std::vector<unsigned char> output;
- int outw, outh;
- EXPECT_FALSE(PNGCodec::Decode(&original[0], original.size(),
- PNGCodec::FORMAT_RGBA, &output, &outw, &outh));
- // Make some compressed data.
- std::vector<unsigned char> compressed;
- ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA, Size(w, h),
- w * 4, false, std::vector<PNGCodec::Comment>(),
- &compressed));
- // Try decompressing a truncated version.
- EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size() / 2,
- PNGCodec::FORMAT_RGBA, &output, &outw, &outh));
- // Corrupt it and try decompressing that.
- for (int i = 10; i < 30; i++)
- compressed[i] = i;
- EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size(),
- PNGCodec::FORMAT_RGBA, &output, &outw, &outh));
- }
- TEST(PNGCodec, EncodeBGRASkBitmapStridePadded) {
- const int kWidth = 20;
- const int kHeight = 20;
- const int kPaddedWidth = 32;
- const int kBytesPerPixel = 4;
- const int kRowBytes = kPaddedWidth * kBytesPerPixel;
- SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
- SkBitmap original_bitmap;
- original_bitmap.setInfo(info, kRowBytes);
- original_bitmap.allocPixels();
- // Write data over the source bitmap.
- // We write on the pad area here too.
- // The encoder should ignore the pad area.
- uint32_t* src_data = original_bitmap.getAddr32(0, 0);
- const int count =
- original_bitmap.computeByteSize() / original_bitmap.bytesPerPixel();
- for (int i = 0; i < count; i++) {
- src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
- }
- // Encode the bitmap.
- std::vector<unsigned char> encoded;
- PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
- // Decode the encoded string.
- SkBitmap decoded_bitmap;
- EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
- &decoded_bitmap));
- // Compare the original bitmap and the output bitmap. We use ColorsClose
- // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication
- // (in Encode) and repremultiplication (in Decode) can be lossy.
- 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);
- // Encode the bitmap.
- std::vector<unsigned char> encoded;
- PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
- // Decode the encoded string.
- SkBitmap decoded_bitmap;
- EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
- &decoded_bitmap));
- // Compare the original bitmap and the output bitmap. We use ColorsClose
- // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication
- // (in Encode) and repremultiplication (in Decode) can be lossy.
- 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);
- // Encode the bitmap.
- std::vector<unsigned char> encoded;
- EXPECT_TRUE(PNGCodec::EncodeA8SkBitmap(original_bitmap, &encoded));
- // Decode the encoded string.
- 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);
- // Encode the bitmap.
- std::vector<unsigned char> encoded;
- PNGCodec::EncodeBGRASkBitmap(original_bitmap, true, &encoded);
- // Decode the encoded string.
- SkBitmap decoded_bitmap;
- EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
- &decoded_bitmap));
- // Compare the original bitmap and the output bitmap. We need to
- // unpremultiply original_pixel, as the decoded bitmap doesn't have an alpha
- // channel.
- 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;
- MakeRGBAImage(w, h, true, &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_RGBA, Size(w, h),
- w * 4, false, comments, &encoded));
- // Each chunk is of the form length (4 bytes), chunk type (tEXt), data,
- // checksum (4 bytes). Make sure we find all of them in the encoded
- // results.
- 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 + std::size(kExpected1)),
- encoded.end());
- EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected2,
- kExpected2 + std::size(kExpected2)),
- encoded.end());
- EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected3,
- kExpected3 + std::size(kExpected3)),
- encoded.end());
- }
- TEST(PNGCodec, EncodeDecodeWithVaryingCompressionLevels) {
- const int w = 20, h = 20;
- // create an image with known values, a must be opaque because it will be
- // lost during encoding
- SkBitmap original_bitmap;
- MakeTestBGRASkBitmap(w, h, &original_bitmap);
- // encode
- 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));
- // Make sure the different compression settings actually do something; the
- // sizes should be different.
- EXPECT_NE(encoded_normal.size(), encoded_fast.size());
- // decode, they should be identical to the original.
- 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));
- }
- } // namespace gfx
|