1 /*
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
17 #include "SkImageDecoder.h"
18 #include "SkImageEncoder.h"
19 #include "SkColorPriv.h"
20 #include "SkScaledBitmapSampler.h"
21 #include "SkStream.h"
22 #include "SkTemplates.h"
23 #include "SkUtils.h"
24
25 // A WebP decoder only, on top of (subset of) libwebp
26 // For more information on WebP image format, and libwebp library, see:
27 // http://code.google.com/speed/webp/
28 // http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
29 // http://review.webmproject.org/gitweb?p=libwebp.git
30
31 #include <stdio.h>
32 extern "C" {
33 // If moving libwebp out of skia source tree, path for webp headers must be
34 // updated accordingly. Here, we enforce using local copy in webp sub-directory.
35 #include "webp/decode.h"
36 #include "webp/decode_vp8.h"
37 #include "webp/encode.h"
38 }
39
40 #ifdef ANDROID
41 #include <cutils/properties.h>
42
43 // Key to lookup the size of memory buffer set in system property
44 static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap";
45 #endif
46
47 // this enables timing code to report milliseconds for a decode
48 //#define TIME_DECODE
49
50 //////////////////////////////////////////////////////////////////////////
51 //////////////////////////////////////////////////////////////////////////
52
53 // Define VP8 I/O on top of Skia stream
54
55 //////////////////////////////////////////////////////////////////////////
56 //////////////////////////////////////////////////////////////////////////
57
58 static const size_t WEBP_VP8_HEADER_SIZE = 30;
59 static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
60
61 // Parse headers of RIFF container, and check for valid Webp (VP8) content.
webp_parse_header(SkStream * stream,int * width,int * height)62 static bool webp_parse_header(SkStream* stream, int* width, int* height) {
63 unsigned char buffer[WEBP_VP8_HEADER_SIZE];
64 const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE);
65 if (len != WEBP_VP8_HEADER_SIZE) {
66 return false; // can't read enough
67 }
68
69 if (WebPGetInfo(buffer, WEBP_VP8_HEADER_SIZE, width, height) == 0) {
70 return false; // Invalid WebP file.
71 }
72
73 // sanity check for image size that's about to be decoded.
74 {
75 Sk64 size;
76 size.setMul(*width, *height);
77 if (size.isNeg() || !size.is32()) {
78 return false;
79 }
80 // now check that if we are 4-bytes per pixel, we also don't overflow
81 if (size.get32() > (0x7FFFFFFF >> 2)) {
82 return false;
83 }
84 }
85 return true;
86 }
87
88 class SkWEBPImageDecoder: public SkImageDecoder {
89 public:
getFormat() const90 virtual Format getFormat() const {
91 return kWEBP_Format;
92 }
93
94 protected:
95 virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height);
96 virtual bool onDecodeRegion(SkBitmap* bitmap, SkIRect rect);
97 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
98
99 private:
100 bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
101 SkStream *inputStream;
102 int origWidth;
103 int origHeight;
104 };
105
106 //////////////////////////////////////////////////////////////////////////
107
108 #include "SkTime.h"
109
110 class AutoTimeMillis {
111 public:
AutoTimeMillis(const char label[])112 AutoTimeMillis(const char label[]) :
113 fLabel(label) {
114 if (!fLabel) {
115 fLabel = "";
116 }
117 fNow = SkTime::GetMSecs();
118 }
~AutoTimeMillis()119 ~AutoTimeMillis() {
120 SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
121 }
122 private:
123 const char* fLabel;
124 SkMSec fNow;
125 };
126
127 ///////////////////////////////////////////////////////////////////////////////
128
129 // This guy exists just to aid in debugging, as it allows debuggers to just
130 // set a break-point in one place to see all error exists.
return_false(const SkBitmap & bm,const char msg[])131 static bool return_false(const SkBitmap& bm, const char msg[]) {
132 #if 0
133 SkDebugf("libwebp error %s [%d %d]", msg, bm.width(), bm.height());
134 #endif
135 return false; // must always return false
136 }
137
webp_decode_mode(SkBitmap * decodedBitmap)138 static WEBP_CSP_MODE webp_decode_mode(SkBitmap* decodedBitmap) {
139 WEBP_CSP_MODE mode = MODE_LAST;
140 SkBitmap::Config config = decodedBitmap->config();
141 if (config == SkBitmap::kARGB_8888_Config) {
142 mode = MODE_RGBA;
143 } else if (config == SkBitmap::kARGB_4444_Config) {
144 mode = MODE_RGBA_4444;
145 } else if (config == SkBitmap::kRGB_565_Config) {
146 mode = MODE_RGB_565;
147 }
148 return mode;
149 }
150
151 // Incremental WebP image decoding. Reads input buffer of 64K size iteratively
152 // and decodes this block to appropriate color-space as per config object.
webp_idecode(SkStream * stream,WebPDecoderConfig & config)153 static bool webp_idecode(SkStream* stream, WebPDecoderConfig& config) {
154 WebPIDecoder* idec = WebPIDecode(NULL, NULL, &config);
155 if (idec == NULL) {
156 WebPFreeDecBuffer(&config.output);
157 return false;
158 }
159
160 stream->rewind();
161 const uint32_t contentSize = stream->getLength();
162 uint32_t read_buffer_size = contentSize;
163 if (read_buffer_size > WEBP_IDECODE_BUFFER_SZ) {
164 read_buffer_size = WEBP_IDECODE_BUFFER_SZ;
165 }
166 SkAutoMalloc srcStorage(read_buffer_size);
167 unsigned char* input = (uint8_t*)srcStorage.get();
168 if (input == NULL) {
169 WebPIDelete(idec);
170 WebPFreeDecBuffer(&config.output);
171 return false;
172 }
173
174 uint32_t bytes_remaining = contentSize;
175 while (bytes_remaining > 0) {
176 const uint32_t bytes_to_read =
177 (bytes_remaining > WEBP_IDECODE_BUFFER_SZ) ?
178 WEBP_IDECODE_BUFFER_SZ : bytes_remaining;
179
180 const size_t bytes_read = stream->read(input, bytes_to_read);
181 if (bytes_read == 0) {
182 break;
183 }
184
185 VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
186 if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
187 bytes_remaining -= bytes_read;
188 } else {
189 break;
190 }
191 }
192 srcStorage.free();
193 WebPIDelete(idec);
194 WebPFreeDecBuffer(&config.output);
195
196 if (bytes_remaining > 0) {
197 return false;
198 } else {
199 return true;
200 }
201 }
202
webp_get_config_resize_crop(WebPDecoderConfig & config,SkBitmap * decodedBitmap,SkIRect region)203 static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
204 SkBitmap* decodedBitmap,
205 SkIRect region) {
206 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
207 if (mode == MODE_LAST) {
208 return false;
209 }
210
211 if (WebPInitDecoderConfig(&config) == 0) {
212 return false;
213 }
214
215 config.output.colorspace = mode;
216 config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
217 config.output.u.RGBA.stride = decodedBitmap->rowBytes();
218 config.output.u.RGBA.size = decodedBitmap->getSize();
219 config.output.is_external_memory = 1;
220
221 config.options.use_cropping = 1;
222 config.options.crop_left = region.fLeft;
223 config.options.crop_top = region.fTop;
224 config.options.crop_width = region.width();
225 config.options.crop_height = region.height();
226
227 if (region.width() != decodedBitmap->width() ||
228 region.height() != decodedBitmap->height()) {
229 config.options.use_scaling = 1;
230 config.options.scaled_width = decodedBitmap->width();
231 config.options.scaled_height = decodedBitmap->height();
232 }
233
234 return true;
235 }
236
webp_get_config_resize(WebPDecoderConfig & config,SkBitmap * decodedBitmap,int origWidth,int origHeight)237 static bool webp_get_config_resize(WebPDecoderConfig& config,
238 SkBitmap* decodedBitmap, int origWidth,
239 int origHeight) {
240 WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap);
241 if (mode == MODE_LAST) {
242 return false;
243 }
244
245 if (WebPInitDecoderConfig(&config) == 0) {
246 return false;
247 }
248
249 config.output.colorspace = mode;
250 config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
251 config.output.u.RGBA.stride = decodedBitmap->rowBytes();
252 config.output.u.RGBA.size = decodedBitmap->getSize();
253 config.output.is_external_memory = 1;
254
255 if (origWidth != decodedBitmap->width() ||
256 origHeight != decodedBitmap->height()) {
257 config.options.use_scaling = 1;
258 config.options.scaled_width = decodedBitmap->width();
259 config.options.scaled_height = decodedBitmap->height();
260 }
261
262 return true;
263 }
264
setDecodeConfig(SkBitmap * decodedBitmap,int width,int height)265 bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
266 int width, int height) {
267 bool hasAlpha = false;
268 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
269
270 // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
271 if (hasAlpha) {
272 if (config != SkBitmap::kARGB_4444_Config) {
273 config = SkBitmap::kARGB_8888_Config;
274 }
275 } else {
276 if (config != SkBitmap::kRGB_565_Config &&
277 config != SkBitmap::kARGB_4444_Config) {
278 config = SkBitmap::kARGB_8888_Config;
279 }
280 }
281
282 if (!this->chooseFromOneChoice(config, width, height)) {
283 return false;
284 }
285
286 decodedBitmap->setConfig(config, width, height, 0);
287
288 // Current WEBP specification has no support for alpha layer.
289 decodedBitmap->setIsOpaque(true);
290
291 return true;
292 }
293
onBuildTileIndex(SkStream * stream,int * width,int * height)294 bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
295 int *width, int *height) {
296 int origWidth, origHeight;
297 if (!webp_parse_header(stream, &origWidth, &origHeight)) {
298 return false;
299 }
300
301 stream->rewind();
302 *width = origWidth;
303 *height = origHeight;
304
305 this->inputStream = stream;
306 this->origWidth = origWidth;
307 this->origHeight = origHeight;
308
309 return true;
310 }
311
onDecodeRegion(SkBitmap * decodedBitmap,SkIRect region)312 bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
313 SkIRect region) {
314 const int width = region.width();
315 const int height = region.height();
316
317 const int sampleSize = this->getSampleSize();
318 SkScaledBitmapSampler sampler(width, height, sampleSize);
319
320 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
321 sampler.scaledHeight())) {
322 return false;
323 }
324
325 if (!this->allocPixelRef(decodedBitmap, NULL)) {
326 return return_false(*decodedBitmap, "allocPixelRef");
327 }
328
329 SkAutoLockPixels alp(*decodedBitmap);
330
331 WebPDecoderConfig config;
332 if (!webp_get_config_resize_crop(config, decodedBitmap, region)) {
333 return false;
334 }
335
336 // Decode the WebP image data stream using WebP incremental decoding for
337 // the specified cropped image-region.
338 return webp_idecode(this->inputStream, config);
339 }
340
onDecode(SkStream * stream,SkBitmap * decodedBitmap,Mode mode)341 bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
342 Mode mode) {
343 #ifdef TIME_DECODE
344 AutoTimeMillis atm("WEBP Decode");
345 #endif
346
347 int origWidth, origHeight;
348 if (!webp_parse_header(stream, &origWidth, &origHeight)) {
349 return false;
350 }
351
352 const int sampleSize = this->getSampleSize();
353 SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
354
355 if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
356 sampler.scaledHeight())) {
357 return false;
358 }
359
360 // If only bounds are requested, done
361 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
362 return true;
363 }
364
365 if (!this->allocPixelRef(decodedBitmap, NULL)) {
366 return return_false(*decodedBitmap, "allocPixelRef");
367 }
368
369 SkAutoLockPixels alp(*decodedBitmap);
370
371 WebPDecoderConfig config;
372 if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight)) {
373 return false;
374 }
375
376 // Decode the WebP image data stream using WebP incremental decoding.
377 return webp_idecode(stream, config);
378 }
379
380 ///////////////////////////////////////////////////////////////////////////////
381
382 typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
383 const SkPMColor* SK_RESTRICT ctable);
384
ARGB_8888_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)385 static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
386 const SkPMColor*) {
387 const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
388 for (int i = 0; i < width; ++i) {
389 const uint32_t c = *src++;
390 rgb[0] = SkGetPackedR32(c);
391 rgb[1] = SkGetPackedG32(c);
392 rgb[2] = SkGetPackedB32(c);
393 rgb += 3;
394 }
395 }
396
RGB_565_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)397 static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
398 const SkPMColor*) {
399 const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
400 for (int i = 0; i < width; ++i) {
401 const uint16_t c = *src++;
402 rgb[0] = SkPacked16ToR32(c);
403 rgb[1] = SkPacked16ToG32(c);
404 rgb[2] = SkPacked16ToB32(c);
405 rgb += 3;
406 }
407 }
408
ARGB_4444_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor *)409 static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
410 const SkPMColor*) {
411 const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
412 for (int i = 0; i < width; ++i) {
413 const SkPMColor16 c = *src++;
414 rgb[0] = SkPacked4444ToR32(c);
415 rgb[1] = SkPacked4444ToG32(c);
416 rgb[2] = SkPacked4444ToB32(c);
417 rgb += 3;
418 }
419 }
420
Index8_To_RGB(const uint8_t * in,uint8_t * rgb,int width,const SkPMColor * SK_RESTRICT ctable)421 static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
422 const SkPMColor* SK_RESTRICT ctable) {
423 const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
424 for (int i = 0; i < width; ++i) {
425 const uint32_t c = ctable[*src++];
426 rgb[0] = SkGetPackedR32(c);
427 rgb[1] = SkGetPackedG32(c);
428 rgb[2] = SkGetPackedB32(c);
429 rgb += 3;
430 }
431 }
432
ChooseImporter(const SkBitmap::Config & config)433 static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) {
434 switch (config) {
435 case SkBitmap::kARGB_8888_Config:
436 return ARGB_8888_To_RGB;
437 case SkBitmap::kRGB_565_Config:
438 return RGB_565_To_RGB;
439 case SkBitmap::kARGB_4444_Config:
440 return ARGB_4444_To_RGB;
441 case SkBitmap::kIndex8_Config:
442 return Index8_To_RGB;
443 default:
444 return NULL;
445 }
446 }
447
StreamWriter(const uint8_t * data,size_t data_size,const WebPPicture * const picture)448 static int StreamWriter(const uint8_t* data, size_t data_size,
449 const WebPPicture* const picture) {
450 SkWStream* const stream = (SkWStream*)picture->custom_ptr;
451 return stream->write(data, data_size) ? 1 : 0;
452 }
453
454 class SkWEBPImageEncoder : public SkImageEncoder {
455 protected:
456 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
457 };
458
onEncode(SkWStream * stream,const SkBitmap & bm,int quality)459 bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
460 int quality) {
461 const SkBitmap::Config config = bm.getConfig();
462 const ScanlineImporter scanline_import = ChooseImporter(config);
463 if (NULL == scanline_import) {
464 return false;
465 }
466
467 SkAutoLockPixels alp(bm);
468 SkAutoLockColors ctLocker;
469 if (NULL == bm.getPixels()) {
470 return false;
471 }
472
473 WebPConfig webp_config;
474 if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) {
475 return false;
476 }
477
478 WebPPicture pic;
479 WebPPictureInit(&pic);
480 pic.width = bm.width();
481 pic.height = bm.height();
482 pic.writer = StreamWriter;
483 pic.custom_ptr = (void*)stream;
484
485 const SkPMColor* colors = ctLocker.lockColors(bm);
486 const uint8_t* src = (uint8_t*)bm.getPixels();
487 const int rgb_stride = pic.width * 3;
488
489 // Import (for each scanline) the bit-map image (in appropriate color-space)
490 // to RGB color space.
491 uint8_t* rgb = new uint8_t[rgb_stride * pic.height];
492 for (int y = 0; y < pic.height; ++y) {
493 scanline_import(src + y * bm.rowBytes(), rgb + y * rgb_stride,
494 pic.width, colors);
495 }
496
497 bool ok = WebPPictureImportRGB(&pic, rgb, rgb_stride);
498 delete[] rgb;
499
500 ok = ok && WebPEncode(&webp_config, &pic);
501 WebPPictureFree(&pic);
502
503 return ok;
504 }
505
506
507 ///////////////////////////////////////////////////////////////////////////////
508
509 #include "SkTRegistry.h"
510
DFactory(SkStream * stream)511 static SkImageDecoder* DFactory(SkStream* stream) {
512 int width, height;
513 if (!webp_parse_header(stream, &width, &height)) {
514 return false;
515 }
516
517 // Magic matches, call decoder
518 return SkNEW(SkWEBPImageDecoder);
519 }
520
sk_libwebp_dfactory(SkStream * stream)521 SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
522 return DFactory(stream);
523 }
524
EFactory(SkImageEncoder::Type t)525 static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
526 return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
527 }
528
sk_libwebp_efactory(SkImageEncoder::Type t)529 SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
530 return EFactory(t);
531 }
532
533 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory);
534 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory);
535