/* * Copyright 2017 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/SkHeifCodec.h"
bool SkHeifCodec::IsSupported(constvoid* buffer, size_t bytesRead,
SkEncodedImageFormat* format) { // Parse the ftyp box up to bytesRead to determine if this is HEIF or AVIF. // Any valid ftyp box should have at least 8 bytes. if (bytesRead < 8) { returnfalse;
}
if (chunkType != FOURCC('f', 't', 'y', 'p')) { returnfalse;
}
int64_t offset = 8; if (chunkSize == 1) { // This indicates that the next 8 bytes represent the chunk size, // and chunk data comes after that. if (bytesRead < 16) { returnfalse;
} auto* chunkSizePtr = SkTAddOffset<const uint64_t>(buffer, offset);
chunkSize = SkEndian_SwapBE64(*chunkSizePtr); if (chunkSize < 16) { // The smallest valid chunk is 16 bytes long in this case. returnfalse;
}
offset += 8;
} elseif (chunkSize < 8) { // The smallest valid chunk is 8 bytes long. returnfalse;
}
if (chunkSize > bytesRead) {
chunkSize = bytesRead;
}
int64_t chunkDataSize = chunkSize - offset; // It should at least have major brand (4-byte) and minor version (4-bytes). // The rest of the chunk (if any) is a list of (4-byte) compatible brands. if (chunkDataSize < 8) { returnfalse;
}
uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4; bool isHeif = false; for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { if (i == 1) { // Skip this index, it refers to the minorVersion, // not a brand. continue;
} auto* brandPtr = SkTAddOffset<const uint32_t>(buffer, offset + 4 * i);
uint32_t brand = SkEndian_SwapBE32(*brandPtr); if (brand == FOURCC('m', 'i', 'f', '1') || brand == FOURCC('h', 'e', 'i', 'c')
|| brand == FOURCC('m', 's', 'f', '1') || brand == FOURCC('h', 'e', 'v', 'c')
|| brand == FOURCC('a', 'v', 'i', 'f') || brand == FOURCC('a', 'v', 'i', 's')) { // AVIF files could have "mif1" as the major brand. So we cannot // distinguish whether the image is AVIF or HEIC just based on the // "mif1" brand. So wait until we see a specific avif brand to // determine whether it is AVIF or HEIC.
isHeif = true; if (brand == FOURCC('a', 'v', 'i', 'f')
|| brand == FOURCC('a', 'v', 'i', 's')) { if (format != nullptr) {
*format = SkEncodedImageFormat::kAVIF;
} returntrue;
}
}
} if (isHeif) { if (format != nullptr) {
*format = SkEncodedImageFormat::kHEIF;
} returntrue;
} returnfalse;
}
static SkEncodedOrigin get_orientation(const HeifFrameInfo& frameInfo) { switch (frameInfo.mRotationAngle) { case 0: return kTopLeft_SkEncodedOrigin; case 90: return kRightTop_SkEncodedOrigin; case 180: return kBottomRight_SkEncodedOrigin; case 270: return kLeftBottom_SkEncodedOrigin;
} return kDefault_SkEncodedOrigin;
}
constexpr size_t bytesToRead = MinBufferedBytesNeeded(); char buffer[bytesToRead];
size_t bytesRead = stream->peek(buffer, bytesToRead); if (0 == bytesRead) { // It is possible the stream does not support peeking, but does support rewinding. // Attempt to read() and pass the actual amount read to the decoder.
bytesRead = stream->read(buffer, bytesToRead); if (!stream->rewind()) {
SkCodecPrintf("Encoded image data could not peek or rewind to determine format!\n");
*result = kCouldNotRewind; return nullptr;
}
}
SkEncodedImageFormat format; if (!SkHeifCodec::IsSupported(buffer, bytesRead, &format)) {
SkCodecPrintf("Failed to get format despite earlier detecting it");
*result = SkCodec::kInternalError; return nullptr;
}
std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr; if (heifInfo.mIccData.size() > 0) { auto iccData = new std::vector<uint8_t>(std::move(heifInfo.mIccData)); auto icc = SkData::MakeWithProc(iccData->data(), iccData->size(), releaseProc, iccData);
profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
} if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) { // This will result in sRGB.
profile = nullptr;
}
if (kUnknown_SkAlphaType == dstInfo.alphaType()) { returnfalse;
}
if (kOpaque_SkAlphaType != dstInfo.alphaType()) {
SkCodecPrintf("Warning: an opaque image should be decoded as opaque " "- it is being decoded as non-opaque, which will draw slower\n");
}
case kBGRA_8888_SkColorType:
this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
case kRGB_565_SkColorType:
this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); if (needsColorXform) { return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
} else { return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
}
case kRGBA_1010102_SkColorType:
this->setSrcXformFormat(skcms_PixelFormat_RGBA_1010102); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_1010102);
case kRGBA_F16_SkColorType:
SkASSERT(needsColorXform); if (srcIsOpaque && colorDepth == 10) {
this->setSrcXformFormat(skcms_PixelFormat_RGBA_1010102); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_1010102);
} else {
this->setSrcXformFormat(skcms_PixelFormat_RGBA_8888); return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
}
default: returnfalse;
}
}
int SkHeifCodec::readRows(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, int count, const Options& opts) { // When fSwizzleSrcRow is non-null, it means that we need to swizzle. In this case, // we will always decode into fSwizzlerSrcRow before swizzling into the next buffer. // We can never swizzle "in place" because the swizzler may perform sampling and/or // subsetting. // When fColorXformSrcRow is non-null, it means that we need to color xform and that // we cannot color xform "in place" (many times we can, but not when the dst is F16). // In this case, we will color xform from fColorXformSrcRow into the dst.
uint8_t* decodeDst = (uint8_t*) dst;
uint32_t* swizzleDst = (uint32_t*) dst;
size_t decodeDstRowBytes = rowBytes;
size_t swizzleDstRowBytes = rowBytes; int dstWidth = opts.fSubset ? opts.fSubset->width() : dstInfo.width(); if (fSwizzleSrcRow && fColorXformSrcRow) {
decodeDst = fSwizzleSrcRow;
swizzleDst = fColorXformSrcRow;
decodeDstRowBytes = 0;
swizzleDstRowBytes = 0;
dstWidth = fSwizzler->swizzleWidth();
} elseif (fColorXformSrcRow) {
decodeDst = (uint8_t*) fColorXformSrcRow;
swizzleDst = fColorXformSrcRow;
decodeDstRowBytes = 0;
swizzleDstRowBytes = 0;
} elseif (fSwizzleSrcRow) {
decodeDst = fSwizzleSrcRow;
decodeDstRowBytes = 0;
dstWidth = fSwizzler->swizzleWidth();
}
for (int y = 0; y < count; y++) { if (!fHeifDecoder->getScanline(decodeDst)) { return y;
}
if (fSwizzler) {
fSwizzler->swizzle(swizzleDst, decodeDst);
}
int SkHeifCodec::onGetFrameCount() { if (!fUseAnimation) { return 1;
}
if (fFrameHolder.size() == 0) {
size_t frameCount;
HeifFrameInfo frameInfo; if (!fHeifDecoder->getSequenceInfo(&frameInfo, &frameCount)
|| frameCount <= 1) {
fUseAnimation = false; return 1;
}
fFrameHolder.reserve(frameCount); for (size_t i = 0; i < frameCount; i++) {
Frame* frame = fFrameHolder.appendNewFrame();
frame->setXYWH(0, 0, frameInfo.mWidth, frameInfo.mHeight);
frame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep); // Currently we don't know the duration until the frame is actually // decoded (onGetFrameInfo is also called before frame is decoded). // For now, fill it base on the value reported for the sequence.
frame->setDuration(frameInfo.mDurationUs / 1000);
frame->setRequiredFrame(SkCodec::kNoFrame);
frame->setHasAlpha(false);
}
}
SkHeifCodec::Frame* SkHeifCodec::FrameHolder::appendNewFrame() { constint i = this->size();
fFrames.emplace_back(i); // TODO: need to handle frame duration here return &fFrames[i];
}
bool SkHeifCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { if (i >= fFrameHolder.size()) { returnfalse;
}
const Frame* frame = fFrameHolder.frame(i); if (!frame) { returnfalse;
}
if (frameInfo) {
frame->fillIn(frameInfo, true);
}
returntrue;
}
int SkHeifCodec::onGetRepetitionCount() { return kRepetitionCountInfinite;
}
/* * Performs the heif decode
*/
SkCodec::Result SkHeifCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& options, int* rowsDecoded) { if (options.fSubset) { // Not supporting subsets on this path for now. // TODO: if the heif has tiles, we can support subset here, but // need to retrieve tile config from metadata retriever first. return kUnimplemented;
}
SkCodec::Result SkHeifCodec::onStartScanlineDecode( const SkImageInfo& dstInfo, const Options& options) { // TODO: For now, just decode the whole thing even when there is a subset. // If the heif image has tiles, we could potentially do this much faster, // but the tile configuration needs to be retrieved from the metadata. if (!fHeifDecoder->decode(&fFrameInfo)) { return kInvalidInput;
}
if (options.fSubset) {
this->initializeSwizzler(dstInfo, options);
} else {
fSwizzler.reset(nullptr);
}
this->allocateStorage(dstInfo);
return kSuccess;
}
int SkHeifCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { return this->readRows(this->dstInfo(), dst, dstRowBytes, count, this->options());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.