Revert "skia: add heif decoding support"
This reverts commit c2a954290d
.
Reason for revert: both Android and Google3 rolls cannot compile. Android cannot cast std::unique_ptr<T> to T*, Google3 cannot find HeifDecoderAPI.h.
Original change's description:
> skia: add heif decoding support
>
> Bug: b/64077740
> Change-Id: I11e0243bcc4c21c0aa5aa29a719dd0fcba7ae6f7
> Reviewed-on: https://skia-review.googlesource.com/35123
> Reviewed-by: Chong Zhang <chz@google.com>
> Reviewed-by: Leon Scroggins <scroggo@google.com>
> Commit-Queue: Chong Zhang <chz@google.com>
> Commit-Queue: Leon Scroggins <scroggo@google.com>
TBR=scroggo@google.com,chz@google.com
Change-Id: Id98f025e63daec50408186000453d1695170f7a8
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: b/64077740
Reviewed-on: https://skia-review.googlesource.com/35741
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
be1b071753
commit
db68a426b6
13
BUILD.gn
13
BUILD.gn
@ -31,7 +31,6 @@ declare_args() {
|
|||||||
skia_use_piex = !is_win
|
skia_use_piex = !is_win
|
||||||
skia_use_zlib = true
|
skia_use_zlib = true
|
||||||
skia_use_metal = false
|
skia_use_metal = false
|
||||||
skia_use_libheif = false
|
|
||||||
|
|
||||||
skia_android_serial = ""
|
skia_android_serial = ""
|
||||||
skia_enable_discrete_gpu = true
|
skia_enable_discrete_gpu = true
|
||||||
@ -533,17 +532,6 @@ optional("gpu") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
optional("heif") {
|
|
||||||
enabled = skia_use_libheif
|
|
||||||
public_defines = [ "SK_HAS_HEIF_LIBRARY" ]
|
|
||||||
|
|
||||||
deps = []
|
|
||||||
|
|
||||||
sources = [
|
|
||||||
"src/codec/SkHeifCodec.cpp",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
optional("jpeg") {
|
optional("jpeg") {
|
||||||
enabled = skia_use_libjpeg_turbo
|
enabled = skia_use_libjpeg_turbo
|
||||||
public_defines = [ "SK_HAS_JPEG_LIBRARY" ]
|
public_defines = [ "SK_HAS_JPEG_LIBRARY" ]
|
||||||
@ -667,7 +655,6 @@ component("skia") {
|
|||||||
":fontmgr_fontconfig",
|
":fontmgr_fontconfig",
|
||||||
":fontmgr_fuchsia",
|
":fontmgr_fuchsia",
|
||||||
":gpu",
|
":gpu",
|
||||||
":heif",
|
|
||||||
":jpeg",
|
":jpeg",
|
||||||
":none",
|
":none",
|
||||||
":pdf",
|
":pdf",
|
||||||
|
@ -31,7 +31,6 @@ tool_shared_libs = [
|
|||||||
'libicui18n',
|
'libicui18n',
|
||||||
'libexpat',
|
'libexpat',
|
||||||
'libft2',
|
'libft2',
|
||||||
'libheif',
|
|
||||||
'libdng_sdk',
|
'libdng_sdk',
|
||||||
'libpiex',
|
'libpiex',
|
||||||
'libcutils',
|
'libcutils',
|
||||||
@ -125,7 +124,6 @@ cc_library {
|
|||||||
"libdng_sdk",
|
"libdng_sdk",
|
||||||
"libexpat",
|
"libexpat",
|
||||||
"libft2",
|
"libft2",
|
||||||
"libheif",
|
|
||||||
"libicui18n",
|
"libicui18n",
|
||||||
"libicuuc",
|
"libicuuc",
|
||||||
"libjpeg",
|
"libjpeg",
|
||||||
@ -197,7 +195,6 @@ cc_test {
|
|||||||
gn_args = {
|
gn_args = {
|
||||||
'is_official_build': 'true',
|
'is_official_build': 'true',
|
||||||
'skia_enable_tools': 'true',
|
'skia_enable_tools': 'true',
|
||||||
'skia_use_libheif': 'true',
|
|
||||||
'skia_use_vulkan': 'true',
|
'skia_use_vulkan': 'true',
|
||||||
'target_cpu': '"none"',
|
'target_cpu': '"none"',
|
||||||
'target_os': '"android"',
|
'target_os': '"android"',
|
||||||
|
@ -50,7 +50,7 @@ public:
|
|||||||
* this many bytes, or by implementing rewind() to be able to rewind()
|
* this many bytes, or by implementing rewind() to be able to rewind()
|
||||||
* after reading this many bytes.
|
* after reading this many bytes.
|
||||||
*/
|
*/
|
||||||
static constexpr size_t MinBufferedBytesNeeded() { return 32; }
|
static size_t MinBufferedBytesNeeded();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error codes for various SkCodec methods.
|
* Error codes for various SkCodec methods.
|
||||||
|
@ -28,7 +28,6 @@ enum class SkEncodedImageFormat {
|
|||||||
kKTX,
|
kKTX,
|
||||||
kASTC,
|
kASTC,
|
||||||
kDNG,
|
kDNG,
|
||||||
kHEIF,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SkEncodedImageFormat_DEFINED
|
#endif // SkEncodedImageFormat_DEFINED
|
||||||
|
@ -32,7 +32,6 @@ SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create(
|
|||||||
case SkEncodedImageFormat::kJPEG:
|
case SkEncodedImageFormat::kJPEG:
|
||||||
case SkEncodedImageFormat::kPNG:
|
case SkEncodedImageFormat::kPNG:
|
||||||
case SkEncodedImageFormat::kWEBP:
|
case SkEncodedImageFormat::kWEBP:
|
||||||
case SkEncodedImageFormat::kHEIF:
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -83,9 +83,6 @@ std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<S
|
|||||||
case SkEncodedImageFormat::kGIF:
|
case SkEncodedImageFormat::kGIF:
|
||||||
case SkEncodedImageFormat::kBMP:
|
case SkEncodedImageFormat::kBMP:
|
||||||
case SkEncodedImageFormat::kWBMP:
|
case SkEncodedImageFormat::kWBMP:
|
||||||
#ifdef SK_HAS_HEIF_LIBRARY
|
|
||||||
case SkEncodedImageFormat::kHEIF:
|
|
||||||
#endif
|
|
||||||
return skstd::make_unique<SkSampledCodec>(codec.release());
|
return skstd::make_unique<SkSampledCodec>(codec.release());
|
||||||
#ifdef SK_HAS_WEBP_LIBRARY
|
#ifdef SK_HAS_WEBP_LIBRARY
|
||||||
case SkEncodedImageFormat::kWEBP:
|
case SkEncodedImageFormat::kWEBP:
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
#include "SkStream.h"
|
#include "SkStream.h"
|
||||||
#include "SkWbmpCodec.h"
|
#include "SkWbmpCodec.h"
|
||||||
#include "SkWebpCodec.h"
|
#include "SkWebpCodec.h"
|
||||||
#include "SkHeifCodec.h"
|
|
||||||
|
|
||||||
struct DecoderProc {
|
struct DecoderProc {
|
||||||
bool (*IsFormat)(const void*, size_t);
|
bool (*IsFormat)(const void*, size_t);
|
||||||
@ -42,12 +41,13 @@ static const DecoderProc gDecoderProcs[] = {
|
|||||||
{ SkIcoCodec::IsIco, SkIcoCodec::MakeFromStream },
|
{ SkIcoCodec::IsIco, SkIcoCodec::MakeFromStream },
|
||||||
#endif
|
#endif
|
||||||
{ SkBmpCodec::IsBmp, SkBmpCodec::MakeFromStream },
|
{ SkBmpCodec::IsBmp, SkBmpCodec::MakeFromStream },
|
||||||
{ SkWbmpCodec::IsWbmp, SkWbmpCodec::MakeFromStream },
|
{ SkWbmpCodec::IsWbmp, SkWbmpCodec::MakeFromStream }
|
||||||
#ifdef SK_HAS_HEIF_LIBRARY
|
|
||||||
{ SkHeifCodec::IsHeif, SkHeifCodec::MakeFromStream },
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t SkCodec::MinBufferedBytesNeeded() {
|
||||||
|
return WEBP_VP8_HEADER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<SkCodec> SkCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
std::unique_ptr<SkCodec> SkCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
|
||||||
Result* outResult, SkPngChunkReader* chunkReader) {
|
Result* outResult, SkPngChunkReader* chunkReader) {
|
||||||
Result resultStorage;
|
Result resultStorage;
|
||||||
@ -60,7 +60,9 @@ std::unique_ptr<SkCodec> SkCodec::MakeFromStream(std::unique_ptr<SkStream> strea
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t bytesToRead = MinBufferedBytesNeeded();
|
// 14 is enough to read all of the supported types.
|
||||||
|
const size_t bytesToRead = 14;
|
||||||
|
SkASSERT(bytesToRead <= MinBufferedBytesNeeded());
|
||||||
|
|
||||||
char buffer[bytesToRead];
|
char buffer[bytesToRead];
|
||||||
size_t bytesRead = stream->peek(buffer, bytesToRead);
|
size_t bytesRead = stream->peek(buffer, bytesToRead);
|
||||||
|
@ -1,397 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "SkCodec.h"
|
|
||||||
#include "SkCodecPriv.h"
|
|
||||||
#include "SkColorPriv.h"
|
|
||||||
#include "SkColorSpace_Base.h"
|
|
||||||
#include "SkStream.h"
|
|
||||||
#include "SkTypes.h"
|
|
||||||
#include "SkHeifCodec.h"
|
|
||||||
#include "SkEndian.h"
|
|
||||||
#include "HeifDecoderAPI.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
off64_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;
|
|
||||||
}
|
|
||||||
off64_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')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SkCodec> SkHeifCodec::MakeFromStream(
|
|
||||||
std::unique_ptr<SkStream> stream, Result* result) {
|
|
||||||
SkCodec* codec = nullptr;
|
|
||||||
|
|
||||||
*result = ReadHeader(stream, &codec);
|
|
||||||
if (kSuccess == *result) {
|
|
||||||
// Codec has taken ownership of the stream, we do not need to delete it
|
|
||||||
SkASSERT(codec);
|
|
||||||
stream.release();
|
|
||||||
return codec;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SkCodec::Origin get_orientation(const HeifFrameInfo& frameInfo) {
|
|
||||||
switch (frameInfo.mRotationAngle) {
|
|
||||||
case 0: return SkCodec::kTopLeft_Origin;
|
|
||||||
case 90: return SkCodec::kRightTop_Origin;
|
|
||||||
case 180: return SkCodec::kBottomRight_Origin;
|
|
||||||
case 270: return SkCodec::kLeftBottom_Origin;
|
|
||||||
}
|
|
||||||
return SkCodec::kDefault_Origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
SkCodec::Result SkHeifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut) {
|
|
||||||
std::unique_ptr<SkHeifStreamWrapper> streamWrapper(
|
|
||||||
new SkHeifStreamWrapper(stream));
|
|
||||||
|
|
||||||
SkASSERT(codecOut != nullptr);
|
|
||||||
|
|
||||||
// Create a HeifDecoder to own all of the decode information
|
|
||||||
std::unique_ptr<HeifDecoder> heifDecoder(createHeifDecoder());
|
|
||||||
if (heifDecoder.get() == nullptr) {
|
|
||||||
return kInternalError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the decoder info and the source manager
|
|
||||||
HeifFrameInfo frameInfo;
|
|
||||||
if (!heifDecoder->init(streamWrapper.release(), &frameInfo)) {
|
|
||||||
return kInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create image info object and the codec
|
|
||||||
SkEncodedInfo info = SkEncodedInfo::Make(
|
|
||||||
SkEncodedInfo::kYUV_Color, SkEncodedInfo::kOpaque_Alpha, 8);
|
|
||||||
|
|
||||||
Origin orientation = get_orientation(frameInfo);
|
|
||||||
|
|
||||||
sk_sp<SkColorSpace> colorSpace = nullptr;
|
|
||||||
if ((frameInfo.mIccSize > 0) && (frameInfo.mIccData != nullptr)) {
|
|
||||||
SkColorSpace_Base::ICCTypeFlag iccType = SkColorSpace_Base::kRGB_ICCTypeFlag;
|
|
||||||
colorSpace = SkColorSpace_Base::MakeICC(
|
|
||||||
frameInfo.mIccData.get(), frameInfo.mIccSize, iccType);
|
|
||||||
}
|
|
||||||
if (!colorSpace) {
|
|
||||||
colorSpace = SkColorSpace::MakeSRGB();
|
|
||||||
}
|
|
||||||
|
|
||||||
*codecOut = new SkHeifCodec(frameInfo.mWidth, frameInfo.mHeight,
|
|
||||||
info, heifDecoder.release(), std::move(colorSpace), orientation);
|
|
||||||
return kSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
SkHeifCodec::SkHeifCodec(int width, int height, const SkEncodedInfo& info,
|
|
||||||
HeifDecoder* heifDecoder, sk_sp<SkColorSpace> colorSpace, Origin origin)
|
|
||||||
: INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat,
|
|
||||||
nullptr, std::move(colorSpace), origin)
|
|
||||||
, fHeifDecoder(heifDecoder)
|
|
||||||
, fFrameInfo(new HeifFrameInfo())
|
|
||||||
, fSwizzleSrcRow(nullptr)
|
|
||||||
, fColorXformSrcRow(nullptr)
|
|
||||||
, fSwizzlerSubset(SkIRect::MakeEmpty())
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks if the conversion between the input image and the requested output
|
|
||||||
* image has been implemented
|
|
||||||
* Sets the output color format
|
|
||||||
*/
|
|
||||||
bool SkHeifCodec::setOutputColorFormat(const SkImageInfo& dstInfo) {
|
|
||||||
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 (this->colorXform()) {
|
|
||||||
return fHeifDecoder->setOutputColor(kHeifColorFormat_RGBA_8888);
|
|
||||||
} else {
|
|
||||||
return fHeifDecoder->setOutputColor(kHeifColorFormat_RGB565);
|
|
||||||
}
|
|
||||||
|
|
||||||
case kRGBA_F16_SkColorType:
|
|
||||||
SkASSERT(this->colorXform());
|
|
||||||
|
|
||||||
if (!dstInfo.colorSpace()->gammaIsLinear()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
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, kOpaque_SkAlphaType);
|
|
||||||
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 (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
|
|
||||||
return kInvalidConversion;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we can decode to the requested destination and set the output color space
|
|
||||||
if (!this->setOutputColorFormat(dstInfo)) {
|
|
||||||
return kInvalidConversion;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fHeifDecoder->decode(fFrameInfo.get())) {
|
|
||||||
return kInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
SkEncodedInfo swizzlerInfo = this->getEncodedInfo();
|
|
||||||
|
|
||||||
SkImageInfo swizzlerDstInfo = dstInfo;
|
|
||||||
if (this->colorXform()) {
|
|
||||||
// The color xform will be expecting RGBA 8888 input.
|
|
||||||
swizzlerDstInfo = swizzlerDstInfo.makeColorType(kRGBA_8888_SkColorType);
|
|
||||||
}
|
|
||||||
|
|
||||||
fSwizzler.reset(SkSwizzler::CreateSwizzler(swizzlerInfo, nullptr,
|
|
||||||
swizzlerDstInfo, options, nullptr, true));
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
SkCodec::Result SkHeifCodec::onStartScanlineDecode(
|
|
||||||
const SkImageInfo& dstInfo, const Options& options) {
|
|
||||||
if (!this->initializeColorXform(dstInfo, options.fPremulBehavior)) {
|
|
||||||
return kInvalidConversion;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we can decode to the requested destination and set the output color space
|
|
||||||
if (!this->setOutputColorFormat(dstInfo)) {
|
|
||||||
return kInvalidConversion;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.get())) {
|
|
||||||
return kInvalidInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 Google Inc.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by a BSD-style license that can be
|
|
||||||
* found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SkHeifCodec_DEFINED
|
|
||||||
#define SkHeifCodec_DEFINED
|
|
||||||
|
|
||||||
#include "SkCodec.h"
|
|
||||||
#include "SkColorSpace.h"
|
|
||||||
#include "SkColorSpaceXform.h"
|
|
||||||
#include "SkImageInfo.h"
|
|
||||||
#include "SkSwizzler.h"
|
|
||||||
#include "SkStream.h"
|
|
||||||
|
|
||||||
struct HeifFrameInfo;
|
|
||||||
struct HeifDecoder;
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* This class implements the decoding for heif images.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class SkHeifCodec : public SkCodec {
|
|
||||||
public:
|
|
||||||
static bool IsHeif(const void*, size_t);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Assumes IsHeif was called and returned true.
|
|
||||||
* Creates a heif decoder and takes ownership of the stream.
|
|
||||||
*/
|
|
||||||
static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
Result onGetPixels(
|
|
||||||
const SkImageInfo& dstInfo,
|
|
||||||
void* dst, size_t dstRowBytes,
|
|
||||||
const Options& options,
|
|
||||||
int* rowsDecoded) override;
|
|
||||||
|
|
||||||
SkEncodedImageFormat onGetEncodedFormat() const override {
|
|
||||||
return SkEncodedImageFormat::kHEIF;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read enough of the stream to initialize the SkHeifCodec.
|
|
||||||
* Returns a bool representing success or failure.
|
|
||||||
*
|
|
||||||
* @param codecOut
|
|
||||||
* If this returns true, and codecOut was not nullptr,
|
|
||||||
* codecOut will be set to a new SkHeifCodec.
|
|
||||||
*
|
|
||||||
* @param stream
|
|
||||||
* Deleted on failure.
|
|
||||||
* codecOut will take ownership of it in the case where we created a codec.
|
|
||||||
*/
|
|
||||||
static Result ReadHeader(SkStream* stream, SkCodec** codecOut);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Creates an instance of the decoder
|
|
||||||
* Called only by NewFromStream
|
|
||||||
*/
|
|
||||||
SkHeifCodec(int width, int height, const SkEncodedInfo&,
|
|
||||||
HeifDecoder*, sk_sp<SkColorSpace>, Origin);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks if the conversion between the input image and the requested output
|
|
||||||
* image has been implemented.
|
|
||||||
*
|
|
||||||
* Sets the output color format.
|
|
||||||
*/
|
|
||||||
bool setOutputColorFormat(const SkImageInfo& dst);
|
|
||||||
|
|
||||||
void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options);
|
|
||||||
void allocateStorage(const SkImageInfo& dstInfo);
|
|
||||||
int readRows(const SkImageInfo& dstInfo, void* dst,
|
|
||||||
size_t rowBytes, int count, const Options&);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Scanline decoding.
|
|
||||||
*/
|
|
||||||
SkSampler* getSampler(bool createIfNecessary) override;
|
|
||||||
Result onStartScanlineDecode(const SkImageInfo& dstInfo,
|
|
||||||
const Options& options) override;
|
|
||||||
int onGetScanlines(void* dst, int count, size_t rowBytes) override;
|
|
||||||
bool onSkipScanlines(int count) override;
|
|
||||||
|
|
||||||
std::unique_ptr<HeifDecoder> fHeifDecoder;
|
|
||||||
std::unique_ptr<HeifFrameInfo> fFrameInfo;
|
|
||||||
SkAutoTMalloc<uint8_t> fStorage;
|
|
||||||
uint8_t* fSwizzleSrcRow;
|
|
||||||
uint32_t* fColorXformSrcRow;
|
|
||||||
|
|
||||||
// In the case that heif decoder cannot take the exact the subset that
|
|
||||||
// we need, we will use the swizzler to further subset the output.
|
|
||||||
SkIRect fSwizzlerSubset;
|
|
||||||
|
|
||||||
std::unique_ptr<SkSwizzler> fSwizzler;
|
|
||||||
|
|
||||||
typedef SkCodec INHERITED;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SkHeifCodec_DEFINED
|
|
@ -23,6 +23,8 @@ extern "C" {
|
|||||||
void WebPDemuxDelete(WebPDemuxer* dmux);
|
void WebPDemuxDelete(WebPDemuxer* dmux);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const size_t WEBP_VP8_HEADER_SIZE = 30;
|
||||||
|
|
||||||
class SkWebpCodec final : public SkCodec {
|
class SkWebpCodec final : public SkCodec {
|
||||||
public:
|
public:
|
||||||
// Assumes IsWebp was called and returned true.
|
// Assumes IsWebp was called and returned true.
|
||||||
|
@ -919,8 +919,8 @@ DEF_TEST(Codec_wbmp_restrictive, r) {
|
|||||||
// wbmp images have a header that can be arbitrarily large, depending on the
|
// wbmp images have a header that can be arbitrarily large, depending on the
|
||||||
// size of the image. We cap the size at 65535, meaning we only need to look at
|
// size of the image. We cap the size at 65535, meaning we only need to look at
|
||||||
// 8 bytes to determine whether we can read the image. This is important
|
// 8 bytes to determine whether we can read the image. This is important
|
||||||
// because SkCodec only passes a limited number of bytes to SkWbmpCodec to
|
// because SkCodec only passes 14 bytes to SkWbmpCodec to determine whether the
|
||||||
// determine whether the image is a wbmp.
|
// image is a wbmp.
|
||||||
DEF_TEST(Codec_wbmp_max_size, r) {
|
DEF_TEST(Codec_wbmp_max_size, r) {
|
||||||
const unsigned char maxSizeWbmp[] = { 0x00, 0x00, // Header
|
const unsigned char maxSizeWbmp[] = { 0x00, 0x00, // Header
|
||||||
0x83, 0xFF, 0x7F, // W: 65535
|
0x83, 0xFF, 0x7F, // W: 65535
|
||||||
@ -1277,7 +1277,7 @@ DEF_TEST(Codec_fallBack, r) {
|
|||||||
"randPixels.bmp",
|
"randPixels.bmp",
|
||||||
};
|
};
|
||||||
for (auto file : files) {
|
for (auto file : files) {
|
||||||
auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded());
|
auto stream = LimitedRewindingStream::Make(file, 14);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
|
SkDebugf("Missing resources (%s). Set --resourcePath.\n", file);
|
||||||
return;
|
return;
|
||||||
|
Loading…
Reference in New Issue
Block a user