Home | History | Annotate | Download | only in images
      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 "SkImageEncoderPriv.h"
     18 
     19 #ifdef SK_HAS_WEBP_LIBRARY
     20 
     21 #include "SkBitmap.h"
     22 #include "SkColorData.h"
     23 #include "SkImageEncoderFns.h"
     24 #include "SkStream.h"
     25 #include "SkTemplates.h"
     26 #include "SkUnPreMultiply.h"
     27 #include "SkUTF.h"
     28 #include "SkWebpEncoder.h"
     29 
     30 // A WebP encoder only, on top of (subset of) libwebp
     31 // For more information on WebP image format, and libwebp library, see:
     32 //   http://code.google.com/speed/webp/
     33 //   http://www.webmproject.org/code/#libwebp_webp_image_decoder_library
     34 //   http://review.webmproject.org/gitweb?p=libwebp.git
     35 
     36 #include <stdio.h>
     37 extern "C" {
     38 // If moving libwebp out of skia source tree, path for webp headers must be
     39 // updated accordingly. Here, we enforce using local copy in webp sub-directory.
     40 #include "webp/encode.h"
     41 #include "webp/mux.h"
     42 }
     43 
     44 static transform_scanline_proc choose_proc(const SkImageInfo& info) {
     45     switch (info.colorType()) {
     46         case kRGBA_8888_SkColorType:
     47             switch (info.alphaType()) {
     48                 case kOpaque_SkAlphaType:
     49                     return transform_scanline_RGBX;
     50                 case kUnpremul_SkAlphaType:
     51                     return transform_scanline_memcpy;
     52                 case kPremul_SkAlphaType:
     53                     return transform_scanline_rgbA;
     54                 default:
     55                     return nullptr;
     56             }
     57         case kBGRA_8888_SkColorType:
     58             switch (info.alphaType()) {
     59                 case kOpaque_SkAlphaType:
     60                     return transform_scanline_BGRX;
     61                 case kUnpremul_SkAlphaType:
     62                     return transform_scanline_BGRA;
     63                 case kPremul_SkAlphaType:
     64                     return transform_scanline_bgrA;
     65                 default:
     66                     return nullptr;
     67             }
     68         case kRGB_565_SkColorType:
     69             if (!info.isOpaque()) {
     70                 return nullptr;
     71             }
     72 
     73             return transform_scanline_565;
     74         case kARGB_4444_SkColorType:
     75             switch (info.alphaType()) {
     76                 case kOpaque_SkAlphaType:
     77                     return transform_scanline_444;
     78                 case kPremul_SkAlphaType:
     79                     return transform_scanline_4444;
     80                 default:
     81                     return nullptr;
     82             }
     83         case kGray_8_SkColorType:
     84             return transform_scanline_gray;
     85         case kRGBA_F16_SkColorType:
     86             switch (info.alphaType()) {
     87                 case kOpaque_SkAlphaType:
     88                 case kUnpremul_SkAlphaType:
     89                     return transform_scanline_F16_to_8888;
     90                 case kPremul_SkAlphaType:
     91                     return transform_scanline_F16_premul_to_8888;
     92                 default:
     93                     return nullptr;
     94             }
     95         default:
     96             return nullptr;
     97     }
     98 }
     99 
    100 static int stream_writer(const uint8_t* data, size_t data_size,
    101                          const WebPPicture* const picture) {
    102   SkWStream* const stream = (SkWStream*)picture->custom_ptr;
    103   return stream->write(data, data_size) ? 1 : 0;
    104 }
    105 
    106 bool SkWebpEncoder::Encode(SkWStream* stream, const SkPixmap& pixmap, const Options& opts) {
    107     if (!SkPixmapIsValid(pixmap)) {
    108         return false;
    109     }
    110 
    111     const transform_scanline_proc proc = choose_proc(pixmap.info());
    112     if (!proc) {
    113         return false;
    114     }
    115 
    116     int bpp;
    117     if (kRGBA_F16_SkColorType == pixmap.colorType()) {
    118         bpp = 4;
    119     } else {
    120         bpp = pixmap.isOpaque() ? 3 : 4;
    121     }
    122 
    123     if (nullptr == pixmap.addr()) {
    124         return false;
    125     }
    126 
    127     WebPConfig webp_config;
    128     if (!WebPConfigPreset(&webp_config, WEBP_PRESET_DEFAULT, opts.fQuality)) {
    129         return false;
    130     }
    131 
    132     WebPPicture pic;
    133     WebPPictureInit(&pic);
    134     SkAutoTCallVProc<WebPPicture, WebPPictureFree> autoPic(&pic);
    135     pic.width = pixmap.width();
    136     pic.height = pixmap.height();
    137     pic.writer = stream_writer;
    138 
    139     // Set compression, method, and pixel format.
    140     // libwebp recommends using BGRA for lossless and YUV for lossy.
    141     // The choices of |webp_config.method| currently just match Chrome's defaults.  We
    142     // could potentially expose this decision to the client.
    143     if (Compression::kLossy == opts.fCompression) {
    144         webp_config.lossless = 0;
    145 #ifndef SK_WEBP_ENCODER_USE_DEFAULT_METHOD
    146         webp_config.method = 3;
    147 #endif
    148         pic.use_argb = 0;
    149     } else {
    150         webp_config.lossless = 1;
    151         webp_config.method = 0;
    152         pic.use_argb = 1;
    153     }
    154 
    155     // If there is no need to embed an ICC profile, we write directly to the input stream.
    156     // Otherwise, we will first encode to |tmp| and use a mux to add the ICC chunk.  libwebp
    157     // forces us to have an encoded image before we can add a profile.
    158     sk_sp<SkData> icc = icc_from_color_space(pixmap.info());
    159     SkDynamicMemoryWStream tmp;
    160     pic.custom_ptr = icc ? (void*)&tmp : (void*)stream;
    161 
    162     const uint8_t* src = (uint8_t*)pixmap.addr();
    163     const int rgbStride = pic.width * bpp;
    164     const size_t rowBytes = pixmap.rowBytes();
    165 
    166     // Import (for each scanline) the bit-map image (in appropriate color-space)
    167     // to RGB color space.
    168     std::unique_ptr<uint8_t[]> rgb(new uint8_t[rgbStride * pic.height]);
    169     for (int y = 0; y < pic.height; ++y) {
    170         proc((char*) &rgb[y * rgbStride],
    171              (const char*) &src[y * rowBytes],
    172              pic.width,
    173              bpp);
    174     }
    175 
    176     auto importProc = WebPPictureImportRGB;
    177     if (3 != bpp) {
    178         if (pixmap.isOpaque()) {
    179             importProc = WebPPictureImportRGBX;
    180         } else {
    181             importProc = WebPPictureImportRGBA;
    182         }
    183     }
    184 
    185     if (!importProc(&pic, &rgb[0], rgbStride)) {
    186         return false;
    187     }
    188 
    189     if (!WebPEncode(&webp_config, &pic)) {
    190         return false;
    191     }
    192 
    193     if (icc) {
    194         sk_sp<SkData> encodedData = tmp.detachAsData();
    195         WebPData encoded = { encodedData->bytes(), encodedData->size() };
    196         WebPData iccChunk = { icc->bytes(), icc->size() };
    197 
    198         SkAutoTCallVProc<WebPMux, WebPMuxDelete> mux(WebPMuxNew());
    199         if (WEBP_MUX_OK != WebPMuxSetImage(mux, &encoded, 0)) {
    200             return false;
    201         }
    202 
    203         if (WEBP_MUX_OK != WebPMuxSetChunk(mux, "ICCP", &iccChunk, 0)) {
    204             return false;
    205         }
    206 
    207         WebPData assembled;
    208         if (WEBP_MUX_OK != WebPMuxAssemble(mux, &assembled)) {
    209             return false;
    210         }
    211 
    212         stream->write(assembled.bytes, assembled.size);
    213         WebPDataClear(&assembled);
    214     }
    215 
    216     return true;
    217 }
    218 
    219 #endif
    220