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 "include/codec/SkAndroidCodec.h"
9
10 #include "include/codec/SkCodec.h"
11 #include "include/core/SkAlphaType.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkEncodedImageFormat.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkStream.h"
18 #include "include/private/SkGainmapInfo.h"
19 #include "include/private/base/SkFloatingPoint.h"
20 #include "modules/skcms/skcms.h"
21 #include "src/codec/SkCodecPriv.h"
22 #include "src/codec/SkSampledCodec.h"
23
24 #if defined(SK_CODEC_DECODES_WEBP) || defined(SK_CODEC_DECODES_RAW) || \
25 defined(SK_HAS_WUFFS_LIBRARY) || defined(SK_CODEC_DECODES_AVIF)
26 #include "src/codec/SkAndroidCodecAdapter.h"
27 #endif
28
29 #include <algorithm>
30 #include <cstdint>
31 #include <utility>
32
33 class SkPngChunkReader;
34
is_valid_sample_size(int sampleSize)35 static bool is_valid_sample_size(int sampleSize) {
36 // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize?
37 return sampleSize > 0;
38 }
39
cicp_get_primaries(uint8_t primaries,skcms_Matrix3x3 * sk_primaries)40 static bool cicp_get_primaries(uint8_t primaries, skcms_Matrix3x3* sk_primaries) {
41 // Rec. ITU-T H.273, Table 2.
42 switch (primaries) {
43 case 0:
44 // Reserved.
45 break;
46 case 1:
47 *sk_primaries = SkNamedGamut::kSRGB;
48 return true;
49 case 2:
50 // Unspecified.
51 break;
52 case 3:
53 // Reserved.
54 break;
55 case 4:
56 return skcms_PrimariesToXYZD50(
57 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.31f, 0.316f, sk_primaries);
58 case 5:
59 return skcms_PrimariesToXYZD50(
60 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f, sk_primaries);
61 case 6:
62 return skcms_PrimariesToXYZD50(
63 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f, sk_primaries);
64 case 7:
65 return skcms_PrimariesToXYZD50(
66 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f, sk_primaries);
67 case 8:
68 return skcms_PrimariesToXYZD50(
69 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f, sk_primaries);
70 case 9:
71 *sk_primaries = SkNamedGamut::kRec2020;
72 return true;
73 case 10:
74 return skcms_PrimariesToXYZD50(
75 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f / 3.f, 1.f / 3.f, sk_primaries);
76 case 11:
77 return skcms_PrimariesToXYZD50(
78 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f, sk_primaries);
79 case 12:
80 return skcms_PrimariesToXYZD50(
81 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f, sk_primaries);
82 case 22:
83 return skcms_PrimariesToXYZD50(
84 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f, sk_primaries);
85 default:
86 // Reserved.
87 break;
88 }
89 *sk_primaries = SkNamedGamut::kSRGB;
90 return false;
91 }
92
cicp_get_transfer_fn(uint8_t transfer_characteristics,skcms_TransferFunction * trfn)93 static bool cicp_get_transfer_fn(uint8_t transfer_characteristics, skcms_TransferFunction* trfn) {
94 // Rec. ITU-T H.273, Table 3.
95 switch (transfer_characteristics) {
96 case 0:
97 // Reserved.
98 break;
99 case 1:
100 *trfn = SkNamedTransferFn::kRec2020;
101 return true;
102 case 2:
103 // Unspecified.
104 break;
105 case 3:
106 // Reserved.
107 break;
108 case 4:
109 *trfn = {2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
110 return true;
111 case 5:
112 *trfn = {2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
113 return true;
114 case 6:
115 *trfn = SkNamedTransferFn::kRec2020;
116 return true;
117 case 7:
118 *trfn = {2.222222222222f,
119 0.899626676224f,
120 0.100373323776f,
121 0.25f,
122 0.091286342118f,
123 0.f,
124 0.f};
125 return true;
126 case 8:
127 *trfn = SkNamedTransferFn::kLinear;
128 return true;
129 case 9:
130 // Logarithmic transfer characteristic (100:1 range).
131 // Not supported by skcms
132 break;
133 case 10:
134 // Logarithmic transfer characteristic (100 * Sqrt( 10 ) : 1 range).
135 // Not supported by skcms
136 break;
137 case 11:
138 *trfn = SkNamedTransferFn::kSRGB;
139 break;
140 case 12:
141 // Rec. ITU-R BT.1361-0 extended colour gamut system (historical).
142 // Same as kRec709 on positive values, differs on negative values.
143 // Not supported by skcms
144 break;
145 case 13:
146 *trfn = SkNamedTransferFn::kSRGB;
147 return true;
148 case 14:
149 *trfn = SkNamedTransferFn::kRec2020;
150 return true;
151 case 15:
152 *trfn = SkNamedTransferFn::kRec2020;
153 return true;
154 case 16:
155 // Android expects PQ to match 203 nits to SDR white
156 *trfn = {-2.f,
157 -1.55522297832f,
158 1.86045365631f,
159 32 / 2523.0f,
160 2413 / 128.0f,
161 -2392 / 128.0f,
162 8192 / 1305.0f};
163 return true;
164 case 17:
165 *trfn = {2.6f, 1.034080527699f, 0.f, 0.f, 0.f, 0.f, 0.f};
166 return true;
167 case 18:
168 // Android expects HLG to match 203 nits to SDR white
169 if (skcms_TransferFunction_makeScaledHLGish(trfn,
170 0.314509843f,
171 2.f,
172 2.f,
173 1.f / 0.17883277f,
174 0.28466892f,
175 0.55991073f)) {
176 return true;
177 }
178 break;
179 default:
180 // 19-255 Reserved.
181 break;
182 }
183
184 *trfn = SkNamedTransferFn::kSRGB;
185 return false;
186 }
187
cicp_get_sk_color_space(uint8_t color_primaries,uint8_t transfer_characteristics,uint8_t matrix_coefficients,uint8_t full_range_flag)188 static sk_sp<SkColorSpace> cicp_get_sk_color_space(uint8_t color_primaries,
189 uint8_t transfer_characteristics,
190 uint8_t matrix_coefficients,
191 uint8_t full_range_flag) {
192 if (matrix_coefficients != 0) return nullptr;
193
194 if (full_range_flag != 1) return nullptr;
195
196 skcms_TransferFunction trfn;
197 if (!cicp_get_transfer_fn(transfer_characteristics, &trfn)) return nullptr;
198
199 skcms_Matrix3x3 primaries;
200 if (!cicp_get_primaries(color_primaries, &primaries)) return nullptr;
201
202 return SkColorSpace::MakeRGB(trfn, primaries);
203 }
204
SkAndroidCodec(SkCodec * codec)205 SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
206 : fInfo(codec->getInfo())
207 , fCodec(codec)
208 {}
209
~SkAndroidCodec()210 SkAndroidCodec::~SkAndroidCodec() {}
211
MakeFromStream(std::unique_ptr<SkStream> stream,SkPngChunkReader * chunkReader)212 std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
213 SkPngChunkReader* chunkReader) {
214 auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader);
215 return MakeFromCodec(std::move(codec));
216 }
217
MakeFromCodec(std::unique_ptr<SkCodec> codec)218 std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromCodec(std::unique_ptr<SkCodec> codec) {
219 if (nullptr == codec) {
220 return nullptr;
221 }
222
223 switch ((SkEncodedImageFormat)codec->getEncodedFormat()) {
224 case SkEncodedImageFormat::kPNG:
225 case SkEncodedImageFormat::kICO:
226 case SkEncodedImageFormat::kJPEG:
227 #ifndef SK_HAS_WUFFS_LIBRARY
228 case SkEncodedImageFormat::kGIF:
229 #endif
230 case SkEncodedImageFormat::kBMP:
231 case SkEncodedImageFormat::kWBMP:
232 case SkEncodedImageFormat::kHEIF:
233 #ifndef SK_CODEC_DECODES_AVIF
234 case SkEncodedImageFormat::kAVIF:
235 #endif
236 return std::make_unique<SkSampledCodec>(codec.release());
237 #ifdef SK_HAS_WUFFS_LIBRARY
238 case SkEncodedImageFormat::kGIF:
239 #endif
240 #ifdef SK_CODEC_DECODES_WEBP
241 case SkEncodedImageFormat::kWEBP:
242 #endif
243 #ifdef SK_CODEC_DECODES_RAW
244 case SkEncodedImageFormat::kDNG:
245 #endif
246 #ifdef SK_CODEC_DECODES_AVIF
247 case SkEncodedImageFormat::kAVIF:
248 #endif
249 #if defined(SK_CODEC_DECODES_WEBP) || defined(SK_CODEC_DECODES_RAW) || \
250 defined(SK_HAS_WUFFS_LIBRARY) || defined(SK_CODEC_DECODES_AVIF)
251 return std::make_unique<SkAndroidCodecAdapter>(codec.release());
252 #endif
253
254 default:
255 return nullptr;
256 }
257 }
258
MakeFromData(sk_sp<SkData> data,SkPngChunkReader * chunkReader)259 std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromData(sk_sp<SkData> data,
260 SkPngChunkReader* chunkReader) {
261 if (!data) {
262 return nullptr;
263 }
264
265 return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader);
266 }
267
computeOutputColorType(SkColorType requestedColorType)268 SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) {
269 bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8;
270 uint8_t colorDepth = fCodec->getEncodedInfo().getColorDepth();
271 switch (requestedColorType) {
272 case kARGB_4444_SkColorType:
273 return kN32_SkColorType;
274 case kN32_SkColorType:
275 break;
276 case kAlpha_8_SkColorType:
277 // Fall through to kGray_8. Before kGray_8_SkColorType existed,
278 // we allowed clients to request kAlpha_8 when they wanted a
279 // grayscale decode.
280 case kGray_8_SkColorType:
281 if (kGray_8_SkColorType == this->getInfo().colorType()) {
282 return kGray_8_SkColorType;
283 }
284 break;
285 case kRGB_565_SkColorType:
286 if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
287 return kRGB_565_SkColorType;
288 }
289 break;
290 case kRGBA_1010102_SkColorType:
291 if (colorDepth == 10) {
292 return kRGBA_1010102_SkColorType;
293 }
294 break;
295 case kRGBA_F16_SkColorType:
296 return kRGBA_F16_SkColorType;
297 default:
298 break;
299 }
300
301 // F16 is the Android default for high precision images.
302 return highPrecision ? kRGBA_F16_SkColorType :
303 (colorDepth == 10 ? kRGBA_1010102_SkColorType : kN32_SkColorType);
304 }
305
computeOutputAlphaType(bool requestedUnpremul)306 SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) {
307 if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
308 return kOpaque_SkAlphaType;
309 }
310 return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
311 }
312
computeOutputColorSpace(SkColorType outputColorType,sk_sp<SkColorSpace> prefColorSpace)313 sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType,
314 sk_sp<SkColorSpace> prefColorSpace) {
315 switch (outputColorType) {
316 case kRGBA_F16_SkColorType:
317 case kRGB_565_SkColorType:
318 case kRGBA_8888_SkColorType:
319 case kBGRA_8888_SkColorType:
320 case kRGBA_1010102_SkColorType: {
321 // If |prefColorSpace| is supplied, choose it.
322 if (prefColorSpace) {
323 return prefColorSpace;
324 }
325
326 const skcms_ICCProfile* encodedProfile = fCodec->getEncodedInfo().profile();
327 if (encodedProfile) {
328 // Prefer CICP information if it exists.
329 if (encodedProfile->has_CICP) {
330 const auto cicpColorSpace =
331 cicp_get_sk_color_space(encodedProfile->CICP.color_primaries,
332 encodedProfile->CICP.transfer_characteristics,
333 encodedProfile->CICP.matrix_coefficients,
334 encodedProfile->CICP.video_full_range_flag);
335 if (cicpColorSpace) {
336 return cicpColorSpace;
337 }
338 }
339 if (auto encodedSpace = SkColorSpace::Make(*encodedProfile)) {
340 // Leave the pixels in the encoded color space. Color space conversion
341 // will be handled after decode time.
342 return encodedSpace;
343 }
344
345 if (encodedProfile->has_toXYZD50) {
346 return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB,
347 encodedProfile->toXYZD50);
348 }
349 }
350
351 return SkColorSpace::MakeSRGB();
352 }
353 default:
354 // Color correction not supported for kGray.
355 return nullptr;
356 }
357 }
358
supports_any_down_scale(const SkCodec * codec)359 static bool supports_any_down_scale(const SkCodec* codec) {
360 return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
361 }
362
363 // There are a variety of ways two SkISizes could be compared. This method
364 // returns true if either dimensions of a is < that of b.
365 // computeSampleSize also uses the opposite, which means that both
366 // dimensions of a >= b.
smaller_than(const SkISize & a,const SkISize & b)367 static inline bool smaller_than(const SkISize& a, const SkISize& b) {
368 return a.width() < b.width() || a.height() < b.height();
369 }
370
371 // Both dimensions of a > that of b.
strictly_bigger_than(const SkISize & a,const SkISize & b)372 static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b) {
373 return a.width() > b.width() && a.height() > b.height();
374 }
375
computeSampleSize(SkISize * desiredSize) const376 int SkAndroidCodec::computeSampleSize(SkISize* desiredSize) const {
377 SkASSERT(desiredSize);
378
379 const auto origDims = fCodec->dimensions();
380 if (!desiredSize || *desiredSize == origDims) {
381 return 1;
382 }
383
384 if (smaller_than(origDims, *desiredSize)) {
385 *desiredSize = origDims;
386 return 1;
387 }
388
389 // Handle bad input:
390 if (desiredSize->width() < 1 || desiredSize->height() < 1) {
391 *desiredSize = SkISize::Make(std::max(1, desiredSize->width()),
392 std::max(1, desiredSize->height()));
393 }
394
395 if (supports_any_down_scale(fCodec.get())) {
396 return 1;
397 }
398
399 int sampleX = origDims.width() / desiredSize->width();
400 int sampleY = origDims.height() / desiredSize->height();
401 int sampleSize = std::min(sampleX, sampleY);
402 auto computedSize = this->getSampledDimensions(sampleSize);
403 if (computedSize == *desiredSize) {
404 return sampleSize;
405 }
406
407 if (computedSize == origDims || sampleSize == 1) {
408 // Cannot downscale
409 *desiredSize = computedSize;
410 return 1;
411 }
412
413 if (strictly_bigger_than(computedSize, *desiredSize)) {
414 // See if there is a tighter fit.
415 while (true) {
416 auto smaller = this->getSampledDimensions(sampleSize + 1);
417 if (smaller == *desiredSize) {
418 return sampleSize + 1;
419 }
420 if (smaller == computedSize || smaller_than(smaller, *desiredSize)) {
421 // Cannot get any smaller without being smaller than desired.
422 *desiredSize = computedSize;
423 return sampleSize;
424 }
425
426 sampleSize++;
427 computedSize = smaller;
428 }
429
430 SkASSERT(false);
431 }
432
433 if (!smaller_than(computedSize, *desiredSize)) {
434 // This means one of the computed dimensions is equal to desired, and
435 // the other is bigger. This is as close as we can get.
436 *desiredSize = computedSize;
437 return sampleSize;
438 }
439
440 // computedSize is too small. Make it larger.
441 while (sampleSize > 2) {
442 auto bigger = this->getSampledDimensions(sampleSize - 1);
443 if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) {
444 *desiredSize = bigger;
445 return sampleSize - 1;
446 }
447 sampleSize--;
448 }
449
450 *desiredSize = origDims;
451 return 1;
452 }
453
getSampledDimensions(int sampleSize) const454 SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const {
455 if (!is_valid_sample_size(sampleSize)) {
456 return {0, 0};
457 }
458
459 // Fast path for when we are not scaling.
460 if (1 == sampleSize) {
461 return fCodec->dimensions();
462 }
463
464 return this->onGetSampledDimensions(sampleSize);
465 }
466
getSupportedSubset(SkIRect * desiredSubset) const467 bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const {
468 if (!desiredSubset || !is_valid_subset(*desiredSubset, fCodec->dimensions())) {
469 return false;
470 }
471
472 return this->onGetSupportedSubset(desiredSubset);
473 }
474
getSampledSubsetDimensions(int sampleSize,const SkIRect & subset) const475 SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const {
476 if (!is_valid_sample_size(sampleSize)) {
477 return {0, 0};
478 }
479
480 // We require that the input subset is a subset that is supported by SkAndroidCodec.
481 // We test this by calling getSupportedSubset() and verifying that no modifications
482 // are made to the subset.
483 SkIRect copySubset = subset;
484 if (!this->getSupportedSubset(©Subset) || copySubset != subset) {
485 return {0, 0};
486 }
487
488 // If the subset is the entire image, for consistency, use getSampledDimensions().
489 if (fCodec->dimensions() == subset.size()) {
490 return this->getSampledDimensions(sampleSize);
491 }
492
493 // This should perhaps call a virtual function, but currently both of our subclasses
494 // want the same implementation.
495 return {get_scaled_dimension(subset.width(), sampleSize),
496 get_scaled_dimension(subset.height(), sampleSize)};
497 }
498
getAndroidPixels(const SkImageInfo & requestInfo,void * requestPixels,size_t requestRowBytes,const AndroidOptions * options)499 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& requestInfo,
500 void* requestPixels, size_t requestRowBytes, const AndroidOptions* options) {
501 if (!requestPixels) {
502 return SkCodec::kInvalidParameters;
503 }
504 if (requestRowBytes < requestInfo.minRowBytes()) {
505 return SkCodec::kInvalidParameters;
506 }
507
508 AndroidOptions defaultOptions;
509 if (!options) {
510 options = &defaultOptions;
511 } else {
512 if (options->fSubset) {
513 if (!is_valid_subset(*options->fSubset, fCodec->dimensions())) {
514 return SkCodec::kInvalidParameters;
515 }
516
517 if (SkIRect::MakeSize(fCodec->dimensions()) == *options->fSubset) {
518 // The caller wants the whole thing, rather than a subset. Modify
519 // the AndroidOptions passed to onGetAndroidPixels to not specify
520 // a subset.
521 defaultOptions = *options;
522 defaultOptions.fSubset = nullptr;
523 options = &defaultOptions;
524 }
525 }
526 }
527
528 if (auto result = fCodec->handleFrameIndex(requestInfo, requestPixels, requestRowBytes,
529 *options, this); result != SkCodec::kSuccess) {
530 return result;
531 }
532
533 return this->onGetAndroidPixels(requestInfo, requestPixels, requestRowBytes, *options);
534 }
535
getAndroidPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)536 SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels,
537 size_t rowBytes) {
538 return this->getAndroidPixels(info, pixels, rowBytes, nullptr);
539 }
540
getAndroidGainmap(SkGainmapInfo * info,std::unique_ptr<SkStream> * outGainmapImageStream)541 bool SkAndroidCodec::getAndroidGainmap(SkGainmapInfo* info,
542 std::unique_ptr<SkStream>* outGainmapImageStream) {
543 if (!fCodec->onGetGainmapInfo(info, outGainmapImageStream)) {
544 return false;
545 }
546 // Convert old parameter names to new parameter names.
547 // TODO(ccameron): Remove these parameters.
548 info->fLogRatioMin.fR = sk_float_log(info->fGainmapRatioMin.fR);
549 info->fLogRatioMin.fG = sk_float_log(info->fGainmapRatioMin.fG);
550 info->fLogRatioMin.fB = sk_float_log(info->fGainmapRatioMin.fB);
551 info->fLogRatioMax.fR = sk_float_log(info->fGainmapRatioMax.fR);
552 info->fLogRatioMax.fG = sk_float_log(info->fGainmapRatioMax.fG);
553 info->fLogRatioMax.fB = sk_float_log(info->fGainmapRatioMax.fB);
554 info->fHdrRatioMin = info->fDisplayRatioSdr;
555 info->fHdrRatioMax = info->fDisplayRatioHdr;
556 return true;
557 }
558