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    bool success = true;
187    VP8StatusCode status = VP8_STATUS_SUSPENDED;
188    do {
189        const uint32_t bytes_to_read = WEBP_IDECODE_BUFFER_SZ;
190        const size_t bytes_read = stream->read(input, bytes_to_read);
191        if (bytes_read == 0) {
192            success = false;
193            break;
194        }
195
196        status = WebPIAppend(idec, input, bytes_read);
197        if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) {
198            success = false;
199            break;
200        }
201    } while (VP8_STATUS_OK != status);
202    srcStorage.free();
203    WebPIDelete(idec);
204    WebPFreeDecBuffer(&config.output);
205
206    return success;
207}
208
209static bool webp_get_config_resize(WebPDecoderConfig& config,
210                                   SkBitmap* decodedBitmap,
211                                   int width, int height, int hasAlpha) {
212    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha);
213    if (mode == MODE_LAST) {
214        return false;
215    }
216
217    if (WebPInitDecoderConfig(&config) == 0) {
218        return false;
219    }
220
221    config.output.colorspace = mode;
222    config.output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
223    config.output.u.RGBA.stride = decodedBitmap->rowBytes();
224    config.output.u.RGBA.size = decodedBitmap->getSize();
225    config.output.is_external_memory = 1;
226
227    if (width != decodedBitmap->width() ||
228        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
237static bool webp_get_config_resize_crop(WebPDecoderConfig& config,
238                                        SkBitmap* decodedBitmap,
239                                        SkIRect region, int hasAlpha) {
240
241    if (!webp_get_config_resize(
242        config, decodedBitmap, region.width(), region.height(), hasAlpha)) {
243      return false;
244    }
245
246    config.options.use_cropping = 1;
247    config.options.crop_left = region.fLeft;
248    config.options.crop_top = region.fTop;
249    config.options.crop_width = region.width();
250    config.options.crop_height = region.height();
251
252    return true;
253}
254
255bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
256                                         int width, int height) {
257    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
258
259    // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
260    if (hasAlpha) {
261        if (config != SkBitmap::kARGB_4444_Config) {
262            config = SkBitmap::kARGB_8888_Config;
263        }
264    } else {
265        if (config != SkBitmap::kRGB_565_Config &&
266            config != SkBitmap::kARGB_4444_Config) {
267            config = SkBitmap::kARGB_8888_Config;
268        }
269    }
270
271    if (!this->chooseFromOneChoice(config, width, height)) {
272        return false;
273    }
274
275    decodedBitmap->setConfig(config, width, height, 0);
276
277    decodedBitmap->setIsOpaque(!hasAlpha);
278
279    return true;
280}
281
282bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream,
283                                          int *width, int *height) {
284    int origWidth, origHeight, hasAlpha;
285    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
286        return false;
287    }
288
289    stream->rewind();
290    *width = origWidth;
291    *height = origHeight;
292
293    this->inputStream = stream;
294    this->origWidth = origWidth;
295    this->origHeight = origHeight;
296    this->hasAlpha = hasAlpha;
297
298    return true;
299}
300
301static bool isConfigCompatible(SkBitmap* bitmap) {
302    SkBitmap::Config config = bitmap->config();
303    return config == SkBitmap::kARGB_4444_Config ||
304           config == SkBitmap::kRGB_565_Config ||
305           config == SkBitmap::kARGB_8888_Config;
306}
307
308bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap,
309                                        SkIRect region) {
310    SkIRect rect = SkIRect::MakeWH(origWidth, origHeight);
311
312    if (!rect.intersect(region)) {
313        // If the requested region is entirely outsides the image, just
314        // returns false
315        return false;
316    }
317
318    const int sampleSize = this->getSampleSize();
319    SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
320    const int width = sampler.scaledWidth();
321    const int height = sampler.scaledHeight();
322
323    // The image can be decoded directly to decodedBitmap if
324    //   1. the region is within the image range
325    //   2. bitmap's config is compatible
326    //   3. bitmap's size is same as the required region (after sampled)
327    bool directDecode = (rect == region) &&
328                        (decodedBitmap->isNull() ||
329                         (isConfigCompatible(decodedBitmap) &&
330                         (decodedBitmap->width() == width) &&
331                         (decodedBitmap->height() == height)));
332    SkTScopedPtr<SkBitmap> adb;
333    SkBitmap *bitmap = decodedBitmap;
334
335    if (!directDecode) {
336        // allocates a temp bitmap
337        bitmap = new SkBitmap;
338        adb.reset(bitmap);
339    }
340
341    if (bitmap->isNull()) {
342        if (!setDecodeConfig(bitmap, width, height)) {
343            return false;
344        }
345        // alloc from native heap if it is a temp bitmap. (prevent GC)
346        bool allocResult = (bitmap == decodedBitmap)
347                               ? allocPixelRef(bitmap, NULL)
348                               : bitmap->allocPixels();
349        if (!allocResult) {
350            return return_false(*decodedBitmap, "allocPixelRef");
351        }
352    } else {
353        // This is also called in setDecodeConfig in above block.
354        // i.e., when bitmap->isNull() is true.
355        if (!chooseFromOneChoice(bitmap->config(), width, height)) {
356            return false;
357        }
358    }
359
360    SkAutoLockPixels alp(*bitmap);
361    WebPDecoderConfig config;
362    if (!webp_get_config_resize_crop(config, bitmap, rect, hasAlpha)) {
363        return false;
364    }
365
366    // Decode the WebP image data stream using WebP incremental decoding for
367    // the specified cropped image-region.
368    if (!webp_idecode(this->inputStream, config)) {
369        return false;
370    }
371
372    if (!directDecode) {
373        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
374                  region.width(), region.height(), rect.x(), rect.y());
375    }
376    return true;
377}
378
379bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
380                                  Mode mode) {
381#ifdef TIME_DECODE
382    AutoTimeMillis atm("WEBP Decode");
383#endif
384
385    int origWidth, origHeight, hasAlpha;
386    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
387        return false;
388    }
389    this->hasAlpha = hasAlpha;
390
391    const int sampleSize = this->getSampleSize();
392    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
393
394    // If only bounds are requested, done
395    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
396        if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
397                             sampler.scaledHeight())) {
398            return false;
399        }
400        return true;
401    }
402#ifdef SK_BUILD_FOR_ANDROID
403    // No Bitmap reuse supported for this format
404    if (!decodedBitmap->isNull()) {
405        return false;
406    }
407#endif
408    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
409                         sampler.scaledHeight())) {
410        return false;
411    }
412
413    if (!this->allocPixelRef(decodedBitmap, NULL)) {
414        return return_false(*decodedBitmap, "allocPixelRef");
415    }
416
417    SkAutoLockPixels alp(*decodedBitmap);
418
419    WebPDecoderConfig config;
420    if (!webp_get_config_resize(config, decodedBitmap, origWidth, origHeight,
421                                hasAlpha)) {
422        return false;
423    }
424
425    // Decode the WebP image data stream using WebP incremental decoding.
426    return webp_idecode(stream, config);
427}
428
429///////////////////////////////////////////////////////////////////////////////
430
431typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
432                                 const SkPMColor* SK_RESTRICT ctable);
433
434static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
435                             const SkPMColor*) {
436  const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
437  for (int i = 0; i < width; ++i) {
438      const uint32_t c = *src++;
439      rgb[0] = SkGetPackedR32(c);
440      rgb[1] = SkGetPackedG32(c);
441      rgb[2] = SkGetPackedB32(c);
442      rgb += 3;
443  }
444}
445
446static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
447                           const SkPMColor*) {
448  const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
449  for (int i = 0; i < width; ++i) {
450      const uint16_t c = *src++;
451      rgb[0] = SkPacked16ToR32(c);
452      rgb[1] = SkPacked16ToG32(c);
453      rgb[2] = SkPacked16ToB32(c);
454      rgb += 3;
455  }
456}
457
458static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
459                             const SkPMColor*) {
460  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
461  for (int i = 0; i < width; ++i) {
462      const SkPMColor16 c = *src++;
463      rgb[0] = SkPacked4444ToR32(c);
464      rgb[1] = SkPacked4444ToG32(c);
465      rgb[2] = SkPacked4444ToB32(c);
466      rgb += 3;
467  }
468}
469
470static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
471                          const SkPMColor* SK_RESTRICT ctable) {
472  const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
473  for (int i = 0; i < width; ++i) {
474      const uint32_t c = ctable[*src++];
475      rgb[0] = SkGetPackedR32(c);
476      rgb[1] = SkGetPackedG32(c);
477      rgb[2] = SkGetPackedB32(c);
478      rgb += 3;
479  }
480}
481
482static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) {
483    switch (config) {
484        case SkBitmap::kARGB_8888_Config:
485            return ARGB_8888_To_RGB;
486        case SkBitmap::kRGB_565_Config:
487            return RGB_565_To_RGB;
488        case SkBitmap::kARGB_4444_Config:
489            return ARGB_4444_To_RGB;
490        case SkBitmap::kIndex8_Config:
491            return Index8_To_RGB;
492        default:
493            return NULL;
494    }
495}
496
497static int StreamWriter(const uint8_t* data, size_t data_size,
498                        const WebPPicture* const picture) {
499  SkWStream* const stream = (SkWStream*)picture->custom_ptr;
500  return stream->write(data, data_size) ? 1 : 0;
501}
502
503class SkWEBPImageEncoder : public SkImageEncoder {
504protected:
505    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
506};
507
508bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
509                                  int quality) {
510    const SkBitmap::Config config = bm.getConfig();
511    const ScanlineImporter scanline_import = ChooseImporter(config);
512    if (NULL == scanline_import) {
513        return false;
514    }
515
516    SkAutoLockPixels alp(bm);
517    SkAutoLockColors ctLocker;
518    if (NULL == bm.getPixels()) {
519        return false;
520    }
521
522    WebPConfig webp_config;
523    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) {
524        return false;
525    }
526
527    WebPPicture pic;
528    WebPPictureInit(&pic);
529    pic.width = bm.width();
530    pic.height = bm.height();
531    pic.writer = StreamWriter;
532    pic.custom_ptr = (void*)stream;
533
534    const SkPMColor* colors = ctLocker.lockColors(bm);
535    const uint8_t* src = (uint8_t*)bm.getPixels();
536    const int rgb_stride = pic.width * 3;
537
538    // Import (for each scanline) the bit-map image (in appropriate color-space)
539    // to RGB color space.
540    uint8_t* rgb = new uint8_t[rgb_stride * pic.height];
541    for (int y = 0; y < pic.height; ++y) {
542        scanline_import(src + y * bm.rowBytes(), rgb + y * rgb_stride,
543                        pic.width, colors);
544    }
545
546    bool ok = WebPPictureImportRGB(&pic, rgb, rgb_stride);
547    delete[] rgb;
548
549    ok = ok && WebPEncode(&webp_config, &pic);
550    WebPPictureFree(&pic);
551
552    return ok;
553}
554
555
556///////////////////////////////////////////////////////////////////////////////
557DEFINE_DECODER_CREATOR(WEBPImageDecoder);
558DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
559///////////////////////////////////////////////////////////////////////////////
560
561#include "SkTRegistry.h"
562
563static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
564    int width, height, hasAlpha;
565    if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
566        return NULL;
567    }
568
569    // Magic matches, call decoder
570    return SkNEW(SkWEBPImageDecoder);
571}
572
573static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
574      return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
575}
576
577static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory);
578static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory);
579