123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- /*
- * 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 "include/core/SkTypes.h"
- #ifdef SK_HAS_HEIF_LIBRARY
- #include "include/codec/SkCodec.h"
- #include "include/core/SkStream.h"
- #include "include/private/SkColorData.h"
- #include "src/codec/SkCodecPriv.h"
- #include "src/codec/SkHeifCodec.h"
- #include "src/core/SkEndian.h"
- #define FOURCC(c1, c2, c3, c4) \
- ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
- bool SkHeifCodec::IsHeif(const void* buffer, size_t bytesRead) {
- // Parse the ftyp box up to bytesRead to determine if this is HEIF.
- // Any valid ftyp box should have at least 8 bytes.
- if (bytesRead < 8) {
- return false;
- }
- uint32_t* ptr = (uint32_t*)buffer;
- uint64_t chunkSize = SkEndian_SwapBE32(ptr[0]);
- uint32_t chunkType = SkEndian_SwapBE32(ptr[1]);
- if (chunkType != FOURCC('f', 't', 'y', 'p')) {
- return false;
- }
- 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) {
- return false;
- }
- 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.
- return false;
- }
- offset += 8;
- } else if (chunkSize < 8) {
- // The smallest valid chunk is 8 bytes long.
- return false;
- }
- 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) {
- return false;
- }
- uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4;
- 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')) {
- return true;
- }
- }
- return false;
- }
- 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;
- }
- struct SkHeifStreamWrapper : public HeifStream {
- SkHeifStreamWrapper(SkStream* stream) : fStream(stream) {}
- ~SkHeifStreamWrapper() override {}
- size_t read(void* buffer, size_t size) override {
- return fStream->read(buffer, size);
- }
- bool rewind() override {
- return fStream->rewind();
- }
- bool seek(size_t position) override {
- return fStream->seek(position);
- }
- bool hasLength() const override {
- return fStream->hasLength();
- }
- size_t getLength() const override {
- return fStream->getLength();
- }
- private:
- std::unique_ptr<SkStream> fStream;
- };
- std::unique_ptr<SkCodec> SkHeifCodec::MakeFromStream(
- std::unique_ptr<SkStream> stream, Result* result) {
- std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder());
- if (heifDecoder.get() == nullptr) {
- *result = kInternalError;
- return nullptr;
- }
- HeifFrameInfo frameInfo;
- if (!heifDecoder->init(new SkHeifStreamWrapper(stream.release()),
- &frameInfo)) {
- *result = kInvalidInput;
- return nullptr;
- }
- std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr;
- if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
- // FIXME: Would it be possible to use MakeWithoutCopy?
- auto icc = SkData::MakeWithCopy(frameInfo.mIccData.get(), frameInfo.mIccSize);
- profile = SkEncodedInfo::ICCProfile::Make(std::move(icc));
- }
- if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) {
- // This will result in sRGB.
- profile = nullptr;
- }
- SkEncodedInfo info = SkEncodedInfo::Make(frameInfo.mWidth, frameInfo.mHeight,
- SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8, std::move(profile));
- SkEncodedOrigin orientation = get_orientation(frameInfo);
- *result = kSuccess;
- return std::unique_ptr<SkCodec>(new SkHeifCodec(std::move(info), heifDecoder.release(),
- orientation));
- }
- SkHeifCodec::SkHeifCodec(SkEncodedInfo&& info, HeifDecoder* heifDecoder, SkEncodedOrigin origin)
- : INHERITED(std::move(info), skcms_PixelFormat_RGBA_8888, nullptr, origin)
- , fHeifDecoder(heifDecoder)
- , fSwizzleSrcRow(nullptr)
- , fColorXformSrcRow(nullptr)
- {}
- bool SkHeifCodec::conversionSupported(const SkImageInfo& dstInfo, bool srcIsOpaque,
- bool needsColorXform) {
- SkASSERT(srcIsOpaque);
- if (kUnknown_SkAlphaType == dstInfo.alphaType()) {
- return false;
- }
- 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");
- }
- switch (dstInfo.colorType()) {
- case kRGBA_8888_SkColorType:
- return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
- case kBGRA_8888_SkColorType:
- return fHeifDecoder->setOutputColor(kHeifColorFormat_BGRA_8888);
- case kRGB_565_SkColorType:
- if (needsColorXform) {
- return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
- } else {
- return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
- }
- case kRGBA_F16_SkColorType:
- SkASSERT(needsColorXform);
- return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
- default:
- return false;
- }
- }
- 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();
- } else if (fColorXformSrcRow) {
- decodeDst = (uint8_t*) fColorXformSrcRow;
- swizzleDst = fColorXformSrcRow;
- decodeDstRowBytes = 0;
- swizzleDstRowBytes = 0;
- } else if (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);
- }
- if (this->colorXform()) {
- this->applyColorXform(dst, swizzleDst, dstWidth);
- dst = SkTAddOffset<void>(dst, rowBytes);
- }
- decodeDst = SkTAddOffset<uint8_t>(decodeDst, decodeDstRowBytes);
- swizzleDst = SkTAddOffset<uint32_t>(swizzleDst, swizzleDstRowBytes);
- }
- return count;
- }
- /*
- * 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;
- }
- if (!fHeifDecoder->decode(&fFrameInfo)) {
- return kInvalidInput;
- }
- fSwizzler.reset(nullptr);
- this->allocateStorage(dstInfo);
- int rows = this->readRows(dstInfo, dst, dstRowBytes, dstInfo.height(), options);
- if (rows < dstInfo.height()) {
- *rowsDecoded = rows;
- return kIncompleteInput;
- }
- return kSuccess;
- }
- void SkHeifCodec::allocateStorage(const SkImageInfo& dstInfo) {
- int dstWidth = dstInfo.width();
- size_t swizzleBytes = 0;
- if (fSwizzler) {
- swizzleBytes = fFrameInfo.mBytesPerPixel * fFrameInfo.mWidth;
- dstWidth = fSwizzler->swizzleWidth();
- SkASSERT(!this->colorXform() || SkIsAlign4(swizzleBytes));
- }
- size_t xformBytes = 0;
- if (this->colorXform() && (kRGBA_F16_SkColorType == dstInfo.colorType() ||
- kRGB_565_SkColorType == dstInfo.colorType())) {
- xformBytes = dstWidth * sizeof(uint32_t);
- }
- size_t totalBytes = swizzleBytes + xformBytes;
- fStorage.reset(totalBytes);
- if (totalBytes > 0) {
- fSwizzleSrcRow = (swizzleBytes > 0) ? fStorage.get() : nullptr;
- fColorXformSrcRow = (xformBytes > 0) ?
- SkTAddOffset<uint32_t>(fStorage.get(), swizzleBytes) : nullptr;
- }
- }
- void SkHeifCodec::initializeSwizzler(
- const SkImageInfo& dstInfo, const Options& options) {
- SkImageInfo swizzlerDstInfo = dstInfo;
- if (this->colorXform()) {
- // The color xform will be expecting RGBA 8888 input.
- swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
- }
- int srcBPP = 4;
- if (dstInfo.colorType() == kRGB_565_SkColorType && !this->colorXform()) {
- srcBPP = 2;
- }
- fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerDstInfo, options);
- SkASSERT(fSwizzler);
- }
- SkSampler* SkHeifCodec::getSampler(bool createIfNecessary) {
- if (!createIfNecessary || fSwizzler) {
- SkASSERT(!fSwizzler || (fSwizzleSrcRow && fStorage.get() == fSwizzleSrcRow));
- return fSwizzler.get();
- }
- this->initializeSwizzler(this->dstInfo(), this->options());
- this->allocateStorage(this->dstInfo());
- return fSwizzler.get();
- }
- bool SkHeifCodec::onRewind() {
- fSwizzler.reset(nullptr);
- fSwizzleSrcRow = nullptr;
- fColorXformSrcRow = nullptr;
- fStorage.reset();
- return true;
- }
- 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());
- }
- bool SkHeifCodec::onSkipScanlines(int count) {
- return count == (int) fHeifDecoder->skipScanlines(count);
- }
- #endif // SK_HAS_HEIF_LIBRARY
|