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