From c2a954290dc3888f877a047098b84c24363895fb Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 16 Aug 2017 13:03:47 -0700 Subject: [PATCH] skia: add heif decoding support Bug: b/64077740 Change-Id: I11e0243bcc4c21c0aa5aa29a719dd0fcba7ae6f7 Reviewed-on: https://skia-review.googlesource.com/35123 Reviewed-by: Chong Zhang Reviewed-by: Leon Scroggins Commit-Queue: Chong Zhang Commit-Queue: Leon Scroggins --- BUILD.gn | 13 + gn/gn_to_bp.py | 3 + include/codec/SkCodec.h | 2 +- include/core/SkEncodedImageFormat.h | 1 + src/android/SkBitmapRegionDecoder.cpp | 1 + src/codec/SkAndroidCodec.cpp | 3 + src/codec/SkCodec.cpp | 14 +- src/codec/SkHeifCodec.cpp | 397 ++++++++++++++++++++++++++ src/codec/SkHeifCodec.h | 108 +++++++ src/codec/SkWebpCodec.h | 2 - tests/CodecTest.cpp | 6 +- 11 files changed, 536 insertions(+), 14 deletions(-) create mode 100644 src/codec/SkHeifCodec.cpp create mode 100644 src/codec/SkHeifCodec.h diff --git a/BUILD.gn b/BUILD.gn index afa38a1520..8d402d3de5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -31,6 +31,7 @@ declare_args() { skia_use_piex = !is_win skia_use_zlib = true skia_use_metal = false + skia_use_libheif = false skia_android_serial = "" skia_enable_discrete_gpu = true @@ -532,6 +533,17 @@ optional("gpu") { } } +optional("heif") { + enabled = skia_use_libheif + public_defines = [ "SK_HAS_HEIF_LIBRARY" ] + + deps = [] + + sources = [ + "src/codec/SkHeifCodec.cpp", + ] +} + optional("jpeg") { enabled = skia_use_libjpeg_turbo public_defines = [ "SK_HAS_JPEG_LIBRARY" ] @@ -655,6 +667,7 @@ component("skia") { ":fontmgr_fontconfig", ":fontmgr_fuchsia", ":gpu", + ":heif", ":jpeg", ":none", ":pdf", diff --git a/gn/gn_to_bp.py b/gn/gn_to_bp.py index 096c5eb280..9e2fa37c4b 100644 --- a/gn/gn_to_bp.py +++ b/gn/gn_to_bp.py @@ -32,6 +32,7 @@ tool_shared_libs = [ 'libicui18n', 'libexpat', 'libft2', + 'libheif', 'libdng_sdk', 'libpiex', 'libcutils', @@ -125,6 +126,7 @@ cc_library { "libdng_sdk", "libexpat", "libft2", + "libheif", "libicui18n", "libicuuc", "libjpeg", @@ -196,6 +198,7 @@ cc_test { gn_args = { 'is_official_build': 'true', 'skia_enable_tools': 'true', + 'skia_use_libheif': 'true', 'skia_use_vulkan': 'true', 'target_cpu': '"none"', 'target_os': '"android"', diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h index fb4f165dbf..fb5bff5165 100644 --- a/include/codec/SkCodec.h +++ b/include/codec/SkCodec.h @@ -50,7 +50,7 @@ public: * this many bytes, or by implementing rewind() to be able to rewind() * after reading this many bytes. */ - static size_t MinBufferedBytesNeeded(); + static constexpr size_t MinBufferedBytesNeeded() { return 32; } /** * Error codes for various SkCodec methods. diff --git a/include/core/SkEncodedImageFormat.h b/include/core/SkEncodedImageFormat.h index 8f79236689..c391053739 100644 --- a/include/core/SkEncodedImageFormat.h +++ b/include/core/SkEncodedImageFormat.h @@ -28,6 +28,7 @@ enum class SkEncodedImageFormat { kKTX, kASTC, kDNG, + kHEIF, }; #endif // SkEncodedImageFormat_DEFINED diff --git a/src/android/SkBitmapRegionDecoder.cpp b/src/android/SkBitmapRegionDecoder.cpp index a6e3ac6d94..72363f59fe 100644 --- a/src/android/SkBitmapRegionDecoder.cpp +++ b/src/android/SkBitmapRegionDecoder.cpp @@ -32,6 +32,7 @@ SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( case SkEncodedImageFormat::kJPEG: case SkEncodedImageFormat::kPNG: case SkEncodedImageFormat::kWEBP: + case SkEncodedImageFormat::kHEIF: break; default: return nullptr; diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp index 3a57352113..3bf69053ea 100644 --- a/src/codec/SkAndroidCodec.cpp +++ b/src/codec/SkAndroidCodec.cpp @@ -83,6 +83,9 @@ std::unique_ptr SkAndroidCodec::MakeFromStream(std::unique_ptr(codec.release()); #ifdef SK_HAS_WEBP_LIBRARY case SkEncodedImageFormat::kWEBP: diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp index 2b6108250c..7dc10854d2 100644 --- a/src/codec/SkCodec.cpp +++ b/src/codec/SkCodec.cpp @@ -23,6 +23,7 @@ #include "SkStream.h" #include "SkWbmpCodec.h" #include "SkWebpCodec.h" +#include "SkHeifCodec.h" struct DecoderProc { bool (*IsFormat)(const void*, size_t); @@ -41,13 +42,12 @@ static const DecoderProc gDecoderProcs[] = { { SkIcoCodec::IsIco, SkIcoCodec::MakeFromStream }, #endif { 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::MakeFromStream(std::unique_ptr stream, Result* outResult, SkPngChunkReader* chunkReader) { Result resultStorage; @@ -60,9 +60,7 @@ std::unique_ptr SkCodec::MakeFromStream(std::unique_ptr strea return nullptr; } - // 14 is enough to read all of the supported types. - const size_t bytesToRead = 14; - SkASSERT(bytesToRead <= MinBufferedBytesNeeded()); + constexpr size_t bytesToRead = MinBufferedBytesNeeded(); char buffer[bytesToRead]; size_t bytesRead = stream->peek(buffer, bytesToRead); diff --git a/src/codec/SkHeifCodec.cpp b/src/codec/SkHeifCodec.cpp new file mode 100644 index 0000000000..aea3ef0745 --- /dev/null +++ b/src/codec/SkHeifCodec.cpp @@ -0,0 +1,397 @@ +/* + * 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(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(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 SkHeifCodec::MakeFromStream( + std::unique_ptr 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 fStream; +}; + +SkCodec::Result SkHeifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut) { + std::unique_ptr streamWrapper( + new SkHeifStreamWrapper(stream)); + + SkASSERT(codecOut != nullptr); + + // Create a HeifDecoder to own all of the decode information + std::unique_ptr 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 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 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(dst, rowBytes); + } + + decodeDst = SkTAddOffset(decodeDst, decodeDstRowBytes); + swizzleDst = SkTAddOffset(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(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); +} + diff --git a/src/codec/SkHeifCodec.h b/src/codec/SkHeifCodec.h new file mode 100644 index 0000000000..dab1535b6b --- /dev/null +++ b/src/codec/SkHeifCodec.h @@ -0,0 +1,108 @@ +/* + * 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 MakeFromStream(std::unique_ptr, 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, 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 fHeifDecoder; + std::unique_ptr fFrameInfo; + SkAutoTMalloc 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 fSwizzler; + + typedef SkCodec INHERITED; +}; + +#endif // SkHeifCodec_DEFINED diff --git a/src/codec/SkWebpCodec.h b/src/codec/SkWebpCodec.h index 60bff61220..134dafa3d5 100644 --- a/src/codec/SkWebpCodec.h +++ b/src/codec/SkWebpCodec.h @@ -23,8 +23,6 @@ extern "C" { void WebPDemuxDelete(WebPDemuxer* dmux); } -static const size_t WEBP_VP8_HEADER_SIZE = 30; - class SkWebpCodec final : public SkCodec { public: // Assumes IsWebp was called and returned true. diff --git a/tests/CodecTest.cpp b/tests/CodecTest.cpp index 4e68cd1ac6..ddf72cd0b7 100644 --- a/tests/CodecTest.cpp +++ b/tests/CodecTest.cpp @@ -919,8 +919,8 @@ DEF_TEST(Codec_wbmp_restrictive, r) { // 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 // 8 bytes to determine whether we can read the image. This is important -// because SkCodec only passes 14 bytes to SkWbmpCodec to determine whether the -// image is a wbmp. +// because SkCodec only passes a limited number of bytes to SkWbmpCodec to +// determine whether the image is a wbmp. DEF_TEST(Codec_wbmp_max_size, r) { const unsigned char maxSizeWbmp[] = { 0x00, 0x00, // Header 0x83, 0xFF, 0x7F, // W: 65535 @@ -1277,7 +1277,7 @@ DEF_TEST(Codec_fallBack, r) { "randPixels.bmp", }; for (auto file : files) { - auto stream = LimitedRewindingStream::Make(file, 14); + auto stream = LimitedRewindingStream::Make(file, SkCodec::MinBufferedBytesNeeded()); if (!stream) { SkDebugf("Missing resources (%s). Set --resourcePath.\n", file); return;