Home | History | Annotate | Download | only in codec
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkAndroidCodec.h"
      9 #include "SkCodec.h"
     10 #include "SkCodecPriv.h"
     11 #include "SkRawAdapterCodec.h"
     12 #include "SkSampledCodec.h"
     13 #include "SkWebpAdapterCodec.h"
     14 
     15 static bool is_valid_sample_size(int sampleSize) {
     16     // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize?
     17     return sampleSize > 0;
     18 }
     19 
     20 SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
     21     : fInfo(codec->getInfo())
     22     , fCodec(codec)
     23 {}
     24 
     25 SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
     26     SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream, chunkReader));
     27     if (nullptr == codec) {
     28         return nullptr;
     29     }
     30 
     31     switch (codec->getEncodedFormat()) {
     32 #ifdef SK_CODEC_DECODES_PNG
     33         case kPNG_SkEncodedFormat:
     34         case kICO_SkEncodedFormat:
     35 #endif
     36 #ifdef SK_CODEC_DECODES_JPEG
     37         case kJPEG_SkEncodedFormat:
     38 #endif
     39 #ifdef SK_CODEC_DECODES_GIF
     40         case kGIF_SkEncodedFormat:
     41 #endif
     42         case kBMP_SkEncodedFormat:
     43         case kWBMP_SkEncodedFormat:
     44             return new SkSampledCodec(codec.detach());
     45 #ifdef SK_CODEC_DECODES_WEBP
     46         case kWEBP_SkEncodedFormat:
     47             return new SkWebpAdapterCodec((SkWebpCodec*) codec.detach());
     48 #endif
     49 #ifdef SK_CODEC_DECODES_RAW
     50         case kRAW_SkEncodedFormat:
     51             return new SkRawAdapterCodec((SkRawCodec*)codec.detach());
     52 #endif
     53         default:
     54             return nullptr;
     55     }
     56 }
     57 
     58 SkAndroidCodec* SkAndroidCodec::NewFromData(SkData* data, SkPngChunkReader* chunkReader) {
     59     if (!data) {
     60         return nullptr;
     61     }
     62 
     63     return NewFromStream(new SkMemoryStream(data), chunkReader);
     64 }
     65 
     66 SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
     67     // The legacy GIF and WBMP decoders always decode to kIndex_8_SkColorType.
     68     // We will maintain this behavior.
     69     SkEncodedFormat format = this->getEncodedFormat();
     70     if (kGIF_SkEncodedFormat == format || kWBMP_SkEncodedFormat == format) {
     71         return kIndex_8_SkColorType;
     72     }
     73 
     74     SkColorType suggestedColorType = this->getInfo().colorType();
     75     switch (requestedColorType) {
     76         case kARGB_4444_SkColorType:
     77         case kN32_SkColorType:
     78             return kN32_SkColorType;
     79         case kIndex_8_SkColorType:
     80             if (kIndex_8_SkColorType == suggestedColorType) {
     81                 return kIndex_8_SkColorType;
     82             }
     83             break;
     84         case kAlpha_8_SkColorType:
     85             // Fall through to kGray_8.  Before kGray_8_SkColorType existed,
     86             // we allowed clients to request kAlpha_8 when they wanted a
     87             // grayscale decode.
     88         case kGray_8_SkColorType:
     89             if (kGray_8_SkColorType == suggestedColorType) {
     90                 return kGray_8_SkColorType;
     91             }
     92             break;
     93         case kRGB_565_SkColorType:
     94             if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
     95                 return kRGB_565_SkColorType;
     96             }
     97             break;
     98         default:
     99             break;
    100     }
    101 
    102     // Android has limited support for kGray_8 (using kAlpha_8).  We will not
    103     // use kGray_8 for Android unless they specifically ask for it.
    104     if (kGray_8_SkColorType == suggestedColorType) {
    105         return kN32_SkColorType;
    106     }
    107 
    108     // This may be kN32_SkColorType or kIndex_8_SkColorType.
    109     return suggestedColorType;
    110 }
    111 
    112 SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
    113     if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
    114         return kOpaque_SkAlphaType;
    115     }
    116     return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
    117 }
    118 
    119 SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
    120     if (!is_valid_sample_size(sampleSize)) {
    121         return SkISize::Make(0, 0);
    122     }
    123 
    124     // Fast path for when we are not scaling.
    125     if (1 == sampleSize) {
    126         return fInfo.dimensions();
    127     }
    128 
    129     return this->onGetSampledDimensions(sampleSize);
    130 }
    131 
    132 bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
    133     if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
    134         return false;
    135     }
    136 
    137     return this->onGetSupportedSubset(desiredSubset);
    138 }
    139 
    140 SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
    141     if (!is_valid_sample_size(sampleSize)) {
    142         return SkISize::Make(0, 0);
    143     }
    144 
    145     // We require that the input subset is a subset that is supported by SkAndroidCodec.
    146     // We test this by calling getSupportedSubset() and verifying that no modifications
    147     // are made to the subset.
    148     SkIRect copySubset = subset;
    149     if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
    150         return SkISize::Make(0, 0);
    151     }
    152 
    153     // If the subset is the entire image, for consistency, use getSampledDimensions().
    154     if (fInfo.dimensions() == subset.size()) {
    155         return this->getSampledDimensions(sampleSize);
    156     }
    157 
    158     // This should perhaps call a virtual function, but currently both of our subclasses
    159     // want the same implementation.
    160     return SkISize::Make(get_scaled_dimension(subset.width(), sampleSize),
    161                 get_scaled_dimension(subset.height(), sampleSize));
    162 }
    163 
    164 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
    165         size_t rowBytes, const AndroidOptions* options) {
    166     if (!pixels) {
    167         return SkCodec::kInvalidParameters;
    168     }
    169     if (rowBytes < info.minRowBytes()) {
    170         return SkCodec::kInvalidParameters;
    171     }
    172 
    173     AndroidOptions defaultOptions;
    174     if (!options) {
    175         options = &defaultOptions;
    176     } else if (options->fSubset) {
    177         if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) {
    178             return SkCodec::kInvalidParameters;
    179         }
    180 
    181         if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) {
    182             // The caller wants the whole thing, rather than a subset. Modify
    183             // the AndroidOptions passed to onGetAndroidPixels to not specify
    184             // a subset.
    185             defaultOptions = *options;
    186             defaultOptions.fSubset = nullptr;
    187             options = &defaultOptions;
    188         }
    189     }
    190 
    191     return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
    192 }
    193 
    194 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
    195         size_t rowBytes) {
    196     return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
    197 }
    198