blob: bda34dcb011dd045042ad24e71dc86a1a6e4d881 [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"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000027#include "SkUtils.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
Matt Sarett62bb2802017-01-23 12:28:02 -0500127 const SkPMColor* colors = nullptr;
Matt Sarett62bb2802017-01-23 12:28:02 -0500128
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000129 WebPConfig webp_config;
Matt Sarett04c37312017-05-05 14:02:13 -0400130 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000131 return false;
132 }
133
134 WebPPicture pic;
135 WebPPictureInit(&pic);
Matt Sarett46a45ba2017-04-06 20:34:38 +0000136 SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
Hal Canary1fcc4042016-11-30 17:07:59 -0500137 pic.width = pixmap.width();
138 pic.height = pixmap.height();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000139 pic.writer = stream_writer;
Matt Sarett46a45ba2017-04-06 20:34:38 +0000140
Matt Sarett2f687872017-05-19 19:12:54 -0400141 // Set compression, method, and pixel format.
142 // libwebp recommends using BGRA for lossless and YUV for lossy.
143 // The choices of |webp_config.method| currently just match Chrome's defaults. We
144 // could potentially expose this decision to the client.
145 if (Compression::kLossy == opts.fCompression) {
146 webp_config.lossless = 0;
147#ifndef SK_WEBP_ENCODER_USE_DEFAULT_METHOD
148 webp_config.method = 3;
149#endif
150 pic.use_argb = 0;
151 } else {
152 webp_config.lossless = 1;
153 webp_config.method = 0;
154 pic.use_argb = 1;
155 }
156
Matt Sarett46a45ba2017-04-06 20:34:38 +0000157 // If there is no need to embed an ICC profile, we write directly to the input stream.
158 // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk. libwebp
159 // forces us to have an encoded image before we can add a profile.
Matt Sarett1950e0a2017-06-12 16:17:30 -0400160 sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
Matt Sarett46a45ba2017-04-06 20:34:38 +0000161 SkDynamicMemoryWStream tmp;
162 pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000163
Hal Canary1fcc4042016-11-30 17:07:59 -0500164 const uint8_t* src = (uint8_t*)pixmap.addr();
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +0000165 const int rgbStride = pic.width * bpp;
Hal Canary1fcc4042016-11-30 17:07:59 -0500166 const size_t rowBytes = pixmap.rowBytes();
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000167
168 // Import (for each scanline) the bit-map image (in appropriate color-space)
169 // to RGB color space.
Hal Canary1fcc4042016-11-30 17:07:59 -0500170 std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000171 for (int y = 0; y < pic.height; ++y) {
Matt Sarett62bb2802017-01-23 12:28:02 -0500172 proc((char*) &rgb[y * rgbStride], (const char*) &src[y * rowBytes], pic.width, bpp, colors);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000173 }
174
Matt Sarett46a45ba2017-04-06 20:34:38 +0000175 auto importProc = WebPPictureImportRGB;
176 if (3 != bpp) {
Matt Sarett55213562017-01-23 19:37:37 -0500177 if (pixmap.isOpaque()) {
Matt Sarett46a45ba2017-04-06 20:34:38 +0000178 importProc = WebPPictureImportRGBX;
Matt Sarett55213562017-01-23 19:37:37 -0500179 } else {
Matt Sarett46a45ba2017-04-06 20:34:38 +0000180 importProc = WebPPictureImportRGBA;
Matt Sarett55213562017-01-23 19:37:37 -0500181 }
commit-bot@chromium.org5007aab2014-02-26 21:35:17 +0000182 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000183
Matt Sarett46a45ba2017-04-06 20:34:38 +0000184 if (!importProc(&pic, &rgb[0], rgbStride)) {
185 return false;
186 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000187
Matt Sarett46a45ba2017-04-06 20:34:38 +0000188 if (!WebPEncode(&webp_config, &pic)) {
189 return false;
190 }
191
192 if (icc) {
193 sk_sp<SkData> encodedData = tmp.detachAsData();
194 WebPData encoded = { encodedData->bytes(), encodedData->size() };
195 WebPData iccChunk = { icc->bytes(), icc->size() };
196
197 SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
198 if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
199 return false;
200 }
201
202 if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
203 return false;
204 }
205
206 WebPData assembled;
207 if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
208 return false;
209 }
210
211 stream->write(assembled.bytes, assembled.size);
212 WebPDataClear(&assembled);
213 }
214
215 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000216}
Matt Sarett55213562017-01-23 19:37:37 -0500217
Hal Canary1fcc4042016-11-30 17:07:59 -0500218#endif