blob: 27e091a5370dd3d1fa72fc7363232cb1de2d83a7 [file] [log] [blame]
/*
* Copyright 2022 Google Inc.
*
* 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/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/core/SkStreamPriv.h"
#include <cstdint>
#include <cstring>
#include <utility>
#include "avif/avif.h"
#include "avif/internal.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) {
AvifDecoder avifDecoder(avifDecoderCreate());
if (avifDecoder == nullptr) {
*result = 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 = SkCopyStreamToData(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 = kInternalError;
return nullptr;
}
res = avifDecoderParse(avifDecoder.get());
if (res != AVIF_RESULT_OK) {
*result = kInvalidInput;
return nullptr;
}
std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
// 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 {
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<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::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 != kRGBA_8888_SkColorType && dstColorType != kRGBA_F16_SkColorType) {
// TODO(vigneshv): Check if more color types need to be supported.
// Currently android supports at least RGB565 and BGRA8888 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()) {
if (!avifImageScale(fAvifDecoder->image,
dstInfo.width(),
dstInfo.height(),
fAvifDecoder->imageSizeLimit,
&fAvifDecoder->diag)) {
return kInvalidInput;
}
}
avifRGBImage rgbImage;
avifRGBImageSetDefaults(&rgbImage, fAvifDecoder->image);
if (dstColorType == kRGBA_8888_SkColorType) {
rgbImage.depth = 8;
} 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;
}