summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/encode/SkWebpEncoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/encode/SkWebpEncoder.cpp')
-rw-r--r--gfx/skia/skia/src/encode/SkWebpEncoder.cpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/encode/SkWebpEncoder.cpp b/gfx/skia/skia/src/encode/SkWebpEncoder.cpp
new file mode 100644
index 0000000000..2189b807a4
--- /dev/null
+++ b/gfx/skia/skia/src/encode/SkWebpEncoder.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypes.h"
+
+#ifdef SK_ENCODE_WEBP
+
+#include "include/core/SkAlphaType.h"
+#include "include/core/SkBitmap.h"
+#include "include/core/SkColorType.h"
+#include "include/core/SkData.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkPixmap.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSpan.h"
+#include "include/core/SkStream.h"
+#include "include/encode/SkEncoder.h"
+#include "include/encode/SkWebpEncoder.h"
+#include "include/private/base/SkTemplates.h"
+#include "src/core/SkImageInfoPriv.h"
+#include "src/encode/SkImageEncoderFns.h"
+#include "src/encode/SkImageEncoderPriv.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+
+// A WebP encoder only, on top of (subset of) libwebp
+// For more information on WebP image format, and libwebp library, see:
+// http://code.google.com/speed/webp/
+// http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
+// http://review.webmproject.org/gitweb?p=libwebp.git
+
+extern "C" {
+// If moving libwebp out of skia source tree, path for webp headers must be
+// updated accordingly. Here, we enforce using local copy in webp sub-directory.
+#include "webp/encode.h"
+#include "webp/mux.h"
+#include "webp/mux_types.h"
+}
+
+static int stream_writer(const uint8_t* data, size_t data_size,
+ const WebPPicture* const picture) {
+ SkWStream* const stream = (SkWStream*)picture->custom_ptr;
+ return stream->write(data, data_size) ? 1 : 0;
+}
+
+using WebPPictureImportProc = int (*) (WebPPicture* picture, const uint8_t* pixels, int stride);
+
+static bool preprocess_webp_picture(WebPPicture* pic,
+ WebPConfig* webp_config,
+ const SkPixmap& pixmap,
+ const SkWebpEncoder::Options& opts) {
+ if (!SkPixmapIsValid(pixmap)) {
+ return false;
+ }
+
+ if (SkColorTypeIsAlphaOnly(pixmap.colorType())) {
+ // Maintain the existing behavior of not supporting encoding alpha-only images.
+ // TODO: Support encoding alpha only to an image with alpha but no color?
+ return false;
+ }
+
+ if (nullptr == pixmap.addr()) {
+ return false;
+ }
+
+ pic->width = pixmap.width();
+ pic->height = pixmap.height();
+
+ // Set compression, method, and pixel format.
+ // libwebp recommends using BGRA for lossless and YUV for lossy.
+ // The choices of |webp_config.method| currently just match Chrome's defaults. We
+ // could potentially expose this decision to the client.
+ if (SkWebpEncoder::Compression::kLossy == opts.fCompression) {
+ webp_config->lossless = 0;
+#ifndef SK_WEBP_ENCODER_USE_DEFAULT_METHOD
+ webp_config->method = 3;
+#endif
+ pic->use_argb = 0;
+ } else {
+ webp_config->lossless = 1;
+ webp_config->method = 0;
+ pic->use_argb = 1;
+ }
+
+ {
+ const SkColorType ct = pixmap.colorType();
+ const bool premul = pixmap.alphaType() == kPremul_SkAlphaType;
+
+ SkBitmap tmpBm;
+ WebPPictureImportProc importProc = nullptr;
+ const SkPixmap* src = &pixmap;
+ if (ct == kRGB_888x_SkColorType) {
+ importProc = WebPPictureImportRGBX;
+ } else if (!premul && ct == kRGBA_8888_SkColorType) {
+ importProc = WebPPictureImportRGBA;
+ }
+#ifdef WebPPictureImportBGRA
+ else if (!premul && ct == kBGRA_8888_SkColorType) {
+ importProc = WebPPictureImportBGRA;
+ }
+#endif
+ else {
+ importProc = WebPPictureImportRGBA;
+ auto info = pixmap.info()
+ .makeColorType(kRGBA_8888_SkColorType)
+ .makeAlphaType(kUnpremul_SkAlphaType);
+ if (!tmpBm.tryAllocPixels(info) ||
+ !pixmap.readPixels(tmpBm.info(), tmpBm.getPixels(), tmpBm.rowBytes())) {
+ return false;
+ }
+ src = &tmpBm.pixmap();
+ }
+
+ if (!importProc(pic, reinterpret_cast<const uint8_t*>(src->addr()), src->rowBytes())) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SkWebpEncoder::Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) {
+ if (!stream) {
+ return false;
+ }
+
+ WebPConfig webp_config;
+ if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
+ return false;
+ }
+
+ WebPPicture pic;
+ WebPPictureInit(&pic);
+ SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
+
+ if (!preprocess_webp_picture(&pic, &webp_config, pixmap, opts)) {
+ return false;
+ }
+
+ // If there is no need to embed an ICC profile, we write directly to the input stream.
+ // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk. libwebp
+ // forces us to have an encoded image before we can add a profile.
+ sk_sp<SkData> icc =
+ icc_from_color_space(pixmap.info(), opts.fICCProfile, opts.fICCProfileDescription);
+ SkDynamicMemoryWStream tmp;
+ pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
+ pic.writer = stream_writer;
+
+ if (!WebPEncode(&webp_config, &pic)) {
+ return false;
+ }
+
+ if (icc) {
+ sk_sp<SkData> encodedData = tmp.detachAsData();
+ WebPData encoded = { encodedData->bytes(), encodedData->size() };
+ WebPData iccChunk = { icc->bytes(), icc->size() };
+
+ SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
+ if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
+ return false;
+ }
+
+ if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
+ return false;
+ }
+
+ WebPData assembled;
+ if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
+ return false;
+ }
+
+ stream->write(assembled.bytes, assembled.size);
+ WebPDataClear(&assembled);
+ }
+
+ return true;
+}
+
+bool SkWebpEncoder::EncodeAnimated(SkWStream* stream,
+ SkSpan<const SkEncoder::Frame> frames,
+ const Options& opts) {
+ if (!stream || !frames.size()) {
+ return false;
+ }
+
+ const int canvasWidth = frames.front().pixmap.width();
+ const int canvasHeight = frames.front().pixmap.height();
+ int timestamp = 0;
+
+ std::unique_ptr<WebPAnimEncoder, void (*)(WebPAnimEncoder*)> enc(
+ WebPAnimEncoderNew(canvasWidth, canvasHeight, nullptr), WebPAnimEncoderDelete);
+ if (!enc) {
+ return false;
+ }
+
+ for (const auto& frame : frames) {
+ const auto& pixmap = frame.pixmap;
+
+ if (pixmap.width() != canvasWidth || pixmap.height() != canvasHeight) {
+ return false;
+ }
+
+ WebPConfig webp_config;
+ if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
+ return false;
+ }
+
+ WebPPicture pic;
+ WebPPictureInit(&pic);
+ SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
+
+ if (!preprocess_webp_picture(&pic, &webp_config, pixmap, opts)) {
+ return false;
+ }
+
+ if (!WebPEncode(&webp_config, &pic)) {
+ return false;
+ }
+
+ if (!WebPAnimEncoderAdd(enc.get(), &pic, timestamp, &webp_config)) {
+ return false;
+ }
+
+ timestamp += frame.duration;
+ }
+
+ // Add a last fake frame to signal the last duration.
+ if (!WebPAnimEncoderAdd(enc.get(), nullptr, timestamp, nullptr)) {
+ return false;
+ }
+
+ WebPData assembled;
+ SkAutoTCallVProc<WebPData, WebPDataClear> autoWebPData(&assembled);
+ if (!WebPAnimEncoderAssemble(enc.get(), &assembled)) {
+ return false;
+ }
+
+ enc.reset();
+
+ return stream->write(assembled.bytes, assembled.size);
+}
+
+#endif