| Index: src/images/SkImageDecoder_libwebp.cpp | 
| diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..d4e40c0cd7834a49023e32b43892c895b0e251a7 | 
| --- /dev/null | 
| +++ b/src/images/SkImageDecoder_libwebp.cpp | 
| @@ -0,0 +1,595 @@ | 
| +/* | 
| + * Copyright 2010, The Android Open Source Project | 
| + * | 
| + * Licensed under the Apache License, Version 2.0 (the "License"); | 
| + * you may not use this file except in compliance with the License. | 
| + * You may obtain a copy of the License at | 
| + * | 
| + *     http://www.apache.org/licenses/LICENSE-2.0 | 
| + * | 
| + * Unless required by applicable law or agreed to in writing, software | 
| + * distributed under the License is distributed on an "AS IS" BASIS, | 
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| + * See the License for the specific language governing permissions and | 
| + * limitations under the License. | 
| + */ | 
| + | 
| +#include "SkImageDecoder.h" | 
| +#include "SkImageEncoder.h" | 
| +#include "SkColorPriv.h" | 
| +#include "SkScaledBitmapSampler.h" | 
| +#include "SkStream.h" | 
| +#include "SkTemplates.h" | 
| +#include "SkUtils.h" | 
| +#include "SkTScopedPtr.h" | 
| + | 
| +// A WebP decoder only, on top of (subset of) libwebp | 
| +// For more information on WebP image format, and libwebp library, see: | 
| +//   http://code.google.com/speed/webp/ | 
| +//   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library | 
| +//   http://review.webmproject.org/gitweb?p=libwebp.git | 
| + | 
| +#include <stdio.h> | 
| +extern "C" { | 
| +// If moving libwebp out of skia source tree, path for webp headers must be | 
| +// updated accordingly. Here, we enforce using local copy in webp sub-directory. | 
| +#include "webp/decode.h" | 
| +#include "webp/encode.h" | 
| +} | 
| + | 
| +#ifdef ANDROID | 
| +#include <cutils/properties.h> | 
| + | 
| +// Key to lookup the size of memory buffer set in system property | 
| +static const char KEY_MEM_CAP[] = "ro.media.dec.webp.memcap"; | 
| +#endif | 
| + | 
| +// this enables timing code to report milliseconds for a decode | 
| +//#define TIME_DECODE | 
| + | 
| +////////////////////////////////////////////////////////////////////////// | 
| +////////////////////////////////////////////////////////////////////////// | 
| + | 
| +// Define VP8 I/O on top of Skia stream | 
| + | 
| +////////////////////////////////////////////////////////////////////////// | 
| +////////////////////////////////////////////////////////////////////////// | 
| + | 
| +static const size_t WEBP_VP8_HEADER_SIZE = 64; | 
| +static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); | 
| + | 
| +// Parse headers of RIFF container, and check for valid Webp (VP8) content. | 
| +static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) { | 
| +    unsigned char buffer[WEBP_VP8_HEADER_SIZE]; | 
| +    const uint32_t contentSize = stream->getLength(); | 
| +    const size_t len = stream->read(buffer, WEBP_VP8_HEADER_SIZE); | 
| +    const uint32_t read_bytes = | 
| +            (contentSize < WEBP_VP8_HEADER_SIZE) ? contentSize : WEBP_VP8_HEADER_SIZE; | 
| +    if (len != read_bytes) { | 
| +        return false; // can't read enough | 
| +    } | 
| + | 
| +    WebPBitstreamFeatures features; | 
| +    VP8StatusCode status = WebPGetFeatures(buffer, read_bytes, &features); | 
| +    if (VP8_STATUS_OK != status) { | 
| +        return false; // Invalid WebP file. | 
| +    } | 
| +    *width = features.width; | 
| +    *height = features.height; | 
| +    *alpha = features.has_alpha; | 
| + | 
| +    // sanity check for image size that's about to be decoded. | 
| +    { | 
| +        Sk64 size; | 
| +        size.setMul(*width, *height); | 
| +        if (size.isNeg() || !size.is32()) { | 
| +            return false; | 
| +        } | 
| +        // now check that if we are 4-bytes per pixel, we also don't overflow | 
| +        if (size.get32() > (0x7FFFFFFF >> 2)) { | 
| +            return false; | 
| +        } | 
| +    } | 
| +    return true; | 
| +} | 
| + | 
| +class SkWEBPImageDecoder: public SkImageDecoder { | 
| +public: | 
| +    SkWEBPImageDecoder() { | 
| +        fInputStream = NULL; | 
| +        fOrigWidth = 0; | 
| +        fOrigHeight = 0; | 
| +        fHasAlpha = 0; | 
| +    } | 
| +    virtual ~SkWEBPImageDecoder() { | 
| +        SkSafeUnref(fInputStream); | 
| +    } | 
| + | 
| +    virtual Format getFormat() const SK_OVERRIDE { | 
| +        return kWEBP_Format; | 
| +    } | 
| + | 
| +protected: | 
| +    virtual bool onBuildTileIndex(SkStream *stream, int *width, int *height) SK_OVERRIDE; | 
| +    virtual bool onDecodeRegion(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE; | 
| +    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE; | 
| + | 
| +private: | 
| +    bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); | 
| +    SkStream* fInputStream; | 
| +    int fOrigWidth; | 
| +    int fOrigHeight; | 
| +    int fHasAlpha; | 
| + | 
| +    typedef SkImageDecoder INHERITED; | 
| +}; | 
| + | 
| +////////////////////////////////////////////////////////////////////////// | 
| + | 
| +#ifdef TIME_DECODE | 
| + | 
| +#include "SkTime.h" | 
| + | 
| +class AutoTimeMillis { | 
| +public: | 
| +    AutoTimeMillis(const char label[]) : | 
| +        fLabel(label) { | 
| +        if (NULL == fLabel) { | 
| +            fLabel = ""; | 
| +        } | 
| +        fNow = SkTime::GetMSecs(); | 
| +    } | 
| +    ~AutoTimeMillis() { | 
| +        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); | 
| +    } | 
| +private: | 
| +    const char* fLabel; | 
| +    SkMSec fNow; | 
| +}; | 
| + | 
| +#endif | 
| + | 
| +/////////////////////////////////////////////////////////////////////////////// | 
| + | 
| +// This guy exists just to aid in debugging, as it allows debuggers to just | 
| +// set a break-point in one place to see all error exists. | 
| +static bool return_false(const SkBitmap& bm, const char msg[]) { | 
| +    SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); | 
| +    return false; // must always return false | 
| +} | 
| + | 
| +static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, int hasAlpha) { | 
| +    WEBP_CSP_MODE mode = MODE_LAST; | 
| +    SkBitmap::Config config = decodedBitmap->config(); | 
| +    // For images that have alpha, choose appropriate color mode (MODE_rgbA, | 
| +    // MODE_rgbA_4444) that pre-multiplies RGB pixel values with transparency | 
| +    // factor (alpha). | 
| +    if (config == SkBitmap::kARGB_8888_Config) { | 
| +      mode = hasAlpha ? MODE_rgbA : MODE_RGBA; | 
| +    } else if (config == SkBitmap::kARGB_4444_Config) { | 
| +      mode = hasAlpha ? MODE_rgbA_4444 : MODE_RGBA_4444; | 
| +    } else if (config == SkBitmap::kRGB_565_Config) { | 
| +      mode = MODE_RGB_565; | 
| +    } | 
| +    SkASSERT(MODE_LAST != mode); | 
| +    return mode; | 
| +} | 
| + | 
| +// Incremental WebP image decoding. Reads input buffer of 64K size iteratively | 
| +// and decodes this block to appropriate color-space as per config object. | 
| +static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { | 
| +    WebPIDecoder* idec = WebPIDecode(NULL, 0, config); | 
| +    if (NULL == idec) { | 
| +        WebPFreeDecBuffer(&config->output); | 
| +        return false; | 
| +    } | 
| + | 
| +    stream->rewind(); | 
| +    const uint32_t contentSize = stream->getLength(); | 
| +    const uint32_t readBufferSize = (contentSize < WEBP_IDECODE_BUFFER_SZ) ? | 
| +                                       contentSize : WEBP_IDECODE_BUFFER_SZ; | 
| +    SkAutoMalloc srcStorage(readBufferSize); | 
| +    unsigned char* input = (uint8_t*)srcStorage.get(); | 
| +    if (NULL == input) { | 
| +        WebPIDelete(idec); | 
| +        WebPFreeDecBuffer(&config->output); | 
| +        return false; | 
| +    } | 
| + | 
| +    uint32_t bytesRemaining = contentSize; | 
| +    while (bytesRemaining > 0) { | 
| +        const uint32_t bytesToRead = (bytesRemaining < WEBP_IDECODE_BUFFER_SZ) ? | 
| +                                      bytesRemaining : WEBP_IDECODE_BUFFER_SZ; | 
| +        const size_t bytesRead = stream->read(input, bytesToRead); | 
| +        if (0 == bytesRead) { | 
| +            break; | 
| +        } | 
| + | 
| +        VP8StatusCode status = WebPIAppend(idec, input, bytesRead); | 
| +        if (VP8_STATUS_OK == status || VP8_STATUS_SUSPENDED == status) { | 
| +            bytesRemaining -= bytesRead; | 
| +        } else { | 
| +            break; | 
| +        } | 
| +    } | 
| +    srcStorage.free(); | 
| +    WebPIDelete(idec); | 
| +    WebPFreeDecBuffer(&config->output); | 
| + | 
| +    if (bytesRemaining > 0) { | 
| +        return false; | 
| +    } else { | 
| +        return true; | 
| +    } | 
| +} | 
| + | 
| +static bool webp_get_config_resize(WebPDecoderConfig* config, | 
| +                                   SkBitmap* decodedBitmap, | 
| +                                   int width, int height, int hasAlpha) { | 
| +    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, hasAlpha); | 
| +    if (MODE_LAST == mode) { | 
| +        return false; | 
| +    } | 
| + | 
| +    if (0 == WebPInitDecoderConfig(config)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    config->output.colorspace = mode; | 
| +    config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); | 
| +    config->output.u.RGBA.stride = decodedBitmap->rowBytes(); | 
| +    config->output.u.RGBA.size = decodedBitmap->getSize(); | 
| +    config->output.is_external_memory = 1; | 
| + | 
| +    if (width != decodedBitmap->width() || height != decodedBitmap->height()) { | 
| +        config->options.use_scaling = 1; | 
| +        config->options.scaled_width = decodedBitmap->width(); | 
| +        config->options.scaled_height = decodedBitmap->height(); | 
| +    } | 
| + | 
| +    return true; | 
| +} | 
| + | 
| +static bool webp_get_config_resize_crop(WebPDecoderConfig* config, | 
| +                                        SkBitmap* decodedBitmap, | 
| +                                        const SkIRect& region, int hasAlpha) { | 
| + | 
| +    if (!webp_get_config_resize(config, decodedBitmap, region.width(), | 
| +                                region.height(), hasAlpha)) { | 
| +      return false; | 
| +    } | 
| + | 
| +    config->options.use_cropping = 1; | 
| +    config->options.crop_left = region.fLeft; | 
| +    config->options.crop_top = region.fTop; | 
| +    config->options.crop_width = region.width(); | 
| +    config->options.crop_height = region.height(); | 
| + | 
| +    return true; | 
| +} | 
| + | 
| +bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, | 
| +                                         int width, int height) { | 
| +    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, fHasAlpha); | 
| + | 
| +    // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. | 
| +    if (fHasAlpha) { | 
| +        if (config != SkBitmap::kARGB_4444_Config) { | 
| +            config = SkBitmap::kARGB_8888_Config; | 
| +        } | 
| +    } else { | 
| +        if (config != SkBitmap::kRGB_565_Config && | 
| +            config != SkBitmap::kARGB_4444_Config) { | 
| +            config = SkBitmap::kARGB_8888_Config; | 
| +        } | 
| +    } | 
| + | 
| +    if (!this->chooseFromOneChoice(config, width, height)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    decodedBitmap->setConfig(config, width, height, 0); | 
| + | 
| +    decodedBitmap->setIsOpaque(!fHasAlpha); | 
| + | 
| +    return true; | 
| +} | 
| + | 
| +bool SkWEBPImageDecoder::onBuildTileIndex(SkStream* stream, | 
| +                                          int *width, int *height) { | 
| +    int origWidth, origHeight, hasAlpha; | 
| +    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    stream->rewind(); | 
| +    *width = origWidth; | 
| +    *height = origHeight; | 
| + | 
| +    SkRefCnt_SafeAssign(this->fInputStream, stream); | 
| +    this->fOrigWidth = origWidth; | 
| +    this->fOrigHeight = origHeight; | 
| +    this->fHasAlpha = hasAlpha; | 
| + | 
| +    return true; | 
| +} | 
| + | 
| +static bool is_config_compatible(const SkBitmap& bitmap) { | 
| +    SkBitmap::Config config = bitmap.config(); | 
| +    return config == SkBitmap::kARGB_4444_Config || | 
| +           config == SkBitmap::kRGB_565_Config || | 
| +           config == SkBitmap::kARGB_8888_Config; | 
| +} | 
| + | 
| +bool SkWEBPImageDecoder::onDecodeRegion(SkBitmap* decodedBitmap, | 
| +                                        const SkIRect& region) { | 
| +    SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight); | 
| + | 
| +    if (!rect.intersect(region)) { | 
| +        // If the requested region is entirely outsides the image, return false | 
| +        return false; | 
| +    } | 
| + | 
| +    const int sampleSize = this->getSampleSize(); | 
| +    SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize); | 
| +    const int width = sampler.scaledWidth(); | 
| +    const int height = sampler.scaledHeight(); | 
| + | 
| +    // The image can be decoded directly to decodedBitmap if | 
| +    //   1. the region is within the image range | 
| +    //   2. bitmap's config is compatible | 
| +    //   3. bitmap's size is same as the required region (after sampled) | 
| +    bool directDecode = (rect == region) && | 
| +                        (decodedBitmap->isNull() || | 
| +                         (is_config_compatible(*decodedBitmap) && | 
| +                         (decodedBitmap->width() == width) && | 
| +                         (decodedBitmap->height() == height))); | 
| +    SkTScopedPtr<SkBitmap> adb; | 
| +    SkBitmap *bitmap = decodedBitmap; | 
| + | 
| +    if (!directDecode) { | 
| +        // allocates a temp bitmap | 
| +        bitmap = new SkBitmap; | 
| +        adb.reset(bitmap); | 
| +    } | 
| + | 
| +    if (bitmap->isNull()) { | 
| +        if (!setDecodeConfig(bitmap, width, height)) { | 
| +            return false; | 
| +        } | 
| +        // alloc from native heap if it is a temp bitmap. (prevent GC) | 
| +        bool allocResult = (bitmap == decodedBitmap) | 
| +                               ? allocPixelRef(bitmap, NULL) | 
| +                               : bitmap->allocPixels(); | 
| +        if (!allocResult) { | 
| +            return return_false(*decodedBitmap, "allocPixelRef"); | 
| +        } | 
| +    } else { | 
| +        // This is also called in setDecodeConfig in above block. | 
| +        // i.e., when bitmap->isNull() is true. | 
| +        if (!chooseFromOneChoice(bitmap->config(), width, height)) { | 
| +            return false; | 
| +        } | 
| +    } | 
| + | 
| +    SkAutoLockPixels alp(*bitmap); | 
| +    WebPDecoderConfig config; | 
| +    if (!webp_get_config_resize_crop(&config, bitmap, rect, fHasAlpha)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    // Decode the WebP image data stream using WebP incremental decoding for | 
| +    // the specified cropped image-region. | 
| +    if (!webp_idecode(this->fInputStream, &config)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    if (!directDecode) { | 
| +        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(), | 
| +                   region.width(), region.height(), rect.x(), rect.y()); | 
| +    } | 
| +    return true; | 
| +} | 
| + | 
| +bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, | 
| +                                  Mode mode) { | 
| +#ifdef TIME_DECODE | 
| +    AutoTimeMillis atm("WEBP Decode"); | 
| +#endif | 
| + | 
| +    int origWidth, origHeight, hasAlpha; | 
| +    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { | 
| +        return false; | 
| +    } | 
| +    this->fHasAlpha = hasAlpha; | 
| + | 
| +    const int sampleSize = this->getSampleSize(); | 
| +    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); | 
| + | 
| +    // If only bounds are requested, done | 
| +    if (SkImageDecoder::kDecodeBounds_Mode == mode) { | 
| +        if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), | 
| +                             sampler.scaledHeight())) { | 
| +            return false; | 
| +        } | 
| +        return true; | 
| +    } | 
| + | 
| +    // No Bitmap reuse supported for this format | 
| +    if (!decodedBitmap->isNull()) { | 
| +        return false; | 
| +    } | 
| +    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), | 
| +                         sampler.scaledHeight())) { | 
| +        return false; | 
| +    } | 
| + | 
| +    if (!this->allocPixelRef(decodedBitmap, NULL)) { | 
| +        return return_false(*decodedBitmap, "allocPixelRef"); | 
| +    } | 
| + | 
| +    SkAutoLockPixels alp(*decodedBitmap); | 
| + | 
| +    WebPDecoderConfig config; | 
| +    if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, | 
| +                                hasAlpha)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    // Decode the WebP image data stream using WebP incremental decoding. | 
| +    return webp_idecode(stream, &config); | 
| +} | 
| + | 
| +/////////////////////////////////////////////////////////////////////////////// | 
| + | 
| +typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, | 
| +                                 const SkPMColor* SK_RESTRICT ctable); | 
| + | 
| +static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | 
| +                             const SkPMColor*) { | 
| +  const uint32_t* SK_RESTRICT src = (const uint32_t*)in; | 
| +  for (int i = 0; i < width; ++i) { | 
| +      const uint32_t c = *src++; | 
| +      rgb[0] = SkGetPackedR32(c); | 
| +      rgb[1] = SkGetPackedG32(c); | 
| +      rgb[2] = SkGetPackedB32(c); | 
| +      rgb += 3; | 
| +  } | 
| +} | 
| + | 
| +static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | 
| +                           const SkPMColor*) { | 
| +  const uint16_t* SK_RESTRICT src = (const uint16_t*)in; | 
| +  for (int i = 0; i < width; ++i) { | 
| +      const uint16_t c = *src++; | 
| +      rgb[0] = SkPacked16ToR32(c); | 
| +      rgb[1] = SkPacked16ToG32(c); | 
| +      rgb[2] = SkPacked16ToB32(c); | 
| +      rgb += 3; | 
| +  } | 
| +} | 
| + | 
| +static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | 
| +                             const SkPMColor*) { | 
| +  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in; | 
| +  for (int i = 0; i < width; ++i) { | 
| +      const SkPMColor16 c = *src++; | 
| +      rgb[0] = SkPacked4444ToR32(c); | 
| +      rgb[1] = SkPacked4444ToG32(c); | 
| +      rgb[2] = SkPacked4444ToB32(c); | 
| +      rgb += 3; | 
| +  } | 
| +} | 
| + | 
| +static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width, | 
| +                          const SkPMColor* SK_RESTRICT ctable) { | 
| +  const uint8_t* SK_RESTRICT src = (const uint8_t*)in; | 
| +  for (int i = 0; i < width; ++i) { | 
| +      const uint32_t c = ctable[*src++]; | 
| +      rgb[0] = SkGetPackedR32(c); | 
| +      rgb[1] = SkGetPackedG32(c); | 
| +      rgb[2] = SkGetPackedB32(c); | 
| +      rgb += 3; | 
| +  } | 
| +} | 
| + | 
| +static ScanlineImporter ChooseImporter(const SkBitmap::Config& config) { | 
| +    switch (config) { | 
| +        case SkBitmap::kARGB_8888_Config: | 
| +            return ARGB_8888_To_RGB; | 
| +        case SkBitmap::kRGB_565_Config: | 
| +            return RGB_565_To_RGB; | 
| +        case SkBitmap::kARGB_4444_Config: | 
| +            return ARGB_4444_To_RGB; | 
| +        case SkBitmap::kIndex8_Config: | 
| +            return Index8_To_RGB; | 
| +        default: | 
| +            return NULL; | 
| +    } | 
| +} | 
| + | 
| +static int stream_writer(const uint8_t* data, size_t data_size, | 
| +                         const WebPPicture* const picture) { | 
| +  SkWStream* const stream = (SkWStream*)picture->custom_ptr; | 
| +  return stream->write(data, data_size) ? 1 : 0; | 
| +} | 
| + | 
| +class SkWEBPImageEncoder : public SkImageEncoder { | 
| +protected: | 
| +    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE; | 
| + | 
| +private: | 
| +    typedef SkImageEncoder INHERITED; | 
| +}; | 
| + | 
| +bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, | 
| +                                  int quality) { | 
| +    const SkBitmap::Config config = bm.getConfig(); | 
| +    const ScanlineImporter scanline_import = ChooseImporter(config); | 
| +    if (NULL == scanline_import) { | 
| +        return false; | 
| +    } | 
| + | 
| +    SkAutoLockPixels alp(bm); | 
| +    SkAutoLockColors ctLocker; | 
| +    if (NULL == bm.getPixels()) { | 
| +        return false; | 
| +    } | 
| + | 
| +    WebPConfig webp_config; | 
| +    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, quality)) { | 
| +        return false; | 
| +    } | 
| + | 
| +    WebPPicture pic; | 
| +    WebPPictureInit(&pic); | 
| +    pic.width = bm.width(); | 
| +    pic.height = bm.height(); | 
| +    pic.writer = stream_writer; | 
| +    pic.custom_ptr = (void*)stream; | 
| + | 
| +    const SkPMColor* colors = ctLocker.lockColors(bm); | 
| +    const uint8_t* src = (uint8_t*)bm.getPixels(); | 
| +    const int rgbStride = pic.width * 3; | 
| + | 
| +    // Import (for each scanline) the bit-map image (in appropriate color-space) | 
| +    // to RGB color space. | 
| +    uint8_t* rgb = new uint8_t[rgbStride * pic.height]; | 
| +    for (int y = 0; y < pic.height; ++y) { | 
| +        scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride, | 
| +                        pic.width, colors); | 
| +    } | 
| + | 
| +    bool ok = WebPPictureImportRGB(&pic, rgb, rgbStride); | 
| +    delete[] rgb; | 
| + | 
| +    ok = ok && WebPEncode(&webp_config, &pic); | 
| +    WebPPictureFree(&pic); | 
| + | 
| +    return ok; | 
| +} | 
| + | 
| + | 
| +/////////////////////////////////////////////////////////////////////////////// | 
| +DEFINE_DECODER_CREATOR(WEBPImageDecoder); | 
| +DEFINE_ENCODER_CREATOR(WEBPImageEncoder); | 
| +/////////////////////////////////////////////////////////////////////////////// | 
| + | 
| +#include "SkTRegistry.h" | 
| + | 
| +static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) { | 
| +    int width, height, hasAlpha; | 
| +    if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { | 
| +        return NULL; | 
| +    } | 
| + | 
| +    // Magic matches, call decoder | 
| +    return SkNEW(SkWEBPImageDecoder); | 
| +} | 
| + | 
| +static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { | 
| +      return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL; | 
| +} | 
| + | 
| +static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory); | 
| +static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory); | 
|  |