gfx/skia/trunk/src/images/SkImageDecoder_libwebp.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/images/SkImageDecoder_libwebp.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,683 @@
     1.4 +/*
     1.5 + * Copyright 2010, The Android Open Source Project
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * Unless required by applicable law or agreed to in writing, software
    1.14 + * distributed under the License is distributed on an "AS IS" BASIS,
    1.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.16 + * See the License for the specific language governing permissions and
    1.17 + * limitations under the License.
    1.18 + */
    1.19 +
    1.20 +#include "SkImageDecoder.h"
    1.21 +#include "SkImageEncoder.h"
    1.22 +#include "SkColorPriv.h"
    1.23 +#include "SkScaledBitmapSampler.h"
    1.24 +#include "SkStream.h"
    1.25 +#include "SkTemplates.h"
    1.26 +#include "SkUtils.h"
    1.27 +
    1.28 +// A WebP decoder only, on top of (subset of) libwebp
    1.29 +// For more information on WebP image format, and libwebp library, see:
    1.30 +//   http://code.google.com/speed/webp/
    1.31 +//   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
    1.32 +//   http://review.webmproject.org/gitweb?p=libwebp.git
    1.33 +
    1.34 +#include <stdio.h>
    1.35 +extern "C" {
    1.36 +// If moving libwebp out of skia source tree, path for webp headers must be
    1.37 +// updated accordingly. Here, we enforce using local copy in webp sub-directory.
    1.38 +#include "webp/decode.h"
    1.39 +#include "webp/encode.h"
    1.40 +}
    1.41 +
    1.42 +// this enables timing code to report milliseconds for a decode
    1.43 +//#define TIME_DECODE
    1.44 +
    1.45 +//////////////////////////////////////////////////////////////////////////
    1.46 +//////////////////////////////////////////////////////////////////////////
    1.47 +
    1.48 +// Define VP8 I/O on top of Skia stream
    1.49 +
    1.50 +//////////////////////////////////////////////////////////////////////////
    1.51 +//////////////////////////////////////////////////////////////////////////
    1.52 +
    1.53 +static const size_t WEBP_VP8_HEADER_SIZE = 64;
    1.54 +static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16);
    1.55 +
    1.56 +// Parse headers of RIFF container, and check for valid Webp (VP8) content.
    1.57 +static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) {
    1.58 +    unsigned char buffer[WEBP_VP8_HEADER_SIZE];
    1.59 +    size_t bytesToRead = WEBP_VP8_HEADER_SIZE;
    1.60 +    size_t totalBytesRead = 0;
    1.61 +    do {
    1.62 +        unsigned char* dst = buffer + totalBytesRead;
    1.63 +        const size_t bytesRead = stream->read(dst, bytesToRead);
    1.64 +        if (0 == bytesRead) {
    1.65 +            // Could not read any bytes. Check to see if we are at the end (exit
    1.66 +            // condition), and continue reading if not. Important for streams
    1.67 +            // that do not have all the data ready.
    1.68 +            continue;
    1.69 +        }
    1.70 +        bytesToRead -= bytesRead;
    1.71 +        totalBytesRead += bytesRead;
    1.72 +        SkASSERT(bytesToRead + totalBytesRead == WEBP_VP8_HEADER_SIZE);
    1.73 +    } while (!stream->isAtEnd() && bytesToRead > 0);
    1.74 +
    1.75 +    WebPBitstreamFeatures features;
    1.76 +    VP8StatusCode status = WebPGetFeatures(buffer, totalBytesRead, &features);
    1.77 +    if (VP8_STATUS_OK != status) {
    1.78 +        return false; // Invalid WebP file.
    1.79 +    }
    1.80 +    *width = features.width;
    1.81 +    *height = features.height;
    1.82 +    *alpha = features.has_alpha;
    1.83 +
    1.84 +    // sanity check for image size that's about to be decoded.
    1.85 +    {
    1.86 +        int64_t size = sk_64_mul(*width, *height);
    1.87 +        if (!sk_64_isS32(size)) {
    1.88 +            return false;
    1.89 +        }
    1.90 +        // now check that if we are 4-bytes per pixel, we also don't overflow
    1.91 +        if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
    1.92 +            return false;
    1.93 +        }
    1.94 +    }
    1.95 +    return true;
    1.96 +}
    1.97 +
    1.98 +class SkWEBPImageDecoder: public SkImageDecoder {
    1.99 +public:
   1.100 +    SkWEBPImageDecoder() {
   1.101 +        fInputStream = NULL;
   1.102 +        fOrigWidth = 0;
   1.103 +        fOrigHeight = 0;
   1.104 +        fHasAlpha = 0;
   1.105 +    }
   1.106 +    virtual ~SkWEBPImageDecoder() {
   1.107 +        SkSafeUnref(fInputStream);
   1.108 +    }
   1.109 +
   1.110 +    virtual Format getFormat() const SK_OVERRIDE {
   1.111 +        return kWEBP_Format;
   1.112 +    }
   1.113 +
   1.114 +protected:
   1.115 +    virtual bool onBuildTileIndex(SkStreamRewindable *stream, int *width, int *height) SK_OVERRIDE;
   1.116 +    virtual bool onDecodeSubset(SkBitmap* bitmap, const SkIRect& rect) SK_OVERRIDE;
   1.117 +    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode) SK_OVERRIDE;
   1.118 +
   1.119 +private:
   1.120 +    /**
   1.121 +     *  Called when determining the output config to request to webp.
   1.122 +     *  If the image does not have alpha, there is no need to premultiply.
   1.123 +     *  If the caller wants unpremultiplied colors, that is respected.
   1.124 +     */
   1.125 +    bool shouldPremultiply() const {
   1.126 +        return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors();
   1.127 +    }
   1.128 +
   1.129 +    bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height);
   1.130 +
   1.131 +    SkStream* fInputStream;
   1.132 +    int fOrigWidth;
   1.133 +    int fOrigHeight;
   1.134 +    int fHasAlpha;
   1.135 +
   1.136 +    typedef SkImageDecoder INHERITED;
   1.137 +};
   1.138 +
   1.139 +//////////////////////////////////////////////////////////////////////////
   1.140 +
   1.141 +#ifdef TIME_DECODE
   1.142 +
   1.143 +#include "SkTime.h"
   1.144 +
   1.145 +class AutoTimeMillis {
   1.146 +public:
   1.147 +    AutoTimeMillis(const char label[]) :
   1.148 +        fLabel(label) {
   1.149 +        if (NULL == fLabel) {
   1.150 +            fLabel = "";
   1.151 +        }
   1.152 +        fNow = SkTime::GetMSecs();
   1.153 +    }
   1.154 +    ~AutoTimeMillis() {
   1.155 +        SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow);
   1.156 +    }
   1.157 +private:
   1.158 +    const char* fLabel;
   1.159 +    SkMSec fNow;
   1.160 +};
   1.161 +
   1.162 +#endif
   1.163 +
   1.164 +///////////////////////////////////////////////////////////////////////////////
   1.165 +
   1.166 +// This guy exists just to aid in debugging, as it allows debuggers to just
   1.167 +// set a break-point in one place to see all error exists.
   1.168 +static bool return_false(const SkBitmap& bm, const char msg[]) {
   1.169 +    SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height()));
   1.170 +    return false; // must always return false
   1.171 +}
   1.172 +
   1.173 +static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) {
   1.174 +    WEBP_CSP_MODE mode = MODE_LAST;
   1.175 +    SkBitmap::Config config = decodedBitmap->config();
   1.176 +
   1.177 +    if (config == SkBitmap::kARGB_8888_Config) {
   1.178 +        #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
   1.179 +            mode = premultiply ? MODE_bgrA : MODE_BGRA;
   1.180 +        #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
   1.181 +            mode = premultiply ? MODE_rgbA : MODE_RGBA;
   1.182 +        #else
   1.183 +            #error "Skia uses BGRA or RGBA byte order"
   1.184 +        #endif
   1.185 +    } else if (config == SkBitmap::kARGB_4444_Config) {
   1.186 +        mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444;
   1.187 +    } else if (config == SkBitmap::kRGB_565_Config) {
   1.188 +        mode = MODE_RGB_565;
   1.189 +    }
   1.190 +    SkASSERT(MODE_LAST != mode);
   1.191 +    return mode;
   1.192 +}
   1.193 +
   1.194 +// Incremental WebP image decoding. Reads input buffer of 64K size iteratively
   1.195 +// and decodes this block to appropriate color-space as per config object.
   1.196 +static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) {
   1.197 +    WebPIDecoder* idec = WebPIDecode(NULL, 0, config);
   1.198 +    if (NULL == idec) {
   1.199 +        WebPFreeDecBuffer(&config->output);
   1.200 +        return false;
   1.201 +    }
   1.202 +
   1.203 +    if (!stream->rewind()) {
   1.204 +        SkDebugf("Failed to rewind webp stream!");
   1.205 +        return false;
   1.206 +    }
   1.207 +    const size_t readBufferSize = stream->hasLength() ?
   1.208 +            SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ;
   1.209 +    SkAutoMalloc srcStorage(readBufferSize);
   1.210 +    unsigned char* input = (uint8_t*)srcStorage.get();
   1.211 +    if (NULL == input) {
   1.212 +        WebPIDelete(idec);
   1.213 +        WebPFreeDecBuffer(&config->output);
   1.214 +        return false;
   1.215 +    }
   1.216 +
   1.217 +    bool success = true;
   1.218 +    VP8StatusCode status = VP8_STATUS_SUSPENDED;
   1.219 +    do {
   1.220 +        const size_t bytesRead = stream->read(input, readBufferSize);
   1.221 +        if (0 == bytesRead) {
   1.222 +            success = false;
   1.223 +            break;
   1.224 +        }
   1.225 +
   1.226 +        status = WebPIAppend(idec, input, bytesRead);
   1.227 +        if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) {
   1.228 +            success = false;
   1.229 +            break;
   1.230 +        }
   1.231 +    } while (VP8_STATUS_OK != status);
   1.232 +    srcStorage.free();
   1.233 +    WebPIDelete(idec);
   1.234 +    WebPFreeDecBuffer(&config->output);
   1.235 +
   1.236 +    return success;
   1.237 +}
   1.238 +
   1.239 +static bool webp_get_config_resize(WebPDecoderConfig* config,
   1.240 +                                   SkBitmap* decodedBitmap,
   1.241 +                                   int width, int height, bool premultiply) {
   1.242 +    WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply);
   1.243 +    if (MODE_LAST == mode) {
   1.244 +        return false;
   1.245 +    }
   1.246 +
   1.247 +    if (0 == WebPInitDecoderConfig(config)) {
   1.248 +        return false;
   1.249 +    }
   1.250 +
   1.251 +    config->output.colorspace = mode;
   1.252 +    config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels();
   1.253 +    config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes();
   1.254 +    config->output.u.RGBA.size = decodedBitmap->getSize();
   1.255 +    config->output.is_external_memory = 1;
   1.256 +
   1.257 +    if (width != decodedBitmap->width() || height != decodedBitmap->height()) {
   1.258 +        config->options.use_scaling = 1;
   1.259 +        config->options.scaled_width = decodedBitmap->width();
   1.260 +        config->options.scaled_height = decodedBitmap->height();
   1.261 +    }
   1.262 +
   1.263 +    return true;
   1.264 +}
   1.265 +
   1.266 +static bool webp_get_config_resize_crop(WebPDecoderConfig* config,
   1.267 +                                        SkBitmap* decodedBitmap,
   1.268 +                                        const SkIRect& region, bool premultiply) {
   1.269 +
   1.270 +    if (!webp_get_config_resize(config, decodedBitmap, region.width(),
   1.271 +                                region.height(), premultiply)) {
   1.272 +      return false;
   1.273 +    }
   1.274 +
   1.275 +    config->options.use_cropping = 1;
   1.276 +    config->options.crop_left = region.fLeft;
   1.277 +    config->options.crop_top = region.fTop;
   1.278 +    config->options.crop_width = region.width();
   1.279 +    config->options.crop_height = region.height();
   1.280 +
   1.281 +    return true;
   1.282 +}
   1.283 +
   1.284 +bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap,
   1.285 +                                         int width, int height) {
   1.286 +    SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, SkToBool(fHasAlpha));
   1.287 +
   1.288 +    // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats.
   1.289 +    if (fHasAlpha) {
   1.290 +        if (config != SkBitmap::kARGB_4444_Config) {
   1.291 +            config = SkBitmap::kARGB_8888_Config;
   1.292 +        }
   1.293 +    } else {
   1.294 +        if (config != SkBitmap::kRGB_565_Config &&
   1.295 +            config != SkBitmap::kARGB_4444_Config) {
   1.296 +            config = SkBitmap::kARGB_8888_Config;
   1.297 +        }
   1.298 +    }
   1.299 +
   1.300 +    if (!this->chooseFromOneChoice(config, width, height)) {
   1.301 +        return false;
   1.302 +    }
   1.303 +
   1.304 +    return decodedBitmap->setConfig(config, width, height, 0,
   1.305 +                                    fHasAlpha ? kPremul_SkAlphaType : kOpaque_SkAlphaType);
   1.306 +}
   1.307 +
   1.308 +bool SkWEBPImageDecoder::onBuildTileIndex(SkStreamRewindable* stream,
   1.309 +                                          int *width, int *height) {
   1.310 +    int origWidth, origHeight, hasAlpha;
   1.311 +    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
   1.312 +        return false;
   1.313 +    }
   1.314 +
   1.315 +    if (!stream->rewind()) {
   1.316 +        SkDebugf("Failed to rewind webp stream!");
   1.317 +        return false;
   1.318 +    }
   1.319 +
   1.320 +    *width = origWidth;
   1.321 +    *height = origHeight;
   1.322 +
   1.323 +    SkRefCnt_SafeAssign(this->fInputStream, stream);
   1.324 +    this->fOrigWidth = origWidth;
   1.325 +    this->fOrigHeight = origHeight;
   1.326 +    this->fHasAlpha = hasAlpha;
   1.327 +
   1.328 +    return true;
   1.329 +}
   1.330 +
   1.331 +static bool is_config_compatible(const SkBitmap& bitmap) {
   1.332 +    SkBitmap::Config config = bitmap.config();
   1.333 +    return config == SkBitmap::kARGB_4444_Config ||
   1.334 +           config == SkBitmap::kRGB_565_Config ||
   1.335 +           config == SkBitmap::kARGB_8888_Config;
   1.336 +}
   1.337 +
   1.338 +bool SkWEBPImageDecoder::onDecodeSubset(SkBitmap* decodedBitmap,
   1.339 +                                        const SkIRect& region) {
   1.340 +    SkIRect rect = SkIRect::MakeWH(fOrigWidth, fOrigHeight);
   1.341 +
   1.342 +    if (!rect.intersect(region)) {
   1.343 +        // If the requested region is entirely outsides the image, return false
   1.344 +        return false;
   1.345 +    }
   1.346 +
   1.347 +    const int sampleSize = this->getSampleSize();
   1.348 +    SkScaledBitmapSampler sampler(rect.width(), rect.height(), sampleSize);
   1.349 +    const int width = sampler.scaledWidth();
   1.350 +    const int height = sampler.scaledHeight();
   1.351 +
   1.352 +    // The image can be decoded directly to decodedBitmap if
   1.353 +    //   1. the region is within the image range
   1.354 +    //   2. bitmap's config is compatible
   1.355 +    //   3. bitmap's size is same as the required region (after sampled)
   1.356 +    bool directDecode = (rect == region) &&
   1.357 +                        (decodedBitmap->isNull() ||
   1.358 +                         (is_config_compatible(*decodedBitmap) &&
   1.359 +                         (decodedBitmap->width() == width) &&
   1.360 +                         (decodedBitmap->height() == height)));
   1.361 +
   1.362 +    SkBitmap tmpBitmap;
   1.363 +    SkBitmap *bitmap = decodedBitmap;
   1.364 +
   1.365 +    if (!directDecode) {
   1.366 +        bitmap = &tmpBitmap;
   1.367 +    }
   1.368 +
   1.369 +    if (bitmap->isNull()) {
   1.370 +        if (!setDecodeConfig(bitmap, width, height)) {
   1.371 +            return false;
   1.372 +        }
   1.373 +        // alloc from native heap if it is a temp bitmap. (prevent GC)
   1.374 +        bool allocResult = (bitmap == decodedBitmap)
   1.375 +                               ? allocPixelRef(bitmap, NULL)
   1.376 +                               : bitmap->allocPixels();
   1.377 +        if (!allocResult) {
   1.378 +            return return_false(*decodedBitmap, "allocPixelRef");
   1.379 +        }
   1.380 +    } else {
   1.381 +        // This is also called in setDecodeConfig in above block.
   1.382 +        // i.e., when bitmap->isNull() is true.
   1.383 +        if (!chooseFromOneChoice(bitmap->config(), width, height)) {
   1.384 +            return false;
   1.385 +        }
   1.386 +    }
   1.387 +
   1.388 +    SkAutoLockPixels alp(*bitmap);
   1.389 +    WebPDecoderConfig config;
   1.390 +    if (!webp_get_config_resize_crop(&config, bitmap, rect,
   1.391 +                                     this->shouldPremultiply())) {
   1.392 +        return false;
   1.393 +    }
   1.394 +
   1.395 +    // Decode the WebP image data stream using WebP incremental decoding for
   1.396 +    // the specified cropped image-region.
   1.397 +    if (!webp_idecode(this->fInputStream, &config)) {
   1.398 +        return false;
   1.399 +    }
   1.400 +
   1.401 +    if (!directDecode) {
   1.402 +        cropBitmap(decodedBitmap, bitmap, sampleSize, region.x(), region.y(),
   1.403 +                   region.width(), region.height(), rect.x(), rect.y());
   1.404 +    }
   1.405 +    return true;
   1.406 +}
   1.407 +
   1.408 +bool SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
   1.409 +                                  Mode mode) {
   1.410 +#ifdef TIME_DECODE
   1.411 +    AutoTimeMillis atm("WEBP Decode");
   1.412 +#endif
   1.413 +
   1.414 +    int origWidth, origHeight, hasAlpha;
   1.415 +    if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) {
   1.416 +        return false;
   1.417 +    }
   1.418 +    this->fHasAlpha = hasAlpha;
   1.419 +
   1.420 +    const int sampleSize = this->getSampleSize();
   1.421 +    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
   1.422 +    if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(),
   1.423 +                         sampler.scaledHeight())) {
   1.424 +        return false;
   1.425 +    }
   1.426 +
   1.427 +    // If only bounds are requested, done
   1.428 +    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
   1.429 +        return true;
   1.430 +    }
   1.431 +
   1.432 +    if (!this->allocPixelRef(decodedBitmap, NULL)) {
   1.433 +        return return_false(*decodedBitmap, "allocPixelRef");
   1.434 +    }
   1.435 +
   1.436 +    SkAutoLockPixels alp(*decodedBitmap);
   1.437 +
   1.438 +    WebPDecoderConfig config;
   1.439 +    if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight,
   1.440 +                                this->shouldPremultiply())) {
   1.441 +        return false;
   1.442 +    }
   1.443 +
   1.444 +    // Decode the WebP image data stream using WebP incremental decoding.
   1.445 +    return webp_idecode(stream, &config);
   1.446 +}
   1.447 +
   1.448 +///////////////////////////////////////////////////////////////////////////////
   1.449 +
   1.450 +#include "SkUnPreMultiply.h"
   1.451 +
   1.452 +typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width,
   1.453 +                                 const SkPMColor* SK_RESTRICT ctable);
   1.454 +
   1.455 +static void ARGB_8888_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
   1.456 +                             const SkPMColor*) {
   1.457 +  const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
   1.458 +  for (int i = 0; i < width; ++i) {
   1.459 +      const uint32_t c = *src++;
   1.460 +      rgb[0] = SkGetPackedR32(c);
   1.461 +      rgb[1] = SkGetPackedG32(c);
   1.462 +      rgb[2] = SkGetPackedB32(c);
   1.463 +      rgb += 3;
   1.464 +  }
   1.465 +}
   1.466 +
   1.467 +static void ARGB_8888_To_RGBA(const uint8_t* in, uint8_t* rgb, int width,
   1.468 +                              const SkPMColor*) {
   1.469 +  const uint32_t* SK_RESTRICT src = (const uint32_t*)in;
   1.470 +  const SkUnPreMultiply::Scale* SK_RESTRICT table =
   1.471 +      SkUnPreMultiply::GetScaleTable();
   1.472 +  for (int i = 0; i < width; ++i) {
   1.473 +      const uint32_t c = *src++;
   1.474 +      uint8_t a = SkGetPackedA32(c);
   1.475 +      uint8_t r = SkGetPackedR32(c);
   1.476 +      uint8_t g = SkGetPackedG32(c);
   1.477 +      uint8_t b = SkGetPackedB32(c);
   1.478 +      if (0 != a && 255 != a) {
   1.479 +        SkUnPreMultiply::Scale scale = table[a];
   1.480 +        r = SkUnPreMultiply::ApplyScale(scale, r);
   1.481 +        g = SkUnPreMultiply::ApplyScale(scale, g);
   1.482 +        b = SkUnPreMultiply::ApplyScale(scale, b);
   1.483 +      }
   1.484 +      rgb[0] = r;
   1.485 +      rgb[1] = g;
   1.486 +      rgb[2] = b;
   1.487 +      rgb[3] = a;
   1.488 +      rgb += 4;
   1.489 +  }
   1.490 +}
   1.491 +
   1.492 +static void RGB_565_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
   1.493 +                           const SkPMColor*) {
   1.494 +  const uint16_t* SK_RESTRICT src = (const uint16_t*)in;
   1.495 +  for (int i = 0; i < width; ++i) {
   1.496 +      const uint16_t c = *src++;
   1.497 +      rgb[0] = SkPacked16ToR32(c);
   1.498 +      rgb[1] = SkPacked16ToG32(c);
   1.499 +      rgb[2] = SkPacked16ToB32(c);
   1.500 +      rgb += 3;
   1.501 +  }
   1.502 +}
   1.503 +
   1.504 +static void ARGB_4444_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
   1.505 +                             const SkPMColor*) {
   1.506 +  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
   1.507 +  for (int i = 0; i < width; ++i) {
   1.508 +      const SkPMColor16 c = *src++;
   1.509 +      rgb[0] = SkPacked4444ToR32(c);
   1.510 +      rgb[1] = SkPacked4444ToG32(c);
   1.511 +      rgb[2] = SkPacked4444ToB32(c);
   1.512 +      rgb += 3;
   1.513 +  }
   1.514 +}
   1.515 +
   1.516 +static void ARGB_4444_To_RGBA(const uint8_t* in, uint8_t* rgb, int width,
   1.517 +                              const SkPMColor*) {
   1.518 +  const SkPMColor16* SK_RESTRICT src = (const SkPMColor16*)in;
   1.519 +  const SkUnPreMultiply::Scale* SK_RESTRICT table =
   1.520 +      SkUnPreMultiply::GetScaleTable();
   1.521 +  for (int i = 0; i < width; ++i) {
   1.522 +      const SkPMColor16 c = *src++;
   1.523 +      uint8_t a = SkPacked4444ToA32(c);
   1.524 +      uint8_t r = SkPacked4444ToR32(c);
   1.525 +      uint8_t g = SkPacked4444ToG32(c);
   1.526 +      uint8_t b = SkPacked4444ToB32(c);
   1.527 +      if (0 != a && 255 != a) {
   1.528 +        SkUnPreMultiply::Scale scale = table[a];
   1.529 +        r = SkUnPreMultiply::ApplyScale(scale, r);
   1.530 +        g = SkUnPreMultiply::ApplyScale(scale, g);
   1.531 +        b = SkUnPreMultiply::ApplyScale(scale, b);
   1.532 +      }
   1.533 +      rgb[0] = r;
   1.534 +      rgb[1] = g;
   1.535 +      rgb[2] = b;
   1.536 +      rgb[3] = a;
   1.537 +      rgb += 4;
   1.538 +  }
   1.539 +}
   1.540 +
   1.541 +static void Index8_To_RGB(const uint8_t* in, uint8_t* rgb, int width,
   1.542 +                          const SkPMColor* SK_RESTRICT ctable) {
   1.543 +  const uint8_t* SK_RESTRICT src = (const uint8_t*)in;
   1.544 +  for (int i = 0; i < width; ++i) {
   1.545 +      const uint32_t c = ctable[*src++];
   1.546 +      rgb[0] = SkGetPackedR32(c);
   1.547 +      rgb[1] = SkGetPackedG32(c);
   1.548 +      rgb[2] = SkGetPackedB32(c);
   1.549 +      rgb += 3;
   1.550 +  }
   1.551 +}
   1.552 +
   1.553 +static ScanlineImporter ChooseImporter(const SkBitmap::Config& config,
   1.554 +                                       bool  hasAlpha,
   1.555 +                                       int*  bpp) {
   1.556 +    switch (config) {
   1.557 +        case SkBitmap::kARGB_8888_Config:
   1.558 +            if (hasAlpha) {
   1.559 +                *bpp = 4;
   1.560 +                return ARGB_8888_To_RGBA;
   1.561 +            } else {
   1.562 +                *bpp = 3;
   1.563 +                return ARGB_8888_To_RGB;
   1.564 +            }
   1.565 +        case SkBitmap::kARGB_4444_Config:
   1.566 +            if (hasAlpha) {
   1.567 +                *bpp = 4;
   1.568 +                return ARGB_4444_To_RGBA;
   1.569 +            } else {
   1.570 +                *bpp = 3;
   1.571 +                return ARGB_4444_To_RGB;
   1.572 +            }
   1.573 +        case SkBitmap::kRGB_565_Config:
   1.574 +            *bpp = 3;
   1.575 +            return RGB_565_To_RGB;
   1.576 +        case SkBitmap::kIndex8_Config:
   1.577 +            *bpp = 3;
   1.578 +            return Index8_To_RGB;
   1.579 +        default:
   1.580 +            return NULL;
   1.581 +    }
   1.582 +}
   1.583 +
   1.584 +static int stream_writer(const uint8_t* data, size_t data_size,
   1.585 +                         const WebPPicture* const picture) {
   1.586 +  SkWStream* const stream = (SkWStream*)picture->custom_ptr;
   1.587 +  return stream->write(data, data_size) ? 1 : 0;
   1.588 +}
   1.589 +
   1.590 +class SkWEBPImageEncoder : public SkImageEncoder {
   1.591 +protected:
   1.592 +    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) SK_OVERRIDE;
   1.593 +
   1.594 +private:
   1.595 +    typedef SkImageEncoder INHERITED;
   1.596 +};
   1.597 +
   1.598 +bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm,
   1.599 +                                  int quality) {
   1.600 +    const SkBitmap::Config config = bm.config();
   1.601 +    const bool hasAlpha = !bm.isOpaque();
   1.602 +    int bpp = -1;
   1.603 +    const ScanlineImporter scanline_import = ChooseImporter(config, hasAlpha,
   1.604 +                                                            &bpp);
   1.605 +    if (NULL == scanline_import) {
   1.606 +        return false;
   1.607 +    }
   1.608 +    if (-1 == bpp) {
   1.609 +        return false;
   1.610 +    }
   1.611 +
   1.612 +    SkAutoLockPixels alp(bm);
   1.613 +    SkAutoLockColors ctLocker;
   1.614 +    if (NULL == bm.getPixels()) {
   1.615 +        return false;
   1.616 +    }
   1.617 +
   1.618 +    WebPConfig webp_config;
   1.619 +    if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) {
   1.620 +        return false;
   1.621 +    }
   1.622 +
   1.623 +    WebPPicture pic;
   1.624 +    WebPPictureInit(&pic);
   1.625 +    pic.width = bm.width();
   1.626 +    pic.height = bm.height();
   1.627 +    pic.writer = stream_writer;
   1.628 +    pic.custom_ptr = (void*)stream;
   1.629 +
   1.630 +    const SkPMColor* colors = ctLocker.lockColors(bm);
   1.631 +    const uint8_t* src = (uint8_t*)bm.getPixels();
   1.632 +    const int rgbStride = pic.width * bpp;
   1.633 +
   1.634 +    // Import (for each scanline) the bit-map image (in appropriate color-space)
   1.635 +    // to RGB color space.
   1.636 +    uint8_t* rgb = new uint8_t[rgbStride * pic.height];
   1.637 +    for (int y = 0; y < pic.height; ++y) {
   1.638 +        scanline_import(src + y * bm.rowBytes(), rgb + y * rgbStride,
   1.639 +                        pic.width, colors);
   1.640 +    }
   1.641 +
   1.642 +    bool ok;
   1.643 +    if (bpp == 3) {
   1.644 +        ok = SkToBool(WebPPictureImportRGB(&pic, rgb, rgbStride));
   1.645 +    } else {
   1.646 +        ok = SkToBool(WebPPictureImportRGBA(&pic, rgb, rgbStride));
   1.647 +    }
   1.648 +    delete[] rgb;
   1.649 +
   1.650 +    ok = ok && WebPEncode(&webp_config, &pic);
   1.651 +    WebPPictureFree(&pic);
   1.652 +
   1.653 +    return ok;
   1.654 +}
   1.655 +
   1.656 +
   1.657 +///////////////////////////////////////////////////////////////////////////////
   1.658 +DEFINE_DECODER_CREATOR(WEBPImageDecoder);
   1.659 +DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
   1.660 +///////////////////////////////////////////////////////////////////////////////
   1.661 +
   1.662 +static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) {
   1.663 +    int width, height, hasAlpha;
   1.664 +    if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
   1.665 +        return NULL;
   1.666 +    }
   1.667 +
   1.668 +    // Magic matches, call decoder
   1.669 +    return SkNEW(SkWEBPImageDecoder);
   1.670 +}
   1.671 +
   1.672 +static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) {
   1.673 +    int width, height, hasAlpha;
   1.674 +    if (webp_parse_header(stream, &width, &height, &hasAlpha)) {
   1.675 +        return SkImageDecoder::kWEBP_Format;
   1.676 +    }
   1.677 +    return SkImageDecoder::kUnknown_Format;
   1.678 +}
   1.679 +
   1.680 +static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
   1.681 +      return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
   1.682 +}
   1.683 +
   1.684 +static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory);
   1.685 +static SkImageDecoder_FormatReg gFormatReg(get_format_webp);
   1.686 +static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory);

mercurial