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 
is_valid_sample_size(int sampleSize)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 
SkAndroidCodec(SkCodec * codec)20 SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
21     : fInfo(codec->getInfo())
22     , fCodec(codec)
23 {}
24 
NewFromStream(SkStream * stream,SkPngChunkReader * chunkReader)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_HAS_PNG_LIBRARY
33         case kPNG_SkEncodedFormat:
34         case kICO_SkEncodedFormat:
35 #endif
36 #ifdef SK_HAS_JPEG_LIBRARY
37         case kJPEG_SkEncodedFormat:
38 #endif
39 #ifdef SK_HAS_GIF_LIBRARY
40         case kGIF_SkEncodedFormat:
41 #endif
42         case kBMP_SkEncodedFormat:
43         case kWBMP_SkEncodedFormat:
44             return new SkSampledCodec(codec.release());
45 #ifdef SK_HAS_WEBP_LIBRARY
46         case kWEBP_SkEncodedFormat:
47             return new SkWebpAdapterCodec((SkWebpCodec*) codec.release());
48 #endif
49 #ifdef SK_CODEC_DECODES_RAW
50         case kDNG_SkEncodedFormat:
51             return new SkRawAdapterCodec((SkRawCodec*)codec.release());
52 #endif
53         default:
54             return nullptr;
55     }
56 }
57 
NewFromData(sk_sp<SkData> data,SkPngChunkReader * chunkReader)58 SkAndroidCodec* SkAndroidCodec::NewFromData(sk_sp<SkData> data, SkPngChunkReader* chunkReader) {
59     if (!data) {
60         return nullptr;
61     }
62 
63     return NewFromStream(new SkMemoryStream(data), chunkReader);
64 }
65 
computeOutputColorType(SkColorType requestedColorType)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 
computeOutputAlphaType(bool requestedUnpremul)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 
getSampledDimensions(int sampleSize) const119 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 
getSupportedSubset(SkIRect * desiredSubset) const132 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 
getSampledSubsetDimensions(int sampleSize,const SkIRect & subset) const140 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 
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions * options)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 
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)194 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
195         size_t rowBytes) {
196     return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
197 }
198