blob: 3a573521134404198ceeae9fe22fcd931febb7f3 [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"
Mike Reedede7bac2017-07-23 15:30:02 -040011#include "SkMakeUnique.h"
yujieqin916de9f2016-01-25 08:26:16 -080012#include "SkRawAdapterCodec.h"
msarett3d9d7a72015-10-21 10:27:10 -070013#include "SkSampledCodec.h"
14#include "SkWebpAdapterCodec.h"
15
16static bool is_valid_sample_size(int sampleSize) {
17 // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize?
18 return sampleSize > 0;
19}
20
Matt Sarett2ae3a2e2017-02-15 08:48:02 -050021/**
22 * Loads the gamut as a set of three points (triangle).
23 */
24static void load_gamut(SkPoint rgb[], const SkMatrix44& xyz) {
25 // rx = rX / (rX + rY + rZ)
26 // ry = rY / (rX + rY + rZ)
27 // gx, gy, bx, and gy are calulcated similarly.
28 float rSum = xyz.get(0, 0) + xyz.get(1, 0) + xyz.get(2, 0);
29 float gSum = xyz.get(0, 1) + xyz.get(1, 1) + xyz.get(2, 1);
30 float bSum = xyz.get(0, 2) + xyz.get(1, 2) + xyz.get(2, 2);
31 rgb[0].fX = xyz.get(0, 0) / rSum;
32 rgb[0].fY = xyz.get(1, 0) / rSum;
33 rgb[1].fX = xyz.get(0, 1) / gSum;
34 rgb[1].fY = xyz.get(1, 1) / gSum;
35 rgb[2].fX = xyz.get(0, 2) / bSum;
36 rgb[2].fY = xyz.get(1, 2) / bSum;
37}
38
39/**
40 * Calculates the area of the triangular gamut.
41 */
42static float calculate_area(SkPoint abc[]) {
43 SkPoint a = abc[0];
44 SkPoint b = abc[1];
45 SkPoint c = abc[2];
46 return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY);
47}
48
49static const float kSRGB_D50_GamutArea = 0.084f;
50
51static bool is_wide_gamut(const SkColorSpace* colorSpace) {
52 // Determine if the source image has a gamut that is wider than sRGB. If so, we
53 // will use P3 as the output color space to avoid clipping the gamut.
54 const SkMatrix44* toXYZD50 = as_CSB(colorSpace)->toXYZD50();
55 if (toXYZD50) {
56 SkPoint rgb[3];
57 load_gamut(rgb, *toXYZD50);
58 return calculate_area(rgb) > kSRGB_D50_GamutArea;
59 }
60
61 return false;
62}
63
msarett90c4d5f2015-12-10 13:09:24 -080064SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
65 : fInfo(codec->getInfo())
66 , fCodec(codec)
msarett3d9d7a72015-10-21 10:27:10 -070067{}
68
Mike Reedede7bac2017-07-23 15:30:02 -040069std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<SkStream> stream, SkPngChunkReader* chunkReader) {
70 auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader);
msarett3d9d7a72015-10-21 10:27:10 -070071 if (nullptr == codec) {
72 return nullptr;
73 }
74
Hal Canarydb683012016-11-23 08:55:18 -070075 switch ((SkEncodedImageFormat)codec->getEncodedFormat()) {
msarettad3a5c62016-05-06 07:21:26 -070076#ifdef SK_HAS_PNG_LIBRARY
Hal Canarydb683012016-11-23 08:55:18 -070077 case SkEncodedImageFormat::kPNG:
78 case SkEncodedImageFormat::kICO:
msarett39b2d5a2016-02-17 08:26:31 -080079#endif
msarettad3a5c62016-05-06 07:21:26 -070080#ifdef SK_HAS_JPEG_LIBRARY
Hal Canarydb683012016-11-23 08:55:18 -070081 case SkEncodedImageFormat::kJPEG:
msarett39b2d5a2016-02-17 08:26:31 -080082#endif
Hal Canarydb683012016-11-23 08:55:18 -070083 case SkEncodedImageFormat::kGIF:
84 case SkEncodedImageFormat::kBMP:
85 case SkEncodedImageFormat::kWBMP:
Mike Reedede7bac2017-07-23 15:30:02 -040086 return skstd::make_unique<SkSampledCodec>(codec.release());
msarettad3a5c62016-05-06 07:21:26 -070087#ifdef SK_HAS_WEBP_LIBRARY
Hal Canarydb683012016-11-23 08:55:18 -070088 case SkEncodedImageFormat::kWEBP:
Mike Reedede7bac2017-07-23 15:30:02 -040089 return skstd::make_unique<SkWebpAdapterCodec>((SkWebpCodec*) codec.release());
msarett39b2d5a2016-02-17 08:26:31 -080090#endif
yujieqin916de9f2016-01-25 08:26:16 -080091#ifdef SK_CODEC_DECODES_RAW
Hal Canarydb683012016-11-23 08:55:18 -070092 case SkEncodedImageFormat::kDNG:
Mike Reedede7bac2017-07-23 15:30:02 -040093 return skstd::make_unique<SkRawAdapterCodec>((SkRawCodec*)codec.release());
yujieqin916de9f2016-01-25 08:26:16 -080094#endif
msarett3d9d7a72015-10-21 10:27:10 -070095 default:
msarett3d9d7a72015-10-21 10:27:10 -070096 return nullptr;
97 }
98}
99
Mike Reedede7bac2017-07-23 15:30:02 -0400100std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromData(sk_sp<SkData> data,
101 SkPngChunkReader* chunkReader) {
msarett3d9d7a72015-10-21 10:27:10 -0700102 if (!data) {
103 return nullptr;
104 }
105
Mike Reed847068c2017-07-26 11:35:53 -0400106 return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader);
msarett3d9d7a72015-10-21 10:27:10 -0700107}
108
msarett9a0e3462015-12-11 07:38:50 -0800109SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
Matt Sarett8dcc84f2016-12-14 10:23:41 -0500110 bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
msarett9a0e3462015-12-11 07:38:50 -0800111 switch (requestedColorType) {
112 case kARGB_4444_SkColorType:
msarett9a0e3462015-12-11 07:38:50 -0800113 return kN32_SkColorType;
Matt Sarett8dcc84f2016-12-14 10:23:41 -0500114 case kN32_SkColorType:
msarett9a0e3462015-12-11 07:38:50 -0800115 break;
116 case kAlpha_8_SkColorType:
117 // Fall through to kGray_8. Before kGray_8_SkColorType existed,
118 // we allowed clients to request kAlpha_8 when they wanted a
119 // grayscale decode.
120 case kGray_8_SkColorType:
Matt Sarett8db74f12017-06-14 13:02:05 +0000121 if (kGray_8_SkColorType == this->getInfo().colorType()) {
msarett9a0e3462015-12-11 07:38:50 -0800122 return kGray_8_SkColorType;
123 }
124 break;
125 case kRGB_565_SkColorType:
126 if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
127 return kRGB_565_SkColorType;
128 }
129 break;
Matt Sarett8dcc84f2016-12-14 10:23:41 -0500130 case kRGBA_F16_SkColorType:
131 return kRGBA_F16_SkColorType;
msarett9a0e3462015-12-11 07:38:50 -0800132 default:
133 break;
134 }
135
Matt Sarett8db74f12017-06-14 13:02:05 +0000136 // F16 is the Android default for high precision images.
137 return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType;
msarett9a0e3462015-12-11 07:38:50 -0800138}
139
140SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
141 if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
142 return kOpaque_SkAlphaType;
143 }
144 return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
145}
146
Matt Sarett68feef42017-04-11 09:51:32 -0400147sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
148 sk_sp<SkColorSpace> prefColorSpace) {
Matt Sarett966bb342016-12-12 16:30:13 -0500149 switch (outputColorType) {
Leon Scroggins IIIc20b5f82017-07-13 08:05:29 -0400150 case kRGBA_8888_SkColorType:
151 case kBGRA_8888_SkColorType: {
Matt Sarett68feef42017-04-11 09:51:32 -0400152 // If |prefColorSpace| is supported, choose it.
Matt Sarett9341c982017-02-28 15:36:42 -0500153 SkColorSpaceTransferFn fn;
Matt Sarett68feef42017-04-11 09:51:32 -0400154 if (prefColorSpace && prefColorSpace->isNumericalTransferFn(&fn)) {
155 return prefColorSpace;
156 }
157
158 SkColorSpace* encodedSpace = fCodec->getInfo().colorSpace();
Matt Sarett9341c982017-02-28 15:36:42 -0500159 if (encodedSpace->isNumericalTransferFn(&fn)) {
160 // Leave the pixels in the encoded color space. Color space conversion
161 // will be handled after decode time.
Matt Sarettcf3f2342017-03-23 15:32:25 -0400162 return sk_ref_sp(encodedSpace);
Matt Sarett9341c982017-02-28 15:36:42 -0500163 }
164
165 if (is_wide_gamut(encodedSpace)) {
Matt Sarett2ae3a2e2017-02-15 08:48:02 -0500166 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
Matt Sarettcf3f2342017-03-23 15:32:25 -0400167 SkColorSpace::kDCIP3_D65_Gamut);
Matt Sarett2ae3a2e2017-02-15 08:48:02 -0500168 }
169
Matt Sarettcf3f2342017-03-23 15:32:25 -0400170 return SkColorSpace::MakeSRGB();
Matt Sarett9341c982017-02-28 15:36:42 -0500171 }
Matt Sarett966bb342016-12-12 16:30:13 -0500172 case kRGBA_F16_SkColorType:
Matt Sarett68feef42017-04-11 09:51:32 -0400173 // Note that |prefColorSpace| is ignored, F16 is always linear sRGB.
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500174 return SkColorSpace::MakeSRGBLinear();
Matt Sarett3725f0a2017-03-28 14:34:20 -0400175 case kRGB_565_SkColorType:
Matt Sarett68feef42017-04-11 09:51:32 -0400176 // Note that |prefColorSpace| is ignored, 565 is always sRGB.
Matt Sarett3725f0a2017-03-28 14:34:20 -0400177 return SkColorSpace::MakeSRGB();
Matt Sarett966bb342016-12-12 16:30:13 -0500178 default:
Matt Sarett3725f0a2017-03-28 14:34:20 -0400179 // Color correction not supported for kGray.
Matt Sarett966bb342016-12-12 16:30:13 -0500180 return nullptr;
181 }
182}
183
msarett3d9d7a72015-10-21 10:27:10 -0700184SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
185 if (!is_valid_sample_size(sampleSize)) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400186 return {0, 0};
msarett3d9d7a72015-10-21 10:27:10 -0700187 }
188
scroggo501b7342015-11-03 07:55:11 -0800189 // Fast path for when we are not scaling.
190 if (1 == sampleSize) {
191 return fInfo.dimensions();
192 }
193
msarett3d9d7a72015-10-21 10:27:10 -0700194 return this->onGetSampledDimensions(sampleSize);
195}
196
197bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
198 if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) {
199 return false;
200 }
201
202 return this->onGetSupportedSubset(desiredSubset);
203}
204
205SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
206 if (!is_valid_sample_size(sampleSize)) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400207 return {0, 0};
msarett3d9d7a72015-10-21 10:27:10 -0700208 }
209
210 // We require that the input subset is a subset that is supported by SkAndroidCodec.
211 // We test this by calling getSupportedSubset() and verifying that no modifications
212 // are made to the subset.
213 SkIRect copySubset = subset;
214 if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
Hal Canaryfafe1352017-04-11 12:12:02 -0400215 return {0, 0};
msarett3d9d7a72015-10-21 10:27:10 -0700216 }
217
scroggo501b7342015-11-03 07:55:11 -0800218 // If the subset is the entire image, for consistency, use getSampledDimensions().
msarett3d9d7a72015-10-21 10:27:10 -0700219 if (fInfo.dimensions() == subset.size()) {
scroggo501b7342015-11-03 07:55:11 -0800220 return this->getSampledDimensions(sampleSize);
msarett3d9d7a72015-10-21 10:27:10 -0700221 }
222
223 // This should perhaps call a virtual function, but currently both of our subclasses
224 // want the same implementation.
Hal Canaryfafe1352017-04-11 12:12:02 -0400225 return {get_scaled_dimension(subset.width(), sampleSize),
226 get_scaled_dimension(subset.height(), sampleSize)};
msarett3d9d7a72015-10-21 10:27:10 -0700227}
228
229SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
scroggoe95a0682015-11-04 04:31:12 -0800230 size_t rowBytes, const AndroidOptions* options) {
msarett3d9d7a72015-10-21 10:27:10 -0700231 if (!pixels) {
232 return SkCodec::kInvalidParameters;
233 }
234 if (rowBytes < info.minRowBytes()) {
235 return SkCodec::kInvalidParameters;
236 }
237
238 AndroidOptions defaultOptions;
239 if (!options) {
240 options = &defaultOptions;
241 } else if (options->fSubset) {
242 if (!is_valid_subset(*options->fSubset, fInfo.dimensions())) {
243 return SkCodec::kInvalidParameters;
244 }
scroggo501b7342015-11-03 07:55:11 -0800245
246 if (SkIRect::MakeSize(fInfo.dimensions()) == *options->fSubset) {
247 // The caller wants the whole thing, rather than a subset. Modify
248 // the AndroidOptions passed to onGetAndroidPixels to not specify
249 // a subset.
250 defaultOptions = *options;
251 defaultOptions.fSubset = nullptr;
252 options = &defaultOptions;
253 }
msarett3d9d7a72015-10-21 10:27:10 -0700254 }
255
256 return this->onGetAndroidPixels(info, pixels, rowBytes, *options);
257}
258
259SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
260 size_t rowBytes) {
261 return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
262}