• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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