• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 /**
21  *  Loads the gamut as a set of three points (triangle).
22  */
load_gamut(SkPoint rgb[],const SkMatrix44 & xyz)23 static void load_gamut(SkPoint rgb[], const SkMatrix44& xyz) {
24     // rx = rX / (rX + rY + rZ)
25     // ry = rY / (rX + rY + rZ)
26     // gx, gy, bx, and gy are calulcated similarly.
27     float rSum = xyz.get(0, 0) + xyz.get(1, 0) + xyz.get(2, 0);
28     float gSum = xyz.get(0, 1) + xyz.get(1, 1) + xyz.get(2, 1);
29     float bSum = xyz.get(0, 2) + xyz.get(1, 2) + xyz.get(2, 2);
30     rgb[0].fX = xyz.get(0, 0) / rSum;
31     rgb[0].fY = xyz.get(1, 0) / rSum;
32     rgb[1].fX = xyz.get(0, 1) / gSum;
33     rgb[1].fY = xyz.get(1, 1) / gSum;
34     rgb[2].fX = xyz.get(0, 2) / bSum;
35     rgb[2].fY = xyz.get(1, 2) / bSum;
36 }
37 
38 /**
39  *  Calculates the area of the triangular gamut.
40  */
calculate_area(SkPoint abc[])41 static float calculate_area(SkPoint abc[]) {
42     SkPoint a = abc[0];
43     SkPoint b = abc[1];
44     SkPoint c = abc[2];
45     return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY);
46 }
47 
48 static const float kSRGB_D50_GamutArea = 0.084f;
49 
is_wide_gamut(const SkColorSpace * colorSpace)50 static bool is_wide_gamut(const SkColorSpace* colorSpace) {
51     // Determine if the source image has a gamut that is wider than sRGB.  If so, we
52     // will use P3 as the output color space to avoid clipping the gamut.
53     const SkMatrix44* toXYZD50 = as_CSB(colorSpace)->toXYZD50();
54     if (toXYZD50) {
55         SkPoint rgb[3];
56         load_gamut(rgb, *toXYZD50);
57         return calculate_area(rgb) > kSRGB_D50_GamutArea;
58     }
59 
60     return false;
61 }
62 
SkAndroidCodec(SkCodec * codec)63 SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
64     : fInfo(codec->getInfo())
65     , fCodec(codec)
66 {}
67 
NewFromStream(SkStream * stream,SkPngChunkReader * chunkReader)68 SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
69     std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, chunkReader));
70     if (nullptr == codec) {
71         return nullptr;
72     }
73 
74     switch ((SkEncodedImageFormat)codec->getEncodedFormat()) {
75 #ifdef SK_HAS_PNG_LIBRARY
76         case SkEncodedImageFormat::kPNG:
77         case SkEncodedImageFormat::kICO:
78 #endif
79 #ifdef SK_HAS_JPEG_LIBRARY
80         case SkEncodedImageFormat::kJPEG:
81 #endif
82         case SkEncodedImageFormat::kGIF:
83         case SkEncodedImageFormat::kBMP:
84         case SkEncodedImageFormat::kWBMP:
85             return new SkSampledCodec(codec.release());
86 #ifdef SK_HAS_WEBP_LIBRARY
87         case SkEncodedImageFormat::kWEBP:
88             return new SkWebpAdapterCodec((SkWebpCodec*) codec.release());
89 #endif
90 #ifdef SK_CODEC_DECODES_RAW
91         case SkEncodedImageFormat::kDNG:
92             return new SkRawAdapterCodec((SkRawCodec*)codec.release());
93 #endif
94         default:
95             return nullptr;
96     }
97 }
98 
NewFromData(sk_sp<SkData> data,SkPngChunkReader * chunkReader)99 SkAndroidCodec* SkAndroidCodec::NewFromData(sk_sp<SkData> data, SkPngChunkReader* chunkReader) {
100     if (!data) {
101         return nullptr;
102     }
103 
104     return NewFromStream(new SkMemoryStream(data), chunkReader);
105 }
106 
computeOutputColorType(SkColorType requestedColorType)107 SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
108     // The legacy GIF and WBMP decoders always decode to kIndex_8_SkColorType.
109     // We will maintain this behavior when we can.
110     const SkColorType suggestedColorType = this->getInfo().colorType();
111     switch ((SkEncodedImageFormat) this->getEncodedFormat()) {
112         case SkEncodedImageFormat::kGIF:
113             if (suggestedColorType == kIndex_8_SkColorType) {
114                 return kIndex_8_SkColorType;
115             }
116             break;
117         case SkEncodedImageFormat::kWBMP:
118             return kIndex_8_SkColorType;
119         default:
120             break;
121     }
122 
123     bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
124     switch (requestedColorType) {
125         case kARGB_4444_SkColorType:
126             return kN32_SkColorType;
127         case kN32_SkColorType:
128             // F16 is the Android default for high precision images.
129             return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType;
130         case kIndex_8_SkColorType:
131             if (kIndex_8_SkColorType == suggestedColorType) {
132                 return kIndex_8_SkColorType;
133             }
134             break;
135         case kAlpha_8_SkColorType:
136             // Fall through to kGray_8.  Before kGray_8_SkColorType existed,
137             // we allowed clients to request kAlpha_8 when they wanted a
138             // grayscale decode.
139         case kGray_8_SkColorType:
140             if (kGray_8_SkColorType == suggestedColorType) {
141                 return kGray_8_SkColorType;
142             }
143             break;
144         case kRGB_565_SkColorType:
145             if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
146                 return kRGB_565_SkColorType;
147             }
148             break;
149         case kRGBA_F16_SkColorType:
150             return kRGBA_F16_SkColorType;
151         default:
152             break;
153     }
154 
155     // Android has limited support for kGray_8 (using kAlpha_8).  We will not
156     // use kGray_8 for Android unless they specifically ask for it.
157     if (kGray_8_SkColorType == suggestedColorType) {
158         return kN32_SkColorType;
159     }
160 
161     // |suggestedColorType| may be kN32_SkColorType or kIndex_8_SkColorType.
162     return highPrecision ? kRGBA_F16_SkColorType : suggestedColorType;
163 }
164 
computeOutputAlphaType(bool requestedUnpremul)165 SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
166     if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
167         return kOpaque_SkAlphaType;
168     }
169     return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
170 }
171 
computeOutputColorSpace(SkColorType outputColorType,sk_sp<SkColorSpace> prefColorSpace)172 sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
173                                                             sk_sp<SkColorSpace> prefColorSpace) {
174     switch (outputColorType) {
175         case kRGBA_8888_SkColorType:
176         case kBGRA_8888_SkColorType:
177         case kIndex_8_SkColorType: {
178             // If |prefColorSpace| is supported, choose it.
179             SkColorSpaceTransferFn fn;
180             if (prefColorSpace && prefColorSpace->isNumericalTransferFn(&fn)) {
181                 return prefColorSpace;
182             }
183 
184             SkColorSpace* encodedSpace = fCodec->getInfo().colorSpace();
185             if (encodedSpace->isNumericalTransferFn(&fn)) {
186                 // Leave the pixels in the encoded color space.  Color space conversion
187                 // will be handled after decode time.
188                 return sk_ref_sp(encodedSpace);
189             }
190 
191             if (is_wide_gamut(encodedSpace)) {
192                 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
193                                              SkColorSpace::kDCIP3_D65_Gamut);
194             }
195 
196             return SkColorSpace::MakeSRGB();
197         }
198         case kRGBA_F16_SkColorType:
199             // Note that |prefColorSpace| is ignored, F16 is always linear sRGB.
200             return SkColorSpace::MakeSRGBLinear();
201         case kRGB_565_SkColorType:
202             // Note that |prefColorSpace| is ignored, 565 is always sRGB.
203             return SkColorSpace::MakeSRGB();
204         default:
205             // Color correction not supported for kGray.
206             return nullptr;
207     }
208 }
209 
getSampledDimensions(int sampleSize) const210 SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
211     if (!is_valid_sample_size(sampleSize)) {
212         return SkISize::Make(0, 0);
213     }
214 
215     // Fast path for when we are not scaling.
216     if (1 == sampleSize) {
217         return fInfo.dimensions();
218     }
219 
220     return this->onGetSampledDimensions(sampleSize);
221 }
222 
getSupportedSubset(SkIRect * desiredSubset) const223 bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
224     if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
225         return false;
226     }
227 
228     return this->onGetSupportedSubset(desiredSubset);
229 }
230 
getSampledSubsetDimensions(int sampleSize,const SkIRect & subset) const231 SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
232     if (!is_valid_sample_size(sampleSize)) {
233         return SkISize::Make(0, 0);
234     }
235 
236     // We require that the input subset is a subset that is supported by SkAndroidCodec.
237     // We test this by calling getSupportedSubset() and verifying that no modifications
238     // are made to the subset.
239     SkIRect copySubset = subset;
240     if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
241         return SkISize::Make(0, 0);
242     }
243 
244     // If the subset is the entire image, for consistency, use getSampledDimensions().
245     if (fInfo.dimensions() == subset.size()) {
246         return this->getSampledDimensions(sampleSize);
247     }
248 
249     // This should perhaps call a virtual function, but currently both of our subclasses
250     // want the same implementation.
251     return SkISize::Make(get_scaled_dimension(subset.width(), sampleSize),
252                 get_scaled_dimension(subset.height(), sampleSize));
253 }
254 
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions * options)255 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
256         size_t rowBytes, const AndroidOptions* options) {
257     if (!pixels) {
258         return SkCodec::kInvalidParameters;
259     }
260     if (rowBytes < info.minRowBytes()) {
261         return SkCodec::kInvalidParameters;
262     }
263 
264     AndroidOptions defaultOptions;
265     if (!options) {
266         options = &defaultOptions;
267     } else if (options->fSubset) {
268         if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) {
269             return SkCodec::kInvalidParameters;
270         }
271 
272         if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) {
273             // The caller wants the whole thing, rather than a subset. Modify
274             // the AndroidOptions passed to onGetAndroidPixels to not specify
275             // a subset.
276             defaultOptions = *options;
277             defaultOptions.fSubset = nullptr;
278             options = &defaultOptions;
279         }
280     }
281 
282     return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
283 }
284 
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)285 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
286         size_t rowBytes) {
287     return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
288 }
289