blob: d516511713099783f6f1994b90b3be0910c4428b [file] [log] [blame]
commit-bot@chromium.orga936e372013-03-14 14:42:18 +00001/*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Hal Canarydb683012016-11-23 08:55:18 -070017#include "SkImageEncoderPriv.h"
Hal Canary1fcc4042016-11-30 17:07:59 -050018
19#ifdef SK_HAS_WEBP_LIBRARY
20
21#include "SkBitmap.h"
Cary Clarka4083c92017-09-15 11:59:23 -040022#include "SkColorData.h"
Matt Sarett5df93de2017-03-22 21:52:47 +000023#include "SkImageEncoderFns.h"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000024#include "SkStream.h"
25#include "SkTemplates.h"
Hal Canary1fcc4042016-11-30 17:07:59 -050026#include "SkUnPreMultiply.h"
Hal Canaryea60b952018-08-21 11:45:46 -040027#include "SkUTF.h"
Matt Sarett04c37312017-05-05 14:02:13 -040028#include "SkWebpEncoder.h"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000029
Matt Sarett46a45ba2017-04-06 20:34:38 +000030// A WebP encoder only, on top of (subset of) libwebp
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000031// For more information on WebP image format, and libwebp library, see:
32// http://code.google.com/speed/webp/
33// http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
34// http://review.webmproject.org/gitweb?p=libwebp.git
35
36#include <stdio.h>
37extern "C" {
38// If moving libwebp out of skia source tree, path for webp headers must be
39// updated accordingly. Here, we enforce using local copy in webp sub-directory.
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000040#include "webp/encode.h"
Matt Sarett46a45ba2017-04-06 20:34:38 +000041#include "webp/mux.h"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000042}
43
Brian Osmanb62f50c2018-07-12 14:44:27 -040044static transform_scanline_proc choose_proc(const SkImageInfo& info) {
Matt Sarett62bb2802017-01-23 12:28:02 -050045 switch (info.colorType()) {
46 case kRGBA_8888_SkColorType:
47 switch (info.alphaType()) {
48 case kOpaque_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050049 return transform_scanline_RGBX;
50 case kUnpremul_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050051 return transform_scanline_memcpy;
52 case kPremul_SkAlphaType:
Brian Osmanb62f50c2018-07-12 14:44:27 -040053 return transform_scanline_rgbA;
Matt Sarett62bb2802017-01-23 12:28:02 -050054 default:
55 return nullptr;
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +000056 }
Matt Sarett62bb2802017-01-23 12:28:02 -050057 case kBGRA_8888_SkColorType:
58 switch (info.alphaType()) {
59 case kOpaque_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050060 return transform_scanline_BGRX;
61 case kUnpremul_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050062 return transform_scanline_BGRA;
63 case kPremul_SkAlphaType:
Brian Osmanb62f50c2018-07-12 14:44:27 -040064 return transform_scanline_bgrA;
Matt Sarett62bb2802017-01-23 12:28:02 -050065 default:
66 return nullptr;
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +000067 }
reed0689d7b2014-06-14 05:30:20 -070068 case kRGB_565_SkColorType:
Matt Sarett55213562017-01-23 19:37:37 -050069 if (!info.isOpaque()) {
70 return nullptr;
71 }
72
Matt Sarett62bb2802017-01-23 12:28:02 -050073 return transform_scanline_565;
74 case kARGB_4444_SkColorType:
75 switch (info.alphaType()) {
76 case kOpaque_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050077 return transform_scanline_444;
78 case kPremul_SkAlphaType:
Matt Sarett62bb2802017-01-23 12:28:02 -050079 return transform_scanline_4444;
80 default:
81 return nullptr;
82 }
Matt Sarett62bb2802017-01-23 12:28:02 -050083 case kGray_8_SkColorType:
Matt Sarett62bb2802017-01-23 12:28:02 -050084 return transform_scanline_gray;
Matt Sarett55213562017-01-23 19:37:37 -050085 case kRGBA_F16_SkColorType:
Matt Sarett55213562017-01-23 19:37:37 -050086 switch (info.alphaType()) {
87 case kOpaque_SkAlphaType:
88 case kUnpremul_SkAlphaType:
89 return transform_scanline_F16_to_8888;
90 case kPremul_SkAlphaType:
91 return transform_scanline_F16_premul_to_8888;
92 default:
93 return nullptr;
94 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000095 default:
halcanary96fcdcc2015-08-27 07:41:13 -070096 return nullptr;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000097 }
98}
99
100static int stream_writer(const uint8_t* data, size_t data_size,
101 const WebPPicture* const picture) {
102 SkWStream* const stream = (SkWStream*)picture->custom_ptr;
103 return stream->write(data, data_size) ? 1 : 0;
104}
105
Matt Sarettd5a16912017-05-16 17:06:52 -0400106bool SkWebpEncoder::Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) {
Brian Osmane1adc3a2018-06-04 09:21:17 -0400107 if (!SkPixmapIsValid(pixmap)) {
Matt Sarett04c37312017-05-05 14:02:13 -0400108 return false;
Matt Sarett55213562017-01-23 19:37:37 -0500109 }
110
Brian Osmanb62f50c2018-07-12 14:44:27 -0400111 const transform_scanline_proc proc = choose_proc(pixmap.info());
Matt Sarett62bb2802017-01-23 12:28:02 -0500112 if (!proc) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000113 return false;
114 }
Matt Sarett55213562017-01-23 19:37:37 -0500115
116 int bpp;
117 if (kRGBA_F16_SkColorType == pixmap.colorType()) {
118 bpp = 4;
119 } else {
120 bpp = pixmap.isOpaque() ? 3 : 4;
121 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000122
Hal Canary1fcc4042016-11-30 17:07:59 -0500123 if (nullptr == pixmap.addr()) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000124 return false;
125 }
126
127 WebPConfig webp_config;
Matt Sarett04c37312017-05-05 14:02:13 -0400128 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000129 return false;
130 }
131
132 WebPPicture pic;
133 WebPPictureInit(&pic);
Matt Sarett46a45ba2017-04-06 20:34:38 +0000134 SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
Hal Canary1fcc4042016-11-30 17:07:59 -0500135 pic.width = pixmap.width();
136 pic.height = pixmap.height();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000137 pic.writer = stream_writer;
Matt Sarett46a45ba2017-04-06 20:34:38 +0000138
Matt Sarett2f687872017-05-19 19:12:54 -0400139 // Set compression, method, and pixel format.
140 // libwebp recommends using BGRA for lossless and YUV for lossy.
141 // The choices of |webp_config.method| currently just match Chrome's defaults. We
142 // could potentially expose this decision to the client.
143 if (Compression::kLossy == opts.fCompression) {
144 webp_config.lossless = 0;
145#ifndef SK_WEBP_ENCODER_USE_DEFAULT_METHOD
146 webp_config.method = 3;
147#endif
148 pic.use_argb = 0;
149 } else {
150 webp_config.lossless = 1;
151 webp_config.method = 0;
152 pic.use_argb = 1;
153 }
154
Matt Sarett46a45ba2017-04-06 20:34:38 +0000155 // If there is no need to embed an ICC profile, we write directly to the input stream.
156 // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk. libwebp
157 // forces us to have an encoded image before we can add a profile.
Matt Sarett1950e0a2017-06-12 16:17:30 -0400158 sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
Matt Sarett46a45ba2017-04-06 20:34:38 +0000159 SkDynamicMemoryWStream tmp;
160 pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000161
Hal Canary1fcc4042016-11-30 17:07:59 -0500162 const uint8_t* src = (uint8_t*)pixmap.addr();
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +0000163 const int rgbStride = pic.width * bpp;
Hal Canary1fcc4042016-11-30 17:07:59 -0500164 const size_t rowBytes = pixmap.rowBytes();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000165
166 // Import (for each scanline) the bit-map image (in appropriate color-space)
167 // to RGB color space.
Hal Canary1fcc4042016-11-30 17:07:59 -0500168 std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000169 for (int y = 0; y < pic.height; ++y) {
Mike Klein0c904fa2018-11-02 12:24:15 -0400170 proc((char*) &rgb[y * rgbStride],
171 (const char*) &src[y * rowBytes],
172 pic.width,
173 bpp);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000174 }
175
Matt Sarett46a45ba2017-04-06 20:34:38 +0000176 auto importProc = WebPPictureImportRGB;
177 if (3 != bpp) {
Matt Sarett55213562017-01-23 19:37:37 -0500178 if (pixmap.isOpaque()) {
Matt Sarett46a45ba2017-04-06 20:34:38 +0000179 importProc = WebPPictureImportRGBX;
Matt Sarett55213562017-01-23 19:37:37 -0500180 } else {
Matt Sarett46a45ba2017-04-06 20:34:38 +0000181 importProc = WebPPictureImportRGBA;
Matt Sarett55213562017-01-23 19:37:37 -0500182 }
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +0000183 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000184
Matt Sarett46a45ba2017-04-06 20:34:38 +0000185 if (!importProc(&pic, &rgb[0], rgbStride)) {
186 return false;
187 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000188
Matt Sarett46a45ba2017-04-06 20:34:38 +0000189 if (!WebPEncode(&webp_config, &pic)) {
190 return false;
191 }
192
193 if (icc) {
194 sk_sp<SkData> encodedData = tmp.detachAsData();
195 WebPData encoded = { encodedData->bytes(), encodedData->size() };
196 WebPData iccChunk = { icc->bytes(), icc->size() };
197
198 SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
199 if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
200 return false;
201 }
202
203 if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
204 return false;
205 }
206
207 WebPData assembled;
208 if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
209 return false;
210 }
211
212 stream->write(assembled.bytes, assembled.size);
213 WebPDataClear(&assembled);
214 }
215
216 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000217}
Matt Sarett55213562017-01-23 19:37:37 -0500218
Hal Canary1fcc4042016-11-30 17:07:59 -0500219#endif