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

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

Issue 2944633002: Use SkPngEncoder in gfx jpeg_codec (Closed)
Patch Set: All Changed Created 3 years, 6 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.cc
diff --git a/ui/gfx/codec/png_codec.cc b/ui/gfx/codec/png_codec.cc
index fe4020596fc9c3ba32609719060a727554b07e81..956e89d9ba1b24a90e6cca073034df6f4e43c897 100644
--- a/ui/gfx/codec/png_codec.cc
+++ b/ui/gfx/codec/png_codec.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/codec/vector_wstream.h"
Elliot Glaysher 2017/06/23 22:55:18 nit: vector_wstream should be in the block of incl
liyuqian 2017/07/05 15:59:39 Done.
#include <stdint.h>
@@ -13,60 +14,13 @@
#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/skia/include/encode/SkPngEncoder.h"
#include "third_party/zlib/zlib.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/skia_util.h"
namespace gfx {
-namespace {
-
-// Converts BGRA->RGBA and RGBA->BGRA.
-void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
- unsigned char* output, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++) {
- const unsigned char* pixel_in = &input[x * 4];
- unsigned char* pixel_out = &output[x * 4];
- pixel_out[0] = pixel_in[2];
- pixel_out[1] = pixel_in[1];
- pixel_out[2] = pixel_in[0];
- pixel_out[3] = pixel_in[3];
- }
-}
-
-void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
- unsigned char* rgb, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++)
- memcpy(&rgb[x * 3], &rgba[x * 4], 3);
-}
-
-void ConvertSkiaToRGB(const unsigned char* skia, int pixel_width,
- unsigned char* rgb, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++) {
- const uint32_t pixel_in = *reinterpret_cast<const uint32_t*>(&skia[x * 4]);
- unsigned char* pixel_out = &rgb[x * 3];
-
- int alpha = SkGetPackedA32(pixel_in);
- if (alpha != 0 && alpha != 255) {
- SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel_in);
- pixel_out[0] = SkColorGetR(unmultiplied);
- pixel_out[1] = SkColorGetG(unmultiplied);
- pixel_out[2] = SkColorGetB(unmultiplied);
- } else {
- pixel_out[0] = SkGetPackedR32(pixel_in);
- pixel_out[1] = SkGetPackedG32(pixel_in);
- pixel_out[2] = SkGetPackedB32(pixel_in);
- }
- }
-}
-
-void ConvertSkiaToRGBA(const unsigned char* skia, int pixel_width,
- unsigned char* rgba, bool* is_opaque) {
- gfx::ConvertSkiaToRGBA(skia, pixel_width, rgba);
-}
-
-} // namespace
-
// Decoder --------------------------------------------------------------------
//
// This code is based on WebKit libpng interface (PNGImageDecoder), which is
@@ -369,15 +323,6 @@ void LogLibPNGDecodeWarning(png_structp png_ptr, png_const_charp warning_msg) {
DLOG(ERROR) << "libpng decode warning: " << warning_msg;
}
-void LogLibPNGEncodeError(png_structp png_ptr, png_const_charp error_msg) {
- DLOG(ERROR) << "libpng encode error: " << error_msg;
- longjmp(png_jmpbuf(png_ptr), 1);
-}
-
-void LogLibPNGEncodeWarning(png_structp png_ptr, png_const_charp warning_msg) {
- DLOG(ERROR) << "libpng encode warning: " << warning_msg;
-}
-
} // namespace
// static
@@ -454,287 +399,73 @@ bool PNGCodec::Decode(const unsigned char* input, size_t input_size,
}
// Encoder --------------------------------------------------------------------
-//
-// This section of the code is based on nsPNGEncoder.cpp in Mozilla
-// (Copyright 2005 Google Inc.)
namespace {
-// Passed around as the io_ptr in the png structs so our callbacks know where
-// to write data.
-struct PngEncoderState {
- explicit PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
- std::vector<unsigned char>* out;
-};
-
-// Called by libpng to flush its internal buffer to ours.
-void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
- PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
- DCHECK(state->out);
-
- size_t old_size = state->out->size();
- state->out->resize(old_size + size);
- memcpy(&(*state->out)[old_size], data, size);
-}
-
-void FakeFlushCallback(png_structp png) {
- // We don't need to perform any flushing since we aren't doing real IO, but
- // we're required to provide this function by libpng.
-}
-
-void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
- unsigned char* rgb, bool* is_opaque) {
- for (int x = 0; x < pixel_width; x++) {
- const unsigned char* pixel_in = &bgra[x * 4];
- unsigned char* pixel_out = &rgb[x * 3];
- pixel_out[0] = pixel_in[2];
- pixel_out[1] = pixel_in[1];
- pixel_out[2] = pixel_in[0];
+static void AddComments(SkPngEncoder::Options& options,
+ const std::vector<PNGCodec::Comment>& comments) {
+ std::vector<const char*> commentPointers;
+ std::vector<size_t> commentSizes;
+ for (const auto& comment : comments) {
+ commentPointers.push_back(comment.key.c_str());
+ commentPointers.push_back(comment.text.c_str());
+ commentSizes.push_back(comment.key.length() + 1);
+ commentSizes.push_back(comment.text.length() + 1);
}
+ options.fComments =
+ SkDataTable::MakeCopyArrays((void const* const*)commentPointers.data(),
+ commentSizes.data(), commentPointers.size());
}
-#ifdef PNG_TEXT_SUPPORTED
-class CommentWriter {
- public:
- explicit CommentWriter(const std::vector<PNGCodec::Comment>& comments)
- : comments_(comments),
- png_text_(new png_text[comments.size()]) {
- for (size_t i = 0; i < comments.size(); ++i)
- AddComment(i, comments[i]);
- }
-
- ~CommentWriter() {
- for (size_t i = 0; i < comments_.size(); ++i) {
- free(png_text_[i].key);
- free(png_text_[i].text);
- }
- delete [] png_text_;
- }
-
- bool HasComments() {
- return !comments_.empty();
- }
-
- png_text* get_png_text() {
- return png_text_;
- }
-
- int size() {
- return static_cast<int>(comments_.size());
- }
-
- private:
- void AddComment(size_t pos, const PNGCodec::Comment& comment) {
- png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
- // A PNG comment's key can only be 79 characters long.
- DCHECK(comment.key.length() < 79);
- png_text_[pos].key = base::strdup(comment.key.substr(0, 78).c_str());
- png_text_[pos].text = base::strdup(comment.text.c_str());
- png_text_[pos].text_length = comment.text.length();
-#ifdef PNG_iTXt_SUPPORTED
- png_text_[pos].itxt_length = 0;
- png_text_[pos].lang = 0;
- png_text_[pos].lang_key = 0;
-#endif
- }
-
- DISALLOW_COPY_AND_ASSIGN(CommentWriter);
-
- const std::vector<PNGCodec::Comment> comments_;
- png_text* png_text_;
-};
-#endif // PNG_TEXT_SUPPORTED
-
-// The type of functions usable for converting between pixel formats.
-typedef void (*FormatConverter)(const unsigned char* in, int w,
- unsigned char* out, bool* is_opaque);
-
-// libpng uses a wacky setjmp-based API, which makes the compiler nervous.
-// We constrain all of the calls we make to libpng where the setjmp() is in
-// place to this function.
-// Returns true on success.
-bool DoLibpngWrite(png_struct* png_ptr, png_info* info_ptr,
- PngEncoderState* state,
- int width, int height, int row_byte_width,
- const unsigned char* input, int compression_level,
- int png_output_color_type, int output_color_components,
- FormatConverter converter,
- const std::vector<PNGCodec::Comment>& comments) {
-#ifdef PNG_TEXT_SUPPORTED
- CommentWriter comment_writer(comments);
-#endif
- unsigned char* row_buffer = NULL;
-
- // Make sure to not declare any locals here -- locals in the presence
- // of setjmp() in C++ code makes gcc complain.
-
- if (setjmp(png_jmpbuf(png_ptr))) {
- delete[] row_buffer;
- return false;
- }
-
- png_set_compression_level(png_ptr, compression_level);
-
- // Set our callback for libpng to give us the data.
- png_set_write_fn(png_ptr, state, EncoderWriteCallback, FakeFlushCallback);
- png_set_error_fn(png_ptr, NULL, LogLibPNGEncodeError, LogLibPNGEncodeWarning);
-
- png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_output_color_type,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT);
+} // namespace
-#ifdef PNG_TEXT_SUPPORTED
- if (comment_writer.HasComments()) {
- png_set_text(png_ptr, info_ptr, comment_writer.get_png_text(),
- comment_writer.size());
- }
-#endif
+static bool EncodeSkPixmap(const SkPixmap& src,
+ const std::vector<PNGCodec::Comment>& comments,
+ std::vector<unsigned char>* output,
+ int zLibLevel = Z_DEFAULT_COMPRESSION) {
dcheng 2017/06/23 21:45:55 Also, another optional nit is that I think it was
dcheng 2017/06/23 21:45:55 Nit: naming (same throughout for unpremulInfo, unp
liyuqian 2017/07/05 15:59:39 Done.
liyuqian 2017/07/05 15:59:39 Done.
+ output->clear();
+ VectorWStream dst(output);
- png_write_info(png_ptr, info_ptr);
+ SkPngEncoder::Options options;
+ AddComments(options, comments);
+ options.fZLibLevel = zLibLevel == Z_DEFAULT_COMPRESSION ? 6 : zLibLevel;
dcheng 2017/06/23 21:45:55 Why do we override Z_DEFAULT_COMPRESSION here? We
msarett1 2017/06/23 22:50:31 What if we have a gfx::PngCodec::kDefaultZLibCompr
liyuqian 2017/07/05 15:59:39 Acknowledged.
liyuqian 2017/07/05 15:59:39 Done.
+ return SkPngEncoder::Encode(&dst, src, options);
+}
- if (!converter) {
- // No conversion needed, give the data directly to libpng.
- for (int y = 0; y < height; y ++) {
- png_write_row(png_ptr,
- const_cast<unsigned char*>(&input[y * row_byte_width]));
- }
+static bool EncodeSkPixmap(const SkPixmap& src,
+ bool discard_transparency,
+ const std::vector<PNGCodec::Comment>& comments,
+ std::vector<unsigned char>* output,
+ int zLibLevel = Z_DEFAULT_COMPRESSION) {
+ if (!discard_transparency) {
msarett1 2017/06/23 22:50:31 nit: I find this more readable as if (discard_tran
liyuqian 2017/07/05 15:59:39 Done.
+ return EncodeSkPixmap(src, comments, output, zLibLevel);
} else {
msarett1 2017/06/23 22:50:31 nit: No need for an else since we return in the if
liyuqian 2017/07/05 15:59:39 Done.
- // Needs conversion using a separate buffer.
- row_buffer = new unsigned char[width * output_color_components];
- for (int y = 0; y < height; y ++) {
- converter(&input[y * row_byte_width], width, row_buffer, NULL);
- png_write_row(png_ptr, row_buffer);
+ auto w = src.info().width();
+ auto h = src.info().height();
+ auto ct = src.info().colorType();
+ auto cs = src.info().refColorSpace();
+ SkImageInfo unpremulInfo =
msarett1 2017/06/23 22:50:31 unpremulInfo = src.info().makeAlphaType(kUnpremul_
liyuqian 2017/07/05 15:59:39 Done.
+ SkImageInfo::Make(w, h, ct, kUnpremul_SkAlphaType, cs);
+
+ SkBitmap unpremulCopy;
+ if (!unpremulCopy.tryAllocPixels(unpremulInfo)) {
+ return false;
}
- delete[] row_buffer;
- }
-
- png_write_end(png_ptr, info_ptr);
- return true;
-}
-
-bool EncodeWithCompressionLevel(const unsigned char* input,
- PNGCodec::ColorFormat format,
- const Size& size,
- int row_byte_width,
- bool discard_transparency,
- const std::vector<PNGCodec::Comment>& comments,
- int compression_level,
- std::vector<unsigned char>* output) {
- // Run to convert an input row into the output row format, NULL means no
- // conversion is necessary.
- FormatConverter converter = NULL;
-
- int input_color_components, output_color_components;
- int png_output_color_type;
- switch (format) {
- case PNGCodec::FORMAT_RGBA:
- input_color_components = 4;
- if (discard_transparency) {
- output_color_components = 3;
- png_output_color_type = PNG_COLOR_TYPE_RGB;
- converter = ConvertRGBAtoRGB;
- } else {
- output_color_components = 4;
- png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- converter = NULL;
- }
- break;
-
- case PNGCodec::FORMAT_BGRA:
- input_color_components = 4;
- if (discard_transparency) {
- output_color_components = 3;
- png_output_color_type = PNG_COLOR_TYPE_RGB;
- converter = ConvertBGRAtoRGB;
- } else {
- output_color_components = 4;
- png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- converter = ConvertBetweenBGRAandRGBA;
- }
- break;
-
- case PNGCodec::FORMAT_SkBitmap:
- // Compare row_byte_width and size.width() to detect the format of
- // SkBitmap. kA8_Config (1bpp) and kARGB_8888_Config (4bpp) are the two
- // supported formats.
- if (row_byte_width < 4 * size.width()) {
- // Not 4bpp, so must be 1bpp.
- // Ignore discard_transparency - it doesn't make sense in this context,
- // since alpha is the only thing we have and it needs to be used for
- // color intensity.
- input_color_components = 1;
- output_color_components = 1;
- png_output_color_type = PNG_COLOR_TYPE_GRAY;
- // |converter| is left as null
- } else {
- input_color_components = 4;
- if (discard_transparency) {
- output_color_components = 3;
- png_output_color_type = PNG_COLOR_TYPE_RGB;
- converter = ConvertSkiaToRGB;
- } else {
- output_color_components = 4;
- png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- converter = ConvertSkiaToRGBA;
- }
- }
- break;
-
- default:
- NOTREACHED() << "Unknown pixel format";
+ SkPixmap unpremulPixmap;
+ if (!unpremulCopy.peekPixels(&unpremulPixmap)) {
msarett1 2017/06/23 22:50:31 Just assert that peekPixels() succeeds. It must b
liyuqian 2017/07/05 15:59:39 Done.
return false;
- }
-
- // Row stride should be at least as long as the length of the data.
- DCHECK(input_color_components * size.width() <= row_byte_width);
-
- PngWriteStructInfo si;
- si.png_ptr_ = png_create_write_struct(PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
- if (!si.png_ptr_)
- return false;
-
- si.info_ptr_ = png_create_info_struct(si.png_ptr_);
- if (!si.info_ptr_)
- return false;
-
- output->clear();
-
- PngEncoderState state(output);
- bool success = DoLibpngWrite(si.png_ptr_, si.info_ptr_, &state,
- size.width(), size.height(), row_byte_width,
- input, compression_level, png_output_color_type,
- output_color_components, converter, comments);
+ }
+ src.readPixels(unpremulPixmap);
msarett1 2017/06/23 22:50:32 Also assert that this succeeds.
liyuqian 2017/07/05 15:59:38 Done.
- return success;
-}
+ SkImageInfo opaqueInfo =
+ SkImageInfo::Make(w, h, ct, kOpaque_SkAlphaType, cs);
-bool InternalEncodeSkBitmap(const SkBitmap& input,
- bool discard_transparency,
- int compression_level,
- std::vector<unsigned char>* output) {
- if (input.empty() || input.isNull())
- return false;
- int bpp = input.bytesPerPixel();
- DCHECK(bpp == 1 || bpp == 4); // We support kA8_Config and kARGB_8888_Config.
-
- unsigned char* inputAddr = bpp == 1 ?
- reinterpret_cast<unsigned char*>(input.getAddr8(0, 0)) :
- reinterpret_cast<unsigned char*>(input.getAddr32(0, 0)); // bpp = 4
- return EncodeWithCompressionLevel(
- inputAddr,
- PNGCodec::FORMAT_SkBitmap,
- Size(input.width(), input.height()),
- static_cast<int>(input.rowBytes()),
- discard_transparency,
- std::vector<PNGCodec::Comment>(),
- compression_level,
- output);
+ SkPixmap opaquePixmap(opaqueInfo, unpremulPixmap.addr(),
msarett1 2017/06/23 22:50:31 Hmm maybe just call tryAllocPixels() using an opaq
liyuqian 2017/07/05 15:59:38 Done.
+ unpremulPixmap.rowBytes(), unpremulPixmap.ctable());
+ return EncodeSkPixmap(opaquePixmap, comments, output, zLibLevel);
+ }
}
-
-} // namespace
-
// static
bool PNGCodec::Encode(const unsigned char* input,
msarett1 2017/06/23 22:50:31 Can we remove this API and make callers use the Sk
liyuqian 2017/07/05 15:59:38 I prefer to do it in another CL because it will ch
ColorFormat format,
@@ -743,43 +474,49 @@ bool PNGCodec::Encode(const unsigned char* input,
bool discard_transparency,
const std::vector<Comment>& comments,
std::vector<unsigned char>* output) {
- return EncodeWithCompressionLevel(input,
- format,
- size,
- row_byte_width,
- discard_transparency,
- comments,
- Z_DEFAULT_COMPRESSION,
- output);
+ auto colorType =
+ format == FORMAT_RGBA ? kRGBA_8888_SkColorType : kBGRA_8888_SkColorType;
+ auto alphaType =
+ format == FORMAT_SkBitmap ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
+ SkImageInfo info =
+ SkImageInfo::Make(size.width(), size.height(), colorType, alphaType);
+ SkPixmap src(info, input, row_byte_width);
+ return EncodeSkPixmap(src, discard_transparency, comments, output);
+}
+
+static bool EncodeSkBitmap(const SkBitmap& input,
+ bool discard_transparency,
+ std::vector<unsigned char>* output,
+ int zLibLevel = Z_DEFAULT_COMPRESSION) {
+ SkPixmap src;
+ if (!input.peekPixels(&src)) {
+ return false;
+ }
+ return EncodeSkPixmap(src, discard_transparency, {}, output, zLibLevel);
}
// static
bool PNGCodec::EncodeBGRASkBitmap(const SkBitmap& input,
bool discard_transparency,
std::vector<unsigned char>* output) {
- return InternalEncodeSkBitmap(input,
- discard_transparency,
- Z_DEFAULT_COMPRESSION,
- output);
+ return EncodeSkBitmap(input, discard_transparency, output);
}
// static
bool PNGCodec::EncodeA8SkBitmap(const SkBitmap& input,
std::vector<unsigned char>* output) {
- return InternalEncodeSkBitmap(input,
- false,
- Z_DEFAULT_COMPRESSION,
- output);
+ SkImageInfo info =
+ SkImageInfo::Make(input.info().width(), input.info().height(),
+ kGray_8_SkColorType, kOpaque_SkAlphaType);
msarett1 2017/06/23 22:50:31 Don't forget the SkColorSpace. What about info =
liyuqian 2017/07/05 15:59:39 Done.
+ SkPixmap src(info, input.getAddr(0, 0), input.rowBytes());
+ return EncodeSkPixmap(src, {}, output);
}
// static
bool PNGCodec::FastEncodeBGRASkBitmap(const SkBitmap& input,
bool discard_transparency,
std::vector<unsigned char>* output) {
- return InternalEncodeSkBitmap(input,
- discard_transparency,
- Z_BEST_SPEED,
- output);
+ return EncodeSkBitmap(input, discard_transparency, output, Z_BEST_SPEED);
}
PNGCodec::Comment::Comment(const std::string& k, const std::string& t)

Powered by Google App Engine
This is Rietveld 408576698