| 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);
|
|
|