| /* | |
| * Copyright 2022 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/codec/SkAvifCodec.h" | |
| #include "include/codec/SkAvifDecoder.h" | |
| #include "include/codec/SkCodec.h" | |
| #include "include/codec/SkCodecAnimation.h" | |
| #include "include/core/SkColorType.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkTypes.h" | |
| #include "modules/skcms/skcms.h" | |
| #include "src/codec/SkCodecPriv.h" | |
| #include "src/core/SkRectMemcpy.h" | |
| #include "src/core/SkStreamPriv.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <utility> | |
| #include "avif/avif.h" | |
| void AvifDecoderDeleter::operator()(avifDecoder* decoder) const { | |
| if (decoder != nullptr) { | |
| avifDecoderDestroy(decoder); | |
| } | |
| } | |
| bool SkAvifCodec::IsAvif(const void* buffer, size_t bytesRead) { | |
| avifROData avifData = {static_cast<const uint8_t*>(buffer), bytesRead}; | |
| bool isAvif = avifPeekCompatibleFileType(&avifData) == AVIF_TRUE; | |
| if (isAvif) return true; | |
| // Peeking sometimes fails if the ftyp box is too large. Check the signature | |
| // just to be sure. | |
| const char* bytes = static_cast<const char*>(buffer); | |
| isAvif = bytesRead >= 12 && !memcmp(&bytes[4], "ftyp", 4) && | |
| (!memcmp(&bytes[8], "avif", 4) || !memcmp(&bytes[8], "avis", 4)); | |
| return isAvif; | |
| } | |
| std::unique_ptr<SkCodec> SkAvifCodec::MakeFromStream(std::unique_ptr<SkStream> stream, | |
| Result* result) { | |
| SkASSERT(result); | |
| if (!stream) { | |
| *result = SkCodec::kInvalidInput; | |
| return nullptr; | |
| } | |
| AvifDecoder avifDecoder(avifDecoderCreate()); | |
| if (avifDecoder == nullptr) { | |
| *result = SkCodec::kInternalError; | |
| return nullptr; | |
| } | |
| avifDecoder->ignoreXMP = AVIF_TRUE; | |
| avifDecoder->ignoreExif = AVIF_TRUE; | |
| avifDecoder->allowProgressive = AVIF_FALSE; | |
| avifDecoder->allowIncremental = AVIF_FALSE; | |
| avifDecoder->strictFlags = AVIF_STRICT_DISABLED; | |
| // TODO(vigneshv): Enable threading based on number of CPU cores available. | |
| avifDecoder->maxThreads = 1; | |
| // libavif needs a contiguous data buffer. | |
| sk_sp<SkData> data = nullptr; | |
| if (stream->getMemoryBase()) { | |
| // It is safe to make without copy because we'll hold onto the stream. | |
| data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); | |
| } else { | |
| data = SkStreamPriv::CopyStreamToData(stream.get()); | |
| // If we are forced to copy the stream to a data, we can go ahead and | |
| // delete the stream. | |
| stream.reset(nullptr); | |
| } | |
| avifResult res = avifDecoderSetIOMemory(avifDecoder.get(), data->bytes(), data->size()); | |
| if (res != AVIF_RESULT_OK) { | |
| *result = SkCodec::kInternalError; | |
| return nullptr; | |
| } | |
| res = avifDecoderParse(avifDecoder.get()); | |
| if (res != AVIF_RESULT_OK) { | |
| *result = SkCodec::kInvalidInput; | |
| return nullptr; | |
| } | |
| std::unique_ptr<SkCodecs::ColorProfile> profile; | |
| // TODO(vigneshv): Get ICC Profile from the avif decoder. | |
| const int bitsPerComponent = avifDecoder->image->depth > 8 ? 16 : 8; | |
| SkEncodedInfo::Color color; | |
| SkEncodedInfo::Alpha alpha; | |
| if (avifDecoder->alphaPresent) { | |
| color = SkEncodedInfo::kRGBA_Color; | |
| alpha = SkEncodedInfo::kUnpremul_Alpha; | |
| } else if (avifDecoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { | |
| color = SkEncodedInfo::kGray_Color; | |
| alpha = SkEncodedInfo::kOpaque_Alpha; | |
| } else { | |
| color = SkEncodedInfo::kRGB_Color; | |
| alpha = SkEncodedInfo::kOpaque_Alpha; | |
| } | |
| SkEncodedInfo info = SkEncodedInfo::Make(avifDecoder->image->width, | |
| avifDecoder->image->height, | |
| color, | |
| alpha, | |
| bitsPerComponent, | |
| std::move(profile), | |
| avifDecoder->image->depth); | |
| bool animation = avifDecoder->imageCount > 1; | |
| *result = kSuccess; | |
| return std::unique_ptr<SkCodec>(new SkAvifCodec(std::move(info), | |
| std::move(stream), | |
| std::move(data), | |
| std::move(avifDecoder), | |
| kDefault_SkEncodedOrigin, | |
| animation)); | |
| } | |
| SkAvifCodec::SkAvifCodec(SkEncodedInfo&& info, | |
| std::unique_ptr<SkStream> stream, | |
| sk_sp<const SkData> data, | |
| AvifDecoder avifDecoder, | |
| SkEncodedOrigin origin, | |
| bool useAnimation) | |
| : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, std::move(stream), origin) | |
| , fData(std::move(data)) | |
| , fAvifDecoder(std::move(avifDecoder)) | |
| , fUseAnimation(useAnimation) {} | |
| int SkAvifCodec::onGetFrameCount() { | |
| if (!fUseAnimation) { | |
| return 1; | |
| } | |
| if (fFrameHolder.size() == 0) { | |
| if (fAvifDecoder->imageCount <= 1) { | |
| fUseAnimation = false; | |
| return 1; | |
| } | |
| fFrameHolder.reserve(fAvifDecoder->imageCount); | |
| for (int i = 0; i < fAvifDecoder->imageCount; i++) { | |
| Frame* frame = fFrameHolder.appendNewFrame(fAvifDecoder->alphaPresent == AVIF_TRUE); | |
| frame->setXYWH(0, 0, fAvifDecoder->image->width, fAvifDecoder->image->height); | |
| frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep); | |
| avifImageTiming timing; | |
| avifDecoderNthImageTiming(fAvifDecoder.get(), i, &timing); | |
| frame->setDuration(timing.duration * 1000); | |
| frame->setRequiredFrame(SkCodec::kNoFrame); | |
| frame->setHasAlpha(fAvifDecoder->alphaPresent == AVIF_TRUE); | |
| } | |
| } | |
| return fFrameHolder.size(); | |
| } | |
| const SkFrame* SkAvifCodec::FrameHolder::onGetFrame(int i) const { | |
| return static_cast<const SkFrame*>(this->frame(i)); | |
| } | |
| SkAvifCodec::Frame* SkAvifCodec::FrameHolder::appendNewFrame(bool hasAlpha) { | |
| const int i = this->size(); | |
| fFrames.emplace_back(i, | |
| hasAlpha ? SkEncodedInfo::kUnpremul_Alpha : SkEncodedInfo::kOpaque_Alpha); | |
| return &fFrames[i]; | |
| } | |
| const SkAvifCodec::Frame* SkAvifCodec::FrameHolder::frame(int i) const { | |
| SkASSERT(i >= 0 && i < this->size()); | |
| return &fFrames[i]; | |
| } | |
| bool SkAvifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { | |
| if (i >= fFrameHolder.size()) { | |
| return false; | |
| } | |
| const Frame* frame = fFrameHolder.frame(i); | |
| if (!frame) { | |
| return false; | |
| } | |
| if (frameInfo) { | |
| frame->fillIn(frameInfo, true); | |
| } | |
| return true; | |
| } | |
| int SkAvifCodec::onGetRepetitionCount() { return kRepetitionCountInfinite; } | |
| SkCodec::IsAnimated SkAvifCodec::onIsAnimated() { | |
| if (!fUseAnimation || fAvifDecoder->imageCount <= 1) { | |
| return IsAnimated::kNo; | |
| } | |
| return IsAnimated::kYes; | |
| } | |
| SkCodec::Result SkAvifCodec::onGetPixels(const SkImageInfo& dstInfo, | |
| void* dst, | |
| size_t dstRowBytes, | |
| const Options& options, | |
| int* rowsDecoded) { | |
| if (options.fSubset) { | |
| return kUnimplemented; | |
| } | |
| const SkColorType dstColorType = dstInfo.colorType(); | |
| if (dstColorType == kGray_8_SkColorType && | |
| this->getEncodedInfo().color() != SkEncodedInfo::kGray_Color) { | |
| return kInvalidConversion; | |
| } | |
| if (dstColorType != kRGBA_8888_SkColorType | |
| && dstColorType != kBGRA_8888_SkColorType | |
| && dstColorType != kRGBA_F16_SkColorType | |
| && dstColorType != kGray_8_SkColorType) { | |
| // TODO(vigneshv): Check if more color types need to be supported. | |
| // Currently android supports at least RGB565 which is not | |
| // supported here. | |
| return kUnimplemented; | |
| } | |
| avifResult result = avifDecoderNthImage(fAvifDecoder.get(), options.fFrameIndex); | |
| if (result != AVIF_RESULT_OK) { | |
| return kInvalidInput; | |
| } | |
| if (this->dimensions() != dstInfo.dimensions()) { | |
| result = avifImageScale( | |
| fAvifDecoder->image, dstInfo.width(), dstInfo.height(), &fAvifDecoder->diag); | |
| if (result != AVIF_RESULT_OK) { | |
| return kInvalidInput; | |
| } | |
| } | |
| if (dstColorType == kGray_8_SkColorType) { | |
| if (fAvifDecoder->image->depth != 8) { | |
| return kInvalidConversion; | |
| } | |
| const uint8_t* srcRow = fAvifDecoder->image->yuvPlanes[AVIF_CHAN_Y]; | |
| size_t srcRowBytes = fAvifDecoder->image->yuvRowBytes[AVIF_CHAN_Y]; | |
| SkRectMemcpy(dst, dstRowBytes, srcRow, srcRowBytes, dstInfo.width(), dstInfo.height()); | |
| if (rowsDecoded) { | |
| *rowsDecoded = dstInfo.height(); | |
| } | |
| return kSuccess; | |
| } | |
| avifRGBImage rgbImage; | |
| avifRGBImageSetDefaults(&rgbImage, fAvifDecoder->image); | |
| if (dstColorType == kRGBA_8888_SkColorType) { | |
| rgbImage.depth = 8; | |
| } else if (dstColorType == kBGRA_8888_SkColorType){ | |
| rgbImage.depth = 8; | |
| rgbImage.format = AVIF_RGB_FORMAT_BGRA; | |
| } else if (dstColorType == kRGBA_F16_SkColorType) { | |
| rgbImage.depth = 16; | |
| rgbImage.isFloat = AVIF_TRUE; | |
| } | |
| rgbImage.pixels = static_cast<uint8_t*>(dst); | |
| rgbImage.rowBytes = dstRowBytes; | |
| rgbImage.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST; | |
| result = avifImageYUVToRGB(fAvifDecoder->image, &rgbImage); | |
| if (result != AVIF_RESULT_OK) { | |
| return kInvalidInput; | |
| } | |
| *rowsDecoded = fAvifDecoder->image->height; | |
| return kSuccess; | |
| } | |
| namespace SkAvifDecoder { | |
| namespace LibAvif { | |
| bool IsAvif(const void* data, size_t len) { | |
| return SkAvifCodec::IsAvif(data, len); | |
| } | |
| std::unique_ptr<SkCodec> Decode(std::unique_ptr<SkStream> stream, | |
| SkCodec::Result* outResult, | |
| SkCodecs::DecodeContext) { | |
| SkCodec::Result resultStorage; | |
| if (!outResult) { | |
| outResult = &resultStorage; | |
| } | |
| return SkAvifCodec::MakeFromStream(std::move(stream), outResult); | |
| } | |
| std::unique_ptr<SkCodec> Decode(sk_sp<const SkData> data, | |
| SkCodec::Result* outResult, | |
| SkCodecs::DecodeContext) { | |
| if (!data) { | |
| if (outResult) { | |
| *outResult = SkCodec::kInvalidInput; | |
| } | |
| return nullptr; | |
| } | |
| return Decode(SkMemoryStream::Make(std::move(data)), outResult, nullptr); | |
| } | |
| } // namespace LibAvif | |
| } // namespace SkAvifDecoder |