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, nullptr, 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 #ifdef SK_HAS_HEIF_LIBRARY
86 case SkEncodedImageFormat::kHEIF:
87 #endif
88 return new SkSampledCodec(codec.release());
89 #ifdef SK_HAS_WEBP_LIBRARY
90 case SkEncodedImageFormat::kWEBP:
91 return new SkWebpAdapterCodec((SkWebpCodec*) codec.release());
92 #endif
93 #ifdef SK_CODEC_DECODES_RAW
94 case SkEncodedImageFormat::kDNG:
95 return new SkRawAdapterCodec((SkRawCodec*)codec.release());
96 #endif
97 default:
98 return nullptr;
99 }
100 }
101
NewFromData(sk_sp<SkData> data,SkPngChunkReader * chunkReader)102 SkAndroidCodec* SkAndroidCodec::NewFromData(sk_sp<SkData> data, SkPngChunkReader* chunkReader) {
103 if (!data) {
104 return nullptr;
105 }
106
107 return NewFromStream(new SkMemoryStream(data), chunkReader);
108 }
109
computeOutputColorType(SkColorType requestedColorType)110 SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
111 bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
112 switch (requestedColorType) {
113 case kARGB_4444_SkColorType:
114 return kN32_SkColorType;
115 case kN32_SkColorType:
116 break;
117 case kAlpha_8_SkColorType:
118 // Fall through to kGray_8. Before kGray_8_SkColorType existed,
119 // we allowed clients to request kAlpha_8 when they wanted a
120 // grayscale decode.
121 case kGray_8_SkColorType:
122 if (kGray_8_SkColorType == this->getInfo().colorType()) {
123 return kGray_8_SkColorType;
124 }
125 break;
126 case kRGB_565_SkColorType:
127 if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
128 return kRGB_565_SkColorType;
129 }
130 break;
131 case kRGBA_F16_SkColorType:
132 return kRGBA_F16_SkColorType;
133 default:
134 break;
135 }
136
137 // F16 is the Android default for high precision images.
138 return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType;
139 }
140
computeOutputAlphaType(bool requestedUnpremul)141 SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
142 if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
143 return kOpaque_SkAlphaType;
144 }
145 return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
146 }
147
computeOutputColorSpace(SkColorType outputColorType,sk_sp<SkColorSpace> prefColorSpace)148 sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
149 sk_sp<SkColorSpace> prefColorSpace) {
150 switch (outputColorType) {
151 case kRGBA_8888_SkColorType:
152 case kBGRA_8888_SkColorType: {
153 // If |prefColorSpace| is supported, choose it.
154 SkColorSpaceTransferFn fn;
155 if (prefColorSpace && prefColorSpace->isNumericalTransferFn(&fn)) {
156 return prefColorSpace;
157 }
158
159 SkColorSpace* encodedSpace = fCodec->getInfo().colorSpace();
160 if (encodedSpace->isNumericalTransferFn(&fn)) {
161 // Leave the pixels in the encoded color space. Color space conversion
162 // will be handled after decode time.
163 return sk_ref_sp(encodedSpace);
164 }
165
166 if (is_wide_gamut(encodedSpace)) {
167 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
168 SkColorSpace::kDCIP3_D65_Gamut);
169 }
170
171 return SkColorSpace::MakeSRGB();
172 }
173 case kRGBA_F16_SkColorType:
174 // Note that |prefColorSpace| is ignored, F16 is always linear sRGB.
175 return SkColorSpace::MakeSRGBLinear();
176 case kRGB_565_SkColorType:
177 // Note that |prefColorSpace| is ignored, 565 is always sRGB.
178 return SkColorSpace::MakeSRGB();
179 default:
180 // Color correction not supported for kGray.
181 return nullptr;
182 }
183 }
184
getSampledDimensions(int sampleSize) const185 SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
186 if (!is_valid_sample_size(sampleSize)) {
187 return {0, 0};
188 }
189
190 // Fast path for when we are not scaling.
191 if (1 == sampleSize) {
192 return fInfo.dimensions();
193 }
194
195 return this->onGetSampledDimensions(sampleSize);
196 }
197
getSupportedSubset(SkIRect * desiredSubset) const198 bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
199 if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
200 return false;
201 }
202
203 return this->onGetSupportedSubset(desiredSubset);
204 }
205
getSampledSubsetDimensions(int sampleSize,const SkIRect & subset) const206 SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
207 if (!is_valid_sample_size(sampleSize)) {
208 return {0, 0};
209 }
210
211 // We require that the input subset is a subset that is supported by SkAndroidCodec.
212 // We test this by calling getSupportedSubset() and verifying that no modifications
213 // are made to the subset.
214 SkIRect copySubset = subset;
215 if (!this->getSupportedSubset(©Subset) || copySubset != subset) {
216 return {0, 0};
217 }
218
219 // If the subset is the entire image, for consistency, use getSampledDimensions().
220 if (fInfo.dimensions() == subset.size()) {
221 return this->getSampledDimensions(sampleSize);
222 }
223
224 // This should perhaps call a virtual function, but currently both of our subclasses
225 // want the same implementation.
226 return {get_scaled_dimension(subset.width(), sampleSize),
227 get_scaled_dimension(subset.height(), sampleSize)};
228 }
229
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const AndroidOptions * options)230 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
231 size_t rowBytes, const AndroidOptions* options) {
232 if (!pixels) {
233 return SkCodec::kInvalidParameters;
234 }
235 if (rowBytes < info.minRowBytes()) {
236 return SkCodec::kInvalidParameters;
237 }
238
239 AndroidOptions defaultOptions;
240 if (!options) {
241 options = &defaultOptions;
242 } else if (options->fSubset) {
243 if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) {
244 return SkCodec::kInvalidParameters;
245 }
246
247 if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) {
248 // The caller wants the whole thing, rather than a subset. Modify
249 // the AndroidOptions passed to onGetAndroidPixels to not specify
250 // a subset.
251 defaultOptions = *options;
252 defaultOptions.fSubset = nullptr;
253 options = &defaultOptions;
254 }
255 }
256
257 return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
258 }
259
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)260 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
261 size_t rowBytes) {
262 return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
263 }
264