blob: 046f0ae3c417d704c7c7ce150e2bcb8a74e30c47 [file] [log] [blame]
msarett3d9d7a72015-10-21 10:27:10 -07001/*
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"
yujieqin916de9f2016-01-25 08:26:16 -080011#include "SkRawAdapterCodec.h"
msarett3d9d7a72015-10-21 10:27:10 -070012#include "SkSampledCodec.h"
13#include "SkWebpAdapterCodec.h"
14
15static 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
Matt Sarett2ae3a2e2017-02-15 08:48:02 -050020/**
21 * Loads the gamut as a set of three points (triangle).
22 */
23static 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 */
41static 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
48static const float kSRGB_D50_GamutArea = 0.084f;
49
50static 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
msarett90c4d5f2015-12-10 13:09:24 -080063SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
64 : fInfo(codec->getInfo())
65 , fCodec(codec)
msarett3d9d7a72015-10-21 10:27:10 -070066{}
67
msarett7d5105c2015-12-02 07:02:41 -080068SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkReader) {
Ben Wagner145dbcd2016-11-03 14:40:50 -040069 std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream, chunkReader));
msarett3d9d7a72015-10-21 10:27:10 -070070 if (nullptr == codec) {
71 return nullptr;
72 }
73
Hal Canarydb683012016-11-23 08:55:18 -070074 switch ((SkEncodedImageFormat)codec->getEncodedFormat()) {
msarettad3a5c62016-05-06 07:21:26 -070075#ifdef SK_HAS_PNG_LIBRARY
Hal Canarydb683012016-11-23 08:55:18 -070076 case SkEncodedImageFormat::kPNG:
77 case SkEncodedImageFormat::kICO:
msarett39b2d5a2016-02-17 08:26:31 -080078#endif
msarettad3a5c62016-05-06 07:21:26 -070079#ifdef SK_HAS_JPEG_LIBRARY
Hal Canarydb683012016-11-23 08:55:18 -070080 case SkEncodedImageFormat::kJPEG:
msarett39b2d5a2016-02-17 08:26:31 -080081#endif
Hal Canarydb683012016-11-23 08:55:18 -070082 case SkEncodedImageFormat::kGIF:
83 case SkEncodedImageFormat::kBMP:
84 case SkEncodedImageFormat::kWBMP:
mtklein18300a32016-03-16 13:53:35 -070085 return new SkSampledCodec(codec.release());
msarettad3a5c62016-05-06 07:21:26 -070086#ifdef SK_HAS_WEBP_LIBRARY
Hal Canarydb683012016-11-23 08:55:18 -070087 case SkEncodedImageFormat::kWEBP:
mtklein18300a32016-03-16 13:53:35 -070088 return new SkWebpAdapterCodec((SkWebpCodec*) codec.release());
msarett39b2d5a2016-02-17 08:26:31 -080089#endif
yujieqin916de9f2016-01-25 08:26:16 -080090#ifdef SK_CODEC_DECODES_RAW
Hal Canarydb683012016-11-23 08:55:18 -070091 case SkEncodedImageFormat::kDNG:
mtklein18300a32016-03-16 13:53:35 -070092 return new SkRawAdapterCodec((SkRawCodec*)codec.release());
yujieqin916de9f2016-01-25 08:26:16 -080093#endif
msarett3d9d7a72015-10-21 10:27:10 -070094 default:
msarett3d9d7a72015-10-21 10:27:10 -070095 return nullptr;
96 }
97}
98
reed42943c82016-09-12 12:01:44 -070099SkAndroidCodec* SkAndroidCodec::NewFromData(sk_sp<SkData> data, SkPngChunkReader* chunkReader) {
msarett3d9d7a72015-10-21 10:27:10 -0700100 if (!data) {
101 return nullptr;
102 }
103
msarett7d5105c2015-12-02 07:02:41 -0800104 return NewFromStream(new SkMemoryStream(data), chunkReader);
msarett3d9d7a72015-10-21 10:27:10 -0700105}
106
msarett9a0e3462015-12-11 07:38:50 -0800107SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
Matt Sarett8dcc84f2016-12-14 10:23:41 -0500108 bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
msarett9a0e3462015-12-11 07:38:50 -0800109 switch (requestedColorType) {
110 case kARGB_4444_SkColorType:
msarett9a0e3462015-12-11 07:38:50 -0800111 return kN32_SkColorType;
Matt Sarett8dcc84f2016-12-14 10:23:41 -0500112 case kN32_SkColorType:
Mike Reed58050132017-07-12 22:10:29 -0400113#ifdef SK_SUPPORT_LEGACY_INDEX_8_COLORTYPE
msarett9a0e3462015-12-11 07:38:50 -0800114 case kIndex_8_SkColorType:
Mike Reed58050132017-07-12 22:10:29 -0400115#endif
msarett9a0e3462015-12-11 07:38:50 -0800116 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:
Matt Sarett8db74f12017-06-14 13:02:05 +0000122 if (kGray_8_SkColorType == this->getInfo().colorType()) {
msarett9a0e3462015-12-11 07:38:50 -0800123 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;
Matt Sarett8dcc84f2016-12-14 10:23:41 -0500131 case kRGBA_F16_SkColorType:
132 return kRGBA_F16_SkColorType;
msarett9a0e3462015-12-11 07:38:50 -0800133 default:
134 break;
135 }
136
Matt Sarett8db74f12017-06-14 13:02:05 +0000137 // F16 is the Android default for high precision images.
138 return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType;
msarett9a0e3462015-12-11 07:38:50 -0800139}
140
141SkAlphaType 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
Matt Sarett68feef42017-04-11 09:51:32 -0400148sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
149 sk_sp<SkColorSpace> prefColorSpace) {
Matt Sarett966bb342016-12-12 16:30:13 -0500150 switch (outputColorType) {
Mike Reed58050132017-07-12 22:10:29 -0400151#ifdef SK_SUPPORT_LEGACY_INDEX_8_COLORTYPE
Leon Scroggins IIIc20b5f82017-07-13 08:05:29 -0400152 case kIndex_8_SkColorType:
153#endif
154 case kRGBA_8888_SkColorType:
155 case kBGRA_8888_SkColorType: {
Matt Sarett68feef42017-04-11 09:51:32 -0400156 // If |prefColorSpace| is supported, choose it.
Matt Sarett9341c982017-02-28 15:36:42 -0500157 SkColorSpaceTransferFn fn;
Matt Sarett68feef42017-04-11 09:51:32 -0400158 if (prefColorSpace && prefColorSpace->isNumericalTransferFn(&fn)) {
159 return prefColorSpace;
160 }
161
162 SkColorSpace* encodedSpace = fCodec->getInfo().colorSpace();
Matt Sarett9341c982017-02-28 15:36:42 -0500163 if (encodedSpace->isNumericalTransferFn(&fn)) {
164 // Leave the pixels in the encoded color space. Color space conversion
165 // will be handled after decode time.
Matt Sarettcf3f2342017-03-23 15:32:25 -0400166 return sk_ref_sp(encodedSpace);
Matt Sarett9341c982017-02-28 15:36:42 -0500167 }
168
169 if (is_wide_gamut(encodedSpace)) {
Matt Sarett2ae3a2e2017-02-15 08:48:02 -0500170 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
Matt Sarettcf3f2342017-03-23 15:32:25 -0400171 SkColorSpace::kDCIP3_D65_Gamut);
Matt Sarett2ae3a2e2017-02-15 08:48:02 -0500172 }
173
Matt Sarettcf3f2342017-03-23 15:32:25 -0400174 return SkColorSpace::MakeSRGB();
Matt Sarett9341c982017-02-28 15:36:42 -0500175 }
Matt Sarett966bb342016-12-12 16:30:13 -0500176 case kRGBA_F16_SkColorType:
Matt Sarett68feef42017-04-11 09:51:32 -0400177 // Note that |prefColorSpace| is ignored, F16 is always linear sRGB.
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500178 return SkColorSpace::MakeSRGBLinear();
Matt Sarett3725f0a2017-03-28 14:34:20 -0400179 case kRGB_565_SkColorType:
Matt Sarett68feef42017-04-11 09:51:32 -0400180 // Note that |prefColorSpace| is ignored, 565 is always sRGB.
Matt Sarett3725f0a2017-03-28 14:34:20 -0400181 return SkColorSpace::MakeSRGB();
Matt Sarett966bb342016-12-12 16:30:13 -0500182 default:
Matt Sarett3725f0a2017-03-28 14:34:20 -0400183 // Color correction not supported for kGray.
Matt Sarett966bb342016-12-12 16:30:13 -0500184 return nullptr;
185 }
186}
187
msarett3d9d7a72015-10-21 10:27:10 -0700188SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
189 if (!is_valid_sample_size(sampleSize)) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400190 return {0, 0};
msarett3d9d7a72015-10-21 10:27:10 -0700191 }
192
scroggo501b7342015-11-03 07:55:11 -0800193 // Fast path for when we are not scaling.
194 if (1 == sampleSize) {
195 return fInfo.dimensions();
196 }
197
msarett3d9d7a72015-10-21 10:27:10 -0700198 return this->onGetSampledDimensions(sampleSize);
199}
200
201bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
202 if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
203 return false;
204 }
205
206 return this->onGetSupportedSubset(desiredSubset);
207}
208
209SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
210 if (!is_valid_sample_size(sampleSize)) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400211 return {0, 0};
msarett3d9d7a72015-10-21 10:27:10 -0700212 }
213
214 // We require that the input subset is a subset that is supported by SkAndroidCodec.
215 // We test this by calling getSupportedSubset() and verifying that no modifications
216 // are made to the subset.
217 SkIRect copySubset = subset;
218 if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400219 return {0, 0};
msarett3d9d7a72015-10-21 10:27:10 -0700220 }
221
scroggo501b7342015-11-03 07:55:11 -0800222 // If the subset is the entire image, for consistency, use getSampledDimensions().
msarett3d9d7a72015-10-21 10:27:10 -0700223 if (fInfo.dimensions() == subset.size()) {
scroggo501b7342015-11-03 07:55:11 -0800224 return this->getSampledDimensions(sampleSize);
msarett3d9d7a72015-10-21 10:27:10 -0700225 }
226
227 // This should perhaps call a virtual function, but currently both of our subclasses
228 // want the same implementation.
Hal Canaryfafe1352017-04-11 12:12:02 -0400229 return {get_scaled_dimension(subset.width(), sampleSize),
230 get_scaled_dimension(subset.height(), sampleSize)};
msarett3d9d7a72015-10-21 10:27:10 -0700231}
232
233SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
scroggoe95a0682015-11-04 04:31:12 -0800234 size_t rowBytes, const AndroidOptions* options) {
msarett3d9d7a72015-10-21 10:27:10 -0700235 if (!pixels) {
236 return SkCodec::kInvalidParameters;
237 }
238 if (rowBytes < info.minRowBytes()) {
239 return SkCodec::kInvalidParameters;
240 }
241
242 AndroidOptions defaultOptions;
243 if (!options) {
244 options = &defaultOptions;
245 } else if (options->fSubset) {
246 if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) {
247 return SkCodec::kInvalidParameters;
248 }
scroggo501b7342015-11-03 07:55:11 -0800249
250 if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) {
251 // The caller wants the whole thing, rather than a subset. Modify
252 // the AndroidOptions passed to onGetAndroidPixels to not specify
253 // a subset.
254 defaultOptions = *options;
255 defaultOptions.fSubset = nullptr;
256 options = &defaultOptions;
257 }
msarett3d9d7a72015-10-21 10:27:10 -0700258 }
259
260 return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
261}
262
263SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
264 size_t rowBytes) {
265 return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
266}