1 /*
2 * Copyright 2024 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 "src/codec/SkCrabbyAvifCodec.h"
9
10 #include "include/codec/SkAndroidCodec.h"
11 #include "include/codec/SkAvifDecoder.h"
12 #include "include/codec/SkCodec.h"
13 #include "include/codec/SkCodecAnimation.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkSize.h"
17 #include "include/core/SkStream.h"
18 #include "include/core/SkTypes.h"
19 #include "include/private/SkGainmapInfo.h"
20 #include "modules/skcms/skcms.h"
21 #include "src/core/SkStreamPriv.h"
22
23 #include <cstdint>
24 #include <cstring>
25 #include <utility>
26
27 #include "avif/avif.h"
28 #include "avif/libavif_compat.h"
29
30 namespace {
31
32 template <typename NumeratorType>
FractionToFloat(NumeratorType numerator,uint32_t denominator)33 float FractionToFloat(NumeratorType numerator, uint32_t denominator) {
34 // First cast to double and not float because uint32_t->float conversion can
35 // cause precision loss.
36 return static_cast<double>(numerator) / denominator;
37 }
38
AltImageColorSpace(const crabbyavif::avifGainMap & gain_map,const crabbyavif::avifImage & image)39 sk_sp<SkColorSpace> AltImageColorSpace(const crabbyavif::avifGainMap& gain_map,
40 const crabbyavif::avifImage& image) {
41 sk_sp<SkColorSpace> color_space = nullptr;
42 if (!gain_map.altICC.size) {
43 return nullptr;
44 }
45 if (image.icc.size == gain_map.altICC.size &&
46 memcmp(gain_map.altICC.data, image.icc.data, gain_map.altICC.size) == 0) {
47 // Same ICC as the base image, no need to specify it.
48 return nullptr;
49 }
50 skcms_ICCProfile icc_profile;
51 if (!skcms_Parse(gain_map.altICC.data, gain_map.altICC.size, &icc_profile)) {
52 return nullptr;
53 }
54 return SkColorSpace::Make(icc_profile);
55 }
56
PopulateGainmapInfo(const crabbyavif::avifGainMap & gain_map,const crabbyavif::avifImage & image,SkGainmapInfo * info)57 bool PopulateGainmapInfo(const crabbyavif::avifGainMap& gain_map,
58 const crabbyavif::avifImage& image,
59 SkGainmapInfo* info) {
60 if (gain_map.baseHdrHeadroom.d == 0 || gain_map.alternateHdrHeadroom.d == 0) {
61 return false;
62 }
63 const float base_headroom =
64 std::exp2(FractionToFloat(gain_map.baseHdrHeadroom.n, gain_map.baseHdrHeadroom.d));
65 const float alternate_headroom = std::exp2(
66 FractionToFloat(gain_map.alternateHdrHeadroom.n, gain_map.alternateHdrHeadroom.d));
67 const bool base_is_hdr = base_headroom > alternate_headroom;
68 info->fDisplayRatioSdr = base_is_hdr ? alternate_headroom : base_headroom;
69 info->fDisplayRatioHdr = base_is_hdr ? base_headroom : alternate_headroom;
70 info->fBaseImageType =
71 base_is_hdr ? SkGainmapInfo::BaseImageType::kHDR : SkGainmapInfo::BaseImageType::kSDR;
72 for (int i = 0; i < 3; ++i) {
73 if (gain_map.gainMapMin[i].d == 0 || gain_map.gainMapMax[i].d == 0 ||
74 gain_map.gainMapGamma[i].d == 0 || gain_map.baseOffset[i].d == 0 ||
75 gain_map.alternateOffset[i].d == 0 || gain_map.gainMapGamma[i].n == 0) {
76 return false;
77 }
78 const float min_log2 = FractionToFloat(gain_map.gainMapMin[i].n, gain_map.gainMapMin[i].d);
79 const float max_log2 = FractionToFloat(gain_map.gainMapMax[i].n, gain_map.gainMapMax[i].d);
80 info->fGainmapRatioMin[i] = std::exp2(min_log2);
81 info->fGainmapRatioMax[i] = std::exp2(max_log2);
82 // Numerator and denominator intentionally swapped to get 1.0/gamma.
83 info->fGainmapGamma[i] =
84 FractionToFloat(gain_map.gainMapGamma[i].d, gain_map.gainMapGamma[i].n);
85 const float base_offset =
86 FractionToFloat(gain_map.baseOffset[i].n, gain_map.baseOffset[i].d);
87 const float alternate_offset =
88 FractionToFloat(gain_map.alternateOffset[i].n, gain_map.alternateOffset[i].d);
89 info->fEpsilonSdr[i] = base_is_hdr ? alternate_offset : base_offset;
90 info->fEpsilonHdr[i] = base_is_hdr ? base_offset : alternate_offset;
91 }
92 if (!gain_map.useBaseColorSpace) {
93 info->fGainmapMathColorSpace = AltImageColorSpace(gain_map, image);
94 }
95 return true;
96 }
97
ComputeSkEncodedOrigin(const crabbyavif::avifImage & image)98 SkEncodedOrigin ComputeSkEncodedOrigin(const crabbyavif::avifImage& image) {
99 // |angle| * 90 specifies the angle of anti-clockwise rotation in degrees.
100 // Legal values: [0-3].
101 const int angle =
102 ((image.transformFlags & crabbyavif::AVIF_TRANSFORM_IROT) && image.irot.angle <= 3)
103 ? image.irot.angle
104 : 0;
105 // |axis| specifies how the mirroring is performed.
106 // -1: No mirroring.
107 // 0: The top and bottom parts of the image are exchanged.
108 // 1: The left and right parts of the image are exchanged.
109 const int axis =
110 ((image.transformFlags & crabbyavif::AVIF_TRANSFORM_IMIR) && image.imir.axis <= 1)
111 ? image.imir.axis
112 : -1;
113 // The first dimension is axis (with an offset of 1). The second dimension
114 // is angle.
115 const SkEncodedOrigin kAxisAngleToSkEncodedOrigin[3][4] = {
116 // No mirroring.
117 {kTopLeft_SkEncodedOrigin,
118 kLeftBottom_SkEncodedOrigin,
119 kBottomRight_SkEncodedOrigin,
120 kRightTop_SkEncodedOrigin},
121 // Top-to-bottom mirroring. Change Top<->Bottom in the first row.
122 {kBottomLeft_SkEncodedOrigin,
123 kLeftTop_SkEncodedOrigin,
124 kTopRight_SkEncodedOrigin,
125 kRightBottom_SkEncodedOrigin},
126 // Left-to-right mirroring. Change Left<->Right in the first row.
127 {kTopRight_SkEncodedOrigin,
128 kRightBottom_SkEncodedOrigin,
129 kBottomLeft_SkEncodedOrigin,
130 kLeftTop_SkEncodedOrigin},
131 };
132 return kAxisAngleToSkEncodedOrigin[axis + 1][angle];
133 }
134
135 } // namespace
136
operator ()(crabbyavif::avifDecoder * decoder) const137 void AvifDecoderDeleter::operator()(crabbyavif::avifDecoder* decoder) const {
138 if (decoder != nullptr) {
139 crabbyavif::avifDecoderDestroy(decoder);
140 }
141 }
142
IsAvif(const void * buffer,size_t bytesRead)143 bool SkCrabbyAvifCodec::IsAvif(const void* buffer, size_t bytesRead) {
144 crabbyavif::avifROData avifData = {static_cast<const uint8_t*>(buffer), bytesRead};
145 return crabbyavif::avifPeekCompatibleFileType(&avifData) == crabbyavif::CRABBY_AVIF_TRUE;
146 }
147
MakeFromStream(std::unique_ptr<SkStream> stream,Result * result,bool gainmapOnly)148 std::unique_ptr<SkCodec> SkCrabbyAvifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
149 Result* result,
150 bool gainmapOnly /*=false*/) {
151 SkASSERT(result);
152 if (!stream) {
153 *result = SkCodec::kInvalidInput;
154 return nullptr;
155 }
156
157 // CrabbyAvif needs a contiguous data buffer.
158 sk_sp<SkData> data = nullptr;
159 if (stream->getMemoryBase()) {
160 // It is safe to make without copy because we'll hold onto the stream.
161 data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength());
162 } else {
163 data = SkCopyStreamToData(stream.get());
164 // If we are forced to copy the stream to a data, we can go ahead and
165 // delete the stream.
166 stream.reset(nullptr);
167 }
168 return SkCrabbyAvifCodec::MakeFromData(std::move(stream), std::move(data), result, gainmapOnly);
169 }
170
MakeFromData(std::unique_ptr<SkStream> stream,sk_sp<SkData> data,Result * result,bool gainmapOnly)171 std::unique_ptr<SkCodec> SkCrabbyAvifCodec::MakeFromData(std::unique_ptr<SkStream> stream,
172 sk_sp<SkData> data,
173 Result* result,
174 bool gainmapOnly /*=false*/) {
175 SkASSERT(result);
176
177 AvifDecoder avifDecoder(crabbyavif::avifDecoderCreate());
178 if (avifDecoder == nullptr) {
179 *result = SkCodec::kInternalError;
180 return nullptr;
181 }
182
183 // Ignore XMP and Exif to ensure that avifDecoderParse() isn't waiting for
184 // some tiny Exif payload hiding at the end of a file.
185 avifDecoder->ignoreXMP = crabbyavif::CRABBY_AVIF_TRUE;
186 avifDecoder->ignoreExif = crabbyavif::CRABBY_AVIF_TRUE;
187
188 // Disable strict mode. This allows some AVIF files in the wild that are
189 // technically invalid according to the specification because they were
190 // created with older tools but can be decoded and rendered without any
191 // issues.
192 avifDecoder->strictFlags = crabbyavif::AVIF_STRICT_DISABLED;
193
194 // Android uses MediaCodec for decoding the underlying image. So there is no
195 // need to set maxThreads since MediaCodec doesn't allow explicit setting
196 // of threads.
197
198 if (gainmapOnly) {
199 avifDecoder->imageContentToDecode = crabbyavif::AVIF_IMAGE_CONTENT_GAIN_MAP;
200 }
201
202 crabbyavif::avifResult res =
203 crabbyavif::avifDecoderSetIOMemory(avifDecoder.get(), data->bytes(), data->size());
204 if (res != crabbyavif::AVIF_RESULT_OK) {
205 *result = SkCodec::kInternalError;
206 return nullptr;
207 }
208
209 res = crabbyavif::avifDecoderParse(avifDecoder.get());
210 if (res != crabbyavif::AVIF_RESULT_OK) {
211 *result = SkCodec::kInvalidInput;
212 return nullptr;
213 }
214
215 // CrabbyAvif uses MediaCodec, which always sets bitsPerComponent to 8.
216 const int bitsPerComponent = 8;
217 SkEncodedInfo::Color color;
218 SkEncodedInfo::Alpha alpha;
219 if (avifDecoder->alphaPresent && !gainmapOnly) {
220 color = SkEncodedInfo::kRGBA_Color;
221 alpha = SkEncodedInfo::kUnpremul_Alpha;
222 } else {
223 color = SkEncodedInfo::kRGB_Color;
224 alpha = SkEncodedInfo::kOpaque_Alpha;
225 }
226 if (gainmapOnly && !avifDecoder->image->gainMap) {
227 *result = SkCodec::kInvalidInput;
228 return nullptr;
229 }
230 crabbyavif::avifImage* image =
231 gainmapOnly ? avifDecoder->image->gainMap->image : avifDecoder->image;
232 auto width = image->width;
233 auto height = image->height;
234 if (image->transformFlags & crabbyavif::AVIF_TRANSFORM_CLAP) {
235 crabbyavif::avifCropRect rect;
236 if (crabbyavif::crabby_avifCropRectConvertCleanApertureBox(
237 &rect, &image->clap, width, height, image->yuvFormat, nullptr)) {
238 width = rect.width;
239 height = rect.height;
240 }
241 }
242
243 std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
244 if (image->icc.size > 0) {
245 auto icc = SkData::MakeWithCopy(image->icc.data, image->icc.size);
246 profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
247 if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) {
248 profile = nullptr;
249 }
250 }
251
252 SkEncodedInfo info = SkEncodedInfo::Make(
253 width, height, color, alpha, bitsPerComponent, std::move(profile), image->depth);
254 bool animation = avifDecoder->imageCount > 1;
255 *result = kSuccess;
256 SkEncodedImageFormat format =
257 avifDecoder->compressionFormat == crabbyavif::COMPRESSION_FORMAT_AVIF
258 ? SkEncodedImageFormat::kAVIF
259 : SkEncodedImageFormat::kHEIF;
260 const SkEncodedOrigin origin = ComputeSkEncodedOrigin(*image);
261 return std::unique_ptr<SkCodec>(new SkCrabbyAvifCodec(std::move(info),
262 std::move(stream),
263 std::move(data),
264 std::move(avifDecoder),
265 origin,
266 animation,
267 gainmapOnly,
268 format));
269 }
270
SkCrabbyAvifCodec(SkEncodedInfo && info,std::unique_ptr<SkStream> stream,sk_sp<SkData> data,AvifDecoder avifDecoder,SkEncodedOrigin origin,bool useAnimation,bool gainmapOnly,SkEncodedImageFormat format)271 SkCrabbyAvifCodec::SkCrabbyAvifCodec(SkEncodedInfo&& info,
272 std::unique_ptr<SkStream> stream,
273 sk_sp<SkData> data,
274 AvifDecoder avifDecoder,
275 SkEncodedOrigin origin,
276 bool useAnimation,
277 bool gainmapOnly,
278 SkEncodedImageFormat format)
279 : SkScalingCodec(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin)
280 , fData(std::move(data))
281 , fAvifDecoder(std::move(avifDecoder))
282 , fUseAnimation(useAnimation)
283 , fGainmapOnly(gainmapOnly)
284 , fFormat(format) {}
285
onGetFrameCount()286 int SkCrabbyAvifCodec::onGetFrameCount() {
287 if (!fUseAnimation) {
288 return 1;
289 }
290
291 if (fFrameHolder.size() == 0) {
292 if (fAvifDecoder->imageCount <= 1) {
293 fUseAnimation = false;
294 return 1;
295 }
296 fFrameHolder.reserve(fAvifDecoder->imageCount);
297 for (int i = 0; i < fAvifDecoder->imageCount; i++) {
298 Frame* frame = fFrameHolder.appendNewFrame(fAvifDecoder->alphaPresent ==
299 crabbyavif::CRABBY_AVIF_TRUE);
300 frame->setXYWH(0, 0, fAvifDecoder->image->width, fAvifDecoder->image->height);
301 frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep);
302 crabbyavif::avifImageTiming timing;
303 avifDecoderNthImageTiming(fAvifDecoder.get(), i, &timing);
304 frame->setDuration(timing.duration * 1000);
305 frame->setRequiredFrame(SkCodec::kNoFrame);
306 frame->setHasAlpha(fAvifDecoder->alphaPresent == crabbyavif::CRABBY_AVIF_TRUE);
307 }
308 }
309
310 return fFrameHolder.size();
311 }
312
onGetFrame(int i) const313 const SkFrame* SkCrabbyAvifCodec::FrameHolder::onGetFrame(int i) const {
314 return static_cast<const SkFrame*>(this->frame(i));
315 }
316
appendNewFrame(bool hasAlpha)317 SkCrabbyAvifCodec::Frame* SkCrabbyAvifCodec::FrameHolder::appendNewFrame(bool hasAlpha) {
318 const int i = this->size();
319 fFrames.emplace_back(i,
320 hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha);
321 return &fFrames[i];
322 }
323
frame(int i) const324 const SkCrabbyAvifCodec::Frame* SkCrabbyAvifCodec::FrameHolder::frame(int i) const {
325 SkASSERT(i >= 0 && i < this->size());
326 return &fFrames[i];
327 }
328
onGetFrameInfo(int i,FrameInfo * frameInfo) const329 bool SkCrabbyAvifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const {
330 if (i >= fFrameHolder.size()) {
331 return false;
332 }
333
334 const Frame* frame = fFrameHolder.frame(i);
335 if (!frame) {
336 return false;
337 }
338
339 if (frameInfo) {
340 frame->fillIn(frameInfo, true);
341 }
342
343 return true;
344 }
345
onGetRepetitionCount()346 int SkCrabbyAvifCodec::onGetRepetitionCount() {
347 return (fAvifDecoder->repetitionCount < 0) ? kRepetitionCountInfinite
348 : fAvifDecoder->repetitionCount;
349 }
350
onIsAnimated()351 SkCodec::IsAnimated SkCrabbyAvifCodec::onIsAnimated() {
352 if (!fUseAnimation || fAvifDecoder->imageCount <= 1) {
353 return IsAnimated::kNo;
354 }
355 return IsAnimated::kYes;
356 }
357
conversionSupported(const SkImageInfo & dstInfo,bool srcIsOpaque,bool needsColorXform)358 bool SkCrabbyAvifCodec::conversionSupported(const SkImageInfo& dstInfo,
359 bool srcIsOpaque,
360 bool needsColorXform) {
361 return dstInfo.colorType() == kRGBA_8888_SkColorType ||
362 dstInfo.colorType() == kRGBA_1010102_SkColorType ||
363 dstInfo.colorType() == kRGBA_F16_SkColorType ||
364 dstInfo.colorType() == kRGB_565_SkColorType;
365 }
366
onGetPixels(const SkImageInfo & dstInfo,void * dst,size_t dstRowBytes,const Options & options,int * rowsDecoded)367 SkCodec::Result SkCrabbyAvifCodec::onGetPixels(const SkImageInfo& dstInfo,
368 void* dst,
369 size_t dstRowBytes,
370 const Options& options,
371 int* rowsDecoded) {
372 switch (dstInfo.colorType()) {
373 case kRGBA_8888_SkColorType:
374 case kRGB_565_SkColorType:
375 fAvifDecoder->androidMediaCodecOutputColorFormat =
376 crabbyavif::ANDROID_MEDIA_CODEC_OUTPUT_COLOR_FORMAT_YUV420_FLEXIBLE;
377 break;
378 case kRGBA_F16_SkColorType:
379 case kRGBA_1010102_SkColorType:
380 fAvifDecoder->androidMediaCodecOutputColorFormat =
381 crabbyavif::ANDROID_MEDIA_CODEC_OUTPUT_COLOR_FORMAT_P010;
382 break;
383 default:
384 return kUnimplemented;
385 }
386
387 crabbyavif::avifResult result =
388 crabbyavif::avifDecoderNthImage(fAvifDecoder.get(), options.fFrameIndex);
389 if (result != crabbyavif::AVIF_RESULT_OK) {
390 return kInvalidInput;
391 }
392 if (fGainmapOnly && !fAvifDecoder->image->gainMap) {
393 return kInvalidInput;
394 }
395
396 // At this point we have the decoded image. Now we have to perform cropping, subset computation
397 // and scaling. The right order of these operations is:
398 // 1) Cropping (as described by the CleanAperture property). This has to be the first step to
399 // ensure that we don't accidentally expose the non-cropped portions of the image to the
400 // subsequent operations.
401 // 2) Subset computation (as requested by options.fSubset).
402 // 3) Scaling (to match dstInfo.dimensions() if necessary). This has to be the last step to
403 // ensure that we never fill in more pixels than what is requested by dstInfo.dimensions().
404
405 crabbyavif::avifImage* image =
406 fGainmapOnly ? fAvifDecoder->image->gainMap->image : fAvifDecoder->image;
407 using AvifImagePtr =
408 std::unique_ptr<crabbyavif::avifImage, decltype(&crabbyavif::crabby_avifImageDestroy)>;
409
410 // cropped_image is a view into the underlying image. It can be safely deleted once the pixels
411 // are converted into RGB (or when it goes out of scope in one of the error paths).
412 AvifImagePtr cropped_image{nullptr, crabbyavif::crabby_avifImageDestroy};
413 if (image->transformFlags & crabbyavif::AVIF_TRANSFORM_CLAP) {
414 crabbyavif::avifCropRect rect;
415 if (crabbyavif::crabby_avifCropRectConvertCleanApertureBox(
416 &rect, &image->clap, image->width, image->height, image->yuvFormat, nullptr)) {
417 cropped_image.reset(crabbyavif::crabby_avifImageCreateEmpty());
418 result = crabbyavif::crabby_avifImageSetViewRect(cropped_image.get(), image, &rect);
419 if (result != crabbyavif::AVIF_RESULT_OK) {
420 return kInvalidInput;
421 }
422 image = cropped_image.get();
423 }
424 }
425
426 AvifImagePtr subset_image{nullptr, crabbyavif::crabby_avifImageDestroy};
427 if (options.fSubset) {
428 const crabbyavif::avifCropRect rect{
429 .x = static_cast<uint32_t>(options.fSubset->x()),
430 .y = static_cast<uint32_t>(options.fSubset->y()),
431 .width = static_cast<uint32_t>(options.fSubset->width()),
432 .height = static_cast<uint32_t>(options.fSubset->height())};
433 subset_image.reset(crabbyavif::crabby_avifImageCreateEmpty());
434 result = crabbyavif::crabby_avifImageSetViewRect(subset_image.get(), image, &rect);
435 if (result != crabbyavif::AVIF_RESULT_OK) {
436 return kInvalidInput;
437 }
438 image = subset_image.get();
439 }
440
441 AvifImagePtr scaled_image{nullptr, crabbyavif::crabby_avifImageDestroy};
442 if (dstInfo.width() != image->width || dstInfo.height() != image->height) {
443 // |image| contains plane pointers which point to Android MediaCodec's buffers. Those
444 // buffers are read-only and hence we cannot scale in place. Make a copy of the image and
445 // scale the copied image.
446 scaled_image.reset(crabbyavif::crabby_avifImageCreateEmpty());
447 result = crabbyavif::crabby_avifImageCopy(
448 scaled_image.get(), image, crabbyavif::AVIF_PLANES_ALL);
449 if (result != crabbyavif::AVIF_RESULT_OK) {
450 return kInvalidInput;
451 }
452 image = scaled_image.get();
453 result = crabbyavif::avifImageScale(
454 image, dstInfo.width(), dstInfo.height(), &fAvifDecoder->diag);
455 if (result != crabbyavif::AVIF_RESULT_OK) {
456 return kInvalidInput;
457 }
458 }
459
460 crabbyavif::avifRGBImage rgbImage;
461 crabbyavif::avifRGBImageSetDefaults(&rgbImage, image);
462
463 switch (dstInfo.colorType()) {
464 case kRGBA_8888_SkColorType:
465 rgbImage.depth = 8;
466 break;
467 case kRGBA_F16_SkColorType:
468 rgbImage.depth = 16;
469 rgbImage.isFloat = crabbyavif::CRABBY_AVIF_TRUE;
470 break;
471 case kRGBA_1010102_SkColorType:
472 rgbImage.depth = 10;
473 rgbImage.format = crabbyavif::AVIF_RGB_FORMAT_RGBA1010102;
474 break;
475 case kRGB_565_SkColorType:
476 rgbImage.depth = 8;
477 rgbImage.format = crabbyavif::AVIF_RGB_FORMAT_RGB565;
478 break;
479 default:
480 // Not reached because of the checks in conversionSupported().
481 return kUnimplemented;
482 }
483
484 rgbImage.pixels = static_cast<uint8_t*>(dst);
485 rgbImage.rowBytes = dstRowBytes;
486 rgbImage.chromaUpsampling = crabbyavif::AVIF_CHROMA_UPSAMPLING_FASTEST;
487
488 result = crabbyavif::avifImageYUVToRGB(image, &rgbImage);
489 if (result != crabbyavif::AVIF_RESULT_OK) {
490 return kInvalidInput;
491 }
492
493 *rowsDecoded = image->height;
494 return kSuccess;
495 }
496
onGetGainmapCodec(SkGainmapInfo * info,std::unique_ptr<SkCodec> * gainmapCodec)497 bool SkCrabbyAvifCodec::onGetGainmapCodec(SkGainmapInfo* info,
498 std::unique_ptr<SkCodec>* gainmapCodec) {
499 if (!gainmapCodec || !info || !fAvifDecoder->image || !fAvifDecoder->image->gainMap ||
500 !PopulateGainmapInfo(*fAvifDecoder->image->gainMap, *fAvifDecoder->image, info)) {
501 return false;
502 }
503 Result result;
504 *gainmapCodec = SkCrabbyAvifCodec::MakeFromData(
505 /*stream=*/nullptr, fData, &result, /*gainmapOnly=*/true);
506 return static_cast<bool>(*gainmapCodec);
507 }
508
509 namespace SkAvifDecoder {
510 namespace CrabbyAvif {
511
IsAvif(const void * data,size_t len)512 bool IsAvif(const void* data, size_t len) { return SkCrabbyAvifCodec::IsAvif(data, len); }
513
Decode(std::unique_ptr<SkStream> stream,SkCodec::Result * outResult,SkCodecs::DecodeContext)514 std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream,
515 SkCodec::Result* outResult,
516 SkCodecs::DecodeContext) {
517 SkCodec::Result resultStorage;
518 if (!outResult) {
519 outResult = &resultStorage;
520 }
521 return SkCrabbyAvifCodec::MakeFromStream(std::move(stream), outResult);
522 }
523
Decode(sk_sp<SkData> data,SkCodec::Result * outResult,SkCodecs::DecodeContext)524 std::unique_ptr<SkCodec> Decode(sk_sp<SkData> data,
525 SkCodec::Result* outResult,
526 SkCodecs::DecodeContext) {
527 if (!data) {
528 if (outResult) {
529 *outResult = SkCodec::kInvalidInput;
530 }
531 return nullptr;
532 }
533 return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr);
534 }
535
536 } // namespace CrabbyAvif
537 } // namespace SkAvifDecoder
538