blob: a1d9083a84b4c7fdba034a71d2311b7a07901eb0 [file] [log] [blame]
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001/*
Leon Scroggins III20baaee2020-03-09 11:48:03 -04002 * Copyright 2010 The Android Open Source Project
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00003 *
Leon Scroggins III20baaee2020-03-09 11:48:03 -04004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00006 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/images/SkImageEncoderPriv.h"
Hal Canary1fcc4042016-11-30 17:07:59 -05009
Mike Klein9d4b7882020-03-06 21:39:05 +000010#ifdef SK_HAS_WEBP_LIBRARY
Mike Kleinab737e42019-05-15 21:58:15 +000011
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkBitmap.h"
13#include "include/core/SkStream.h"
14#include "include/core/SkUnPreMultiply.h"
15#include "include/encode/SkWebpEncoder.h"
16#include "include/private/SkColorData.h"
17#include "include/private/SkTemplates.h"
18#include "src/images/SkImageEncoderFns.h"
19#include "src/utils/SkUTF.h"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000020
Matt Sarett46a45ba2017-04-06 20:34:38 +000021// A WebP encoder only, on top of (subset of) libwebp
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000022// For more information on WebP image format, and libwebp library, see:
23// http://code.google.com/speed/webp/
24// http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
25// http://review.webmproject.org/gitweb?p=libwebp.git
26
27#include <stdio.h>
28extern "C" {
29// If moving libwebp out of skia source tree, path for webp headers must be
30// updated accordingly. Here, we enforce using local copy in webp sub-directory.
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000031#include "webp/encode.h"
Matt Sarett46a45ba2017-04-06 20:34:38 +000032#include "webp/mux.h"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000033}
34
Brian Osmanb62f50c2018-07-12 14:44:27 -040035static transform_scanline_proc choose_proc(const SkImageInfo& info) {
Matt Sarett62bb2802017-01-23 12:28:02 -050036 switch (info.colorType()) {
37 case kRGBA_8888_SkColorType:
38 switch (info.alphaType()) {
39 case kOpaque_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050040 return transform_scanline_RGBX;
41 case kUnpremul_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050042 return transform_scanline_memcpy;
43 case kPremul_SkAlphaType:
Brian Osmanb62f50c2018-07-12 14:44:27 -040044 return transform_scanline_rgbA;
Matt Sarett62bb2802017-01-23 12:28:02 -050045 default:
46 return nullptr;
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +000047 }
Matt Sarett62bb2802017-01-23 12:28:02 -050048 case kBGRA_8888_SkColorType:
49 switch (info.alphaType()) {
50 case kOpaque_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050051 return transform_scanline_BGRX;
52 case kUnpremul_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050053 return transform_scanline_BGRA;
54 case kPremul_SkAlphaType:
Brian Osmanb62f50c2018-07-12 14:44:27 -040055 return transform_scanline_bgrA;
Matt Sarett62bb2802017-01-23 12:28:02 -050056 default:
57 return nullptr;
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +000058 }
reed0689d7b2014-06-14 05:30:20 -070059 case kRGB_565_SkColorType:
Matt Sarett55213562017-01-23 19:37:37 -050060 if (!info.isOpaque()) {
61 return nullptr;
62 }
63
Matt Sarett62bb2802017-01-23 12:28:02 -050064 return transform_scanline_565;
65 case kARGB_4444_SkColorType:
66 switch (info.alphaType()) {
67 case kOpaque_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050068 return transform_scanline_444;
69 case kPremul_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050070 return transform_scanline_4444;
71 default:
72 return nullptr;
73 }
Matt Sarett62bb2802017-01-23 12:28:02 -050074 case kGray_8_SkColorType:
Matt Sarett62bb2802017-01-23 12:28:02 -050075 return transform_scanline_gray;
Matt Sarett55213562017-01-23 19:37:37 -050076 case kRGBA_F16_SkColorType:
Matt Sarett55213562017-01-23 19:37:37 -050077 switch (info.alphaType()) {
78 case kOpaque_SkAlphaType:
79 case kUnpremul_SkAlphaType:
80 return transform_scanline_F16_to_8888;
81 case kPremul_SkAlphaType:
82 return transform_scanline_F16_premul_to_8888;
83 default:
84 return nullptr;
85 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000086 default:
halcanary96fcdcc2015-08-27 07:41:13 -070087 return nullptr;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000088 }
89}
90
91static int stream_writer(const uint8_t* data, size_t data_size,
92 const WebPPicture* const picture) {
93 SkWStream* const stream = (SkWStream*)picture->custom_ptr;
94 return stream->write(data, data_size) ? 1 : 0;
95}
96
Matt Sarettd5a16912017-05-16 17:06:52 -040097bool SkWebpEncoder::Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) {
Brian Osmane1adc3a2018-06-04 09:21:17 -040098 if (!SkPixmapIsValid(pixmap)) {
Matt Sarett04c37312017-05-05 14:02:13 -040099 return false;
Matt Sarett55213562017-01-23 19:37:37 -0500100 }
101
Brian Osmanb62f50c2018-07-12 14:44:27 -0400102 const transform_scanline_proc proc = choose_proc(pixmap.info());
Matt Sarett62bb2802017-01-23 12:28:02 -0500103 if (!proc) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000104 return false;
105 }
Matt Sarett55213562017-01-23 19:37:37 -0500106
107 int bpp;
108 if (kRGBA_F16_SkColorType == pixmap.colorType()) {
109 bpp = 4;
110 } else {
111 bpp = pixmap.isOpaque() ? 3 : 4;
112 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000113
Hal Canary1fcc4042016-11-30 17:07:59 -0500114 if (nullptr == pixmap.addr()) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000115 return false;
116 }
117
118 WebPConfig webp_config;
Matt Sarett04c37312017-05-05 14:02:13 -0400119 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000120 return false;
121 }
122
123 WebPPicture pic;
124 WebPPictureInit(&pic);
Matt Sarett46a45ba2017-04-06 20:34:38 +0000125 SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
Hal Canary1fcc4042016-11-30 17:07:59 -0500126 pic.width = pixmap.width();
127 pic.height = pixmap.height();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000128 pic.writer = stream_writer;
Matt Sarett46a45ba2017-04-06 20:34:38 +0000129
Matt Sarett2f687872017-05-19 19:12:54 -0400130 // Set compression, method, and pixel format.
131 // libwebp recommends using BGRA for lossless and YUV for lossy.
132 // The choices of |webp_config.method| currently just match Chrome's defaults. We
133 // could potentially expose this decision to the client.
134 if (Compression::kLossy == opts.fCompression) {
135 webp_config.lossless = 0;
136#ifndef SK_WEBP_ENCODER_USE_DEFAULT_METHOD
137 webp_config.method = 3;
138#endif
139 pic.use_argb = 0;
140 } else {
141 webp_config.lossless = 1;
142 webp_config.method = 0;
143 pic.use_argb = 1;
144 }
145
Matt Sarett46a45ba2017-04-06 20:34:38 +0000146 // If there is no need to embed an ICC profile, we write directly to the input stream.
147 // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk. libwebp
148 // forces us to have an encoded image before we can add a profile.
Matt Sarett1950e0a2017-06-12 16:17:30 -0400149 sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
Matt Sarett46a45ba2017-04-06 20:34:38 +0000150 SkDynamicMemoryWStream tmp;
151 pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000152
Hal Canary1fcc4042016-11-30 17:07:59 -0500153 const uint8_t* src = (uint8_t*)pixmap.addr();
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +0000154 const int rgbStride = pic.width * bpp;
Hal Canary1fcc4042016-11-30 17:07:59 -0500155 const size_t rowBytes = pixmap.rowBytes();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000156
157 // Import (for each scanline) the bit-map image (in appropriate color-space)
158 // to RGB color space.
Hal Canary1fcc4042016-11-30 17:07:59 -0500159 std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000160 for (int y = 0; y < pic.height; ++y) {
Mike Klein0c904fa2018-11-02 12:24:15 -0400161 proc((char*) &rgb[y * rgbStride],
162 (const char*) &src[y * rowBytes],
163 pic.width,
164 bpp);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000165 }
166
Matt Sarett46a45ba2017-04-06 20:34:38 +0000167 auto importProc = WebPPictureImportRGB;
168 if (3 != bpp) {
Matt Sarett55213562017-01-23 19:37:37 -0500169 if (pixmap.isOpaque()) {
Matt Sarett46a45ba2017-04-06 20:34:38 +0000170 importProc = WebPPictureImportRGBX;
Matt Sarett55213562017-01-23 19:37:37 -0500171 } else {
Matt Sarett46a45ba2017-04-06 20:34:38 +0000172 importProc = WebPPictureImportRGBA;
Matt Sarett55213562017-01-23 19:37:37 -0500173 }
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +0000174 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000175
Matt Sarett46a45ba2017-04-06 20:34:38 +0000176 if (!importProc(&pic, &rgb[0], rgbStride)) {
177 return false;
178 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000179
Matt Sarett46a45ba2017-04-06 20:34:38 +0000180 if (!WebPEncode(&webp_config, &pic)) {
181 return false;
182 }
183
184 if (icc) {
185 sk_sp<SkData> encodedData = tmp.detachAsData();
186 WebPData encoded = { encodedData->bytes(), encodedData->size() };
187 WebPData iccChunk = { icc->bytes(), icc->size() };
188
189 SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
190 if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
191 return false;
192 }
193
194 if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
195 return false;
196 }
197
198 WebPData assembled;
199 if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
200 return false;
201 }
202
203 stream->write(assembled.bytes, assembled.size);
204 WebPDataClear(&assembled);
205 }
206
207 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000208}
Mike Kleinab737e42019-05-15 21:58:15 +0000209
210#endif