| /* |
| * 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 "SkImageEncoderPriv.h" |
| |
| #ifdef SK_HAS_WEBP_LIBRARY |
| |
| #include "SkBitmap.h" |
| #include "SkColorPriv.h" |
| #include "SkImageEncoderFns.h" |
| #include "SkStream.h" |
| #include "SkTemplates.h" |
| #include "SkUnPreMultiply.h" |
| #include "SkUtils.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/encode.h" |
| } |
| |
| static transform_scanline_proc choose_proc(const SkImageInfo& info) { |
| const bool isGammaEncoded = info.gammaCloseToSRGB(); |
| switch (info.colorType()) { |
| case kRGBA_8888_SkColorType: |
| switch (info.alphaType()) { |
| case kOpaque_SkAlphaType: |
| return transform_scanline_RGBX; |
| case kUnpremul_SkAlphaType: |
| return transform_scanline_memcpy; |
| case kPremul_SkAlphaType: |
| return isGammaEncoded ? transform_scanline_srgbA : |
| transform_scanline_rgbA; |
| default: |
| return nullptr; |
| } |
| case kBGRA_8888_SkColorType: |
| switch (info.alphaType()) { |
| case kOpaque_SkAlphaType: |
| return transform_scanline_BGRX; |
| case kUnpremul_SkAlphaType: |
| return transform_scanline_BGRA; |
| case kPremul_SkAlphaType: |
| return isGammaEncoded ? transform_scanline_sbgrA : |
| transform_scanline_bgrA; |
| default: |
| return nullptr; |
| } |
| case kRGB_565_SkColorType: |
| if (!info.isOpaque()) { |
| return nullptr; |
| } |
| |
| return transform_scanline_565; |
| case kARGB_4444_SkColorType: |
| switch (info.alphaType()) { |
| case kOpaque_SkAlphaType: |
| return transform_scanline_444; |
| case kPremul_SkAlphaType: |
| return transform_scanline_4444; |
| default: |
| return nullptr; |
| } |
| case kIndex_8_SkColorType: |
| switch (info.alphaType()) { |
| case kOpaque_SkAlphaType: |
| return transform_scanline_index8_opaque; |
| case kUnpremul_SkAlphaType: |
| case kPremul_SkAlphaType: |
| // If the color table is premultiplied, we'll fix it before calling the |
| // scanline proc. |
| return transform_scanline_index8_unpremul; |
| default: |
| return nullptr; |
| } |
| case kGray_8_SkColorType: |
| return transform_scanline_gray; |
| case kRGBA_F16_SkColorType: |
| if (!info.colorSpace() || !info.colorSpace()->gammaIsLinear()) { |
| return nullptr; |
| } |
| |
| switch (info.alphaType()) { |
| case kOpaque_SkAlphaType: |
| case kUnpremul_SkAlphaType: |
| return transform_scanline_F16_to_8888; |
| case kPremul_SkAlphaType: |
| return transform_scanline_F16_premul_to_8888; |
| default: |
| return nullptr; |
| } |
| default: |
| return nullptr; |
| } |
| } |
| |
| 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; |
| } |
| |
| static bool do_encode(SkWStream* stream, const SkPixmap& srcPixmap, const SkEncodeOptions& opts, |
| int quality) { |
| SkASSERT(!srcPixmap.colorSpace() || srcPixmap.colorSpace()->gammaCloseToSRGB() || |
| srcPixmap.colorSpace()->gammaIsLinear()); |
| |
| SkPixmap pixmap = srcPixmap; |
| if (SkEncodeOptions::ColorBehavior::kLegacy == opts.fColorBehavior) { |
| pixmap.setColorSpace(nullptr); |
| } else { |
| if (!pixmap.colorSpace()) { |
| return false; |
| } |
| } |
| |
| const transform_scanline_proc proc = choose_proc(pixmap.info()); |
| if (!proc) { |
| return false; |
| } |
| |
| int bpp; |
| if (kRGBA_F16_SkColorType == pixmap.colorType()) { |
| bpp = 4; |
| } else { |
| bpp = pixmap.isOpaque() ? 3 : 4; |
| } |
| |
| if (nullptr == pixmap.addr()) { |
| return false; |
| } |
| |
| const SkPMColor* colors = nullptr; |
| SkPMColor storage[256]; |
| if (kIndex_8_SkColorType == pixmap.colorType()) { |
| if (!pixmap.ctable()) { |
| return false; |
| } |
| |
| colors = pixmap.ctable()->readColors(); |
| if (kPremul_SkAlphaType == pixmap.alphaType()) { |
| // Unpremultiply the colors. |
| const SkImageInfo rgbaInfo = pixmap.info().makeColorType(kRGBA_8888_SkColorType); |
| transform_scanline_proc proc = choose_proc(rgbaInfo); |
| proc((char*) storage, (const char*) colors, pixmap.ctable()->count(), 4, nullptr); |
| colors = storage; |
| } |
| } |
| |
| WebPConfig webp_config; |
| if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, (float) quality)) { |
| return false; |
| } |
| |
| WebPPicture pic; |
| WebPPictureInit(&pic); |
| pic.width = pixmap.width(); |
| pic.height = pixmap.height(); |
| pic.writer = stream_writer; |
| pic.custom_ptr = (void*)stream; |
| |
| const uint8_t* src = (uint8_t*)pixmap.addr(); |
| const int rgbStride = pic.width * bpp; |
| const size_t rowBytes = pixmap.rowBytes(); |
| |
| // Import (for each scanline) the bit-map image (in appropriate color-space) |
| // to RGB color space. |
| std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]); |
| for (int y = 0; y < pic.height; ++y) { |
| proc((char*) &rgb[y * rgbStride], (const char*) &src[y * rowBytes], pic.width, bpp, colors); |
| } |
| |
| bool ok; |
| if (bpp == 3) { |
| ok = SkToBool(WebPPictureImportRGB(&pic, &rgb[0], rgbStride)); |
| } else { |
| if (pixmap.isOpaque()) { |
| ok = SkToBool(WebPPictureImportRGBX(&pic, &rgb[0], rgbStride)); |
| } else { |
| ok = SkToBool(WebPPictureImportRGBA(&pic, &rgb[0], rgbStride)); |
| } |
| } |
| |
| ok = ok && WebPEncode(&webp_config, &pic); |
| WebPPictureFree(&pic); |
| |
| return ok; |
| } |
| |
| bool SkEncodeImageAsWEBP(SkWStream* stream, const SkPixmap& src, int quality) { |
| return do_encode(stream, src, SkEncodeOptions(), quality); |
| } |
| |
| bool SkEncodeImageAsWEBP(SkWStream* stream, const SkPixmap& src, const SkEncodeOptions& opts) { |
| return do_encode(stream, src, opts, 100); |
| } |
| |
| #endif |