Commit 5724df18 authored by Reza.Zakerinasab's avatar Reza.Zakerinasab Committed by Commit Bot

Decode 16 bit PNG in PNGImageDecoder

This change adds 16 bit decoding to PNGImageDecoder.
This change is a part of the bigger change to support 16 bit PNGs
in the new color managed canvas API
(chromium-review.googlesource.com/c/chromium/src/+/1079788).

BUG: 839034
Change-Id: I1bf92e1dd4fcbd79ffe7def33223fb5298649eab
Reviewed-on: https://chromium-review.googlesource.com/1148787Reviewed-by: default avatarLeon Scroggins <scroggo@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Commit-Queue: Mohammad Reza Zakerinasab <zakerinasab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583760}
parent cd4b0faf
......@@ -1861,6 +1861,7 @@ jumbo_source_set("blink_platform_unittests_sources") {
"//services/viz/public/interfaces",
"//services/viz/public/interfaces:interfaces_blink",
"//skia",
"//skia:skcms",
"//testing/gmock",
"//testing/gtest",
"//third_party:freetype_harfbuzz",
......
......@@ -64,6 +64,8 @@ inline bool MatchesBMPSignature(const char* contents) {
}
static constexpr size_t kLongestSignatureLength = sizeof("RIFF????WEBPVP") - 1;
static const size_t k4BytesPerPixel = 4;
static const size_t k8BytesPerPixel = 8;
std::unique_ptr<ImageDecoder> ImageDecoder::Create(
scoped_refptr<SegmentReader> data,
......@@ -75,15 +77,24 @@ std::unique_ptr<ImageDecoder> ImageDecoder::Create(
// At least kLongestSignatureLength bytes are needed to sniff the signature.
if (data->size() < kLongestSignatureLength)
return nullptr;
// On low end devices, always decode to 8888.
if (high_bit_depth_decoding_option == kHighBitDepthToHalfFloat &&
Platform::Current() && Platform::Current()->IsLowEndDevice()) {
high_bit_depth_decoding_option = kDefaultBitDepth;
}
size_t max_decoded_bytes = Platform::Current()
? Platform::Current()->MaxDecodedImageBytes()
: kNoDecodedImageByteLimit;
if (!desired_size.isEmpty()) {
static const size_t kBytesPerPixels = 4;
size_t requested_decoded_bytes =
kBytesPerPixels * desired_size.width() * desired_size.height();
max_decoded_bytes = std::min(requested_decoded_bytes, max_decoded_bytes);
size_t num_pixels = desired_size.width() * desired_size.height();
if (high_bit_depth_decoding_option == kDefaultBitDepth) {
max_decoded_bytes =
std::min(k4BytesPerPixel * num_pixels, max_decoded_bytes);
} else { // kHighBitDepthToHalfFloat
max_decoded_bytes =
std::min(k8BytesPerPixel * num_pixels, max_decoded_bytes);
}
}
// Access the first kLongestSignatureLength chars to sniff the signature.
......@@ -188,8 +199,12 @@ size_t ImageDecoder::FrameBytesAtIndex(size_t index) const {
uint64_t area;
};
return ImageSize(FrameSizeAtIndex(index)).area *
sizeof(ImageFrame::PixelData);
size_t decoded_bytes_per_pixel = k4BytesPerPixel;
if (frame_buffer_cache_[index].GetPixelFormat() ==
ImageFrame::PixelFormat::kRGBA_F16) {
decoded_bytes_per_pixel = k8BytesPerPixel;
}
return ImageSize(FrameSizeAtIndex(index)).area * decoded_bytes_per_pixel;
}
size_t ImageDecoder::ClearCacheExceptFrame(size_t clear_except_frame) {
......@@ -391,12 +406,18 @@ void ImageDecoder::UpdateAggressivePurging(size_t index) {
// As we decode we will learn the total number of frames, and thus total
// possible image memory used.
size_t decoded_bytes_per_pixel = k4BytesPerPixel;
if (frame_buffer_cache_.size() && frame_buffer_cache_[0].GetPixelFormat() ==
ImageFrame::PixelFormat::kRGBA_F16) {
decoded_bytes_per_pixel = k8BytesPerPixel;
}
const uint64_t frame_memory_usage =
DecodedSize().Area() * 4; // 4 bytes per pixel
DecodedSize().Area() * decoded_bytes_per_pixel;
// This condition never fails in the current code. Our existing image decoders
// parse for the image size and SetFailed() if that size overflows
DCHECK_EQ(frame_memory_usage / 4, DecodedSize().Area());
DCHECK_EQ(frame_memory_usage / decoded_bytes_per_pixel, DecodedSize().Area());
const uint64_t total_memory_usage = frame_memory_usage * index;
if (total_memory_usage / frame_memory_usage != index) { // overflow occurred
......
......@@ -153,6 +153,12 @@ class PLATFORM_EXPORT ImageDecoder {
bool IsAllDataReceived() const { return is_all_data_received_; }
// Returns true if the decoder supports decoding to high bit depth. The
// decoded output will be high bit depth (half float backed bitmap) iff
// encoded image is high bit depth and high_bit_depth_decoding_option_ is set
// to kHighBitDepthToHalfFloat.
virtual bool ImageIsHighBitDepth() { return false; }
// Returns true if the buffer holds enough data to instantiate a decoder.
// This is useful for callers to determine whether a decoder instantiation
// failure is due to insufficient or bad data.
......@@ -214,7 +220,11 @@ class PLATFORM_EXPORT ImageDecoder {
// Returns whether the size is legal (i.e. not going to result in
// overflow elsewhere). If not, marks decoding as failed.
virtual bool SetSize(unsigned width, unsigned height) {
if (SizeCalculationMayOverflow(width, height))
unsigned decoded_bytes_per_pixel = 4;
if (ImageIsHighBitDepth() &&
high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat)
decoded_bytes_per_pixel = 8;
if (SizeCalculationMayOverflow(width, height, decoded_bytes_per_pixel))
return SetFailed();
size_ = IntSize(width, height);
......@@ -447,12 +457,16 @@ class PLATFORM_EXPORT ImageDecoder {
}
private:
// Some code paths compute the size of the image as "width * height * 4"
// Some code paths compute the size of the image as "width * height * 4 or 8"
// and return it as a (signed) int. Avoid overflow.
static bool SizeCalculationMayOverflow(unsigned width, unsigned height) {
static bool SizeCalculationMayOverflow(unsigned width,
unsigned height,
unsigned decoded_bytes_per_pixel) {
unsigned long long total_size = static_cast<unsigned long long>(width) *
static_cast<unsigned long long>(height);
return total_size > ((1 << 29) - 1);
if (decoded_bytes_per_pixel == 4)
return total_size > ((1 << 29) - 1);
return total_size > ((1 << 28) - 1);
}
bool purge_aggressively_;
......
......@@ -39,12 +39,15 @@ namespace blink {
class TestImageDecoder : public ImageDecoder {
public:
TestImageDecoder()
TestImageDecoder(
ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option)
: ImageDecoder(kAlphaNotPremultiplied,
ImageDecoder::kDefaultBitDepth,
high_bit_depth_decoding_option,
ColorBehavior::TransformToSRGB(),
kNoDecodedImageByteLimit) {}
TestImageDecoder() : TestImageDecoder(ImageDecoder::kDefaultBitDepth) {}
String FilenameExtension() const override { return ""; }
Vector<ImageFrame, 1>& FrameBufferCache() { return frame_buffer_cache_; }
......@@ -64,20 +67,52 @@ class TestImageDecoder : public ImageDecoder {
frame_buffer_cache_[i].SetOriginalFrameRect(IntRect(0, 0, width, height));
}
bool ImageIsHighBitDepth() override { return image_is_high_bit_depth_; }
void SetImageToHighBitDepthForTest() { image_is_high_bit_depth_ = true; }
private:
bool image_is_high_bit_depth_ = false;
void DecodeSize() override {}
void Decode(size_t index) override {}
};
TEST(ImageDecoderTest, sizeCalculationMayOverflow) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
EXPECT_FALSE(decoder->SetSize(1 << 29, 1));
EXPECT_FALSE(decoder->SetSize(1, 1 << 29));
EXPECT_FALSE(decoder->SetSize(1 << 15, 1 << 15));
EXPECT_TRUE(decoder->SetSize(1 << 28, 1));
EXPECT_TRUE(decoder->SetSize(1, 1 << 28));
EXPECT_TRUE(decoder->SetSize(1 << 14, 1 << 14));
// Test coverage:
// Regular bit depth image with regular decoder
// Regular bit depth image with high bit depth decoder
// High bit depth image with regular decoder
// High bit depth image with high bit depth decoder
bool high_bit_depth_decoder_status[] = {false, true};
bool high_bit_depth_image_status[] = {false, true};
for (bool high_bit_depth_decoder : high_bit_depth_decoder_status) {
for (bool high_bit_depth_image : high_bit_depth_image_status) {
std::unique_ptr<TestImageDecoder> decoder;
if (high_bit_depth_decoder) {
decoder = std::make_unique<TestImageDecoder>(
ImageDecoder::kHighBitDepthToHalfFloat);
} else {
decoder = std::make_unique<TestImageDecoder>();
}
if (high_bit_depth_image)
decoder->SetImageToHighBitDepthForTest();
unsigned log_pixel_size = 2; // pixel is 4 bytes
if (high_bit_depth_decoder && high_bit_depth_image)
log_pixel_size = 3; // pixel is 8 byts
unsigned overflow_dim_shift = 31 - log_pixel_size;
unsigned overflow_dim_shift_half = (overflow_dim_shift + 1) / 2;
EXPECT_FALSE(decoder->SetSize(1 << overflow_dim_shift, 1));
EXPECT_FALSE(decoder->SetSize(1, 1 << overflow_dim_shift));
EXPECT_FALSE(decoder->SetSize(1 << overflow_dim_shift_half,
1 << overflow_dim_shift_half));
EXPECT_TRUE(decoder->SetSize(1 << (overflow_dim_shift - 1), 1));
EXPECT_TRUE(decoder->SetSize(1, 1 << (overflow_dim_shift - 1)));
EXPECT_TRUE(decoder->SetSize(1 << (overflow_dim_shift_half - 1),
1 << (overflow_dim_shift_half - 1)));
}
}
}
TEST(ImageDecoderTest, requiredPreviousFrameIndex) {
......
......@@ -86,6 +86,7 @@ void ImageFrame::ZeroFillPixelData() {
bool ImageFrame::CopyBitmapData(const ImageFrame& other) {
DCHECK_NE(this, &other);
has_alpha_ = other.has_alpha_;
pixel_format_ = other.pixel_format_;
bitmap_.reset();
SkImageInfo info = other.bitmap_.info();
return bitmap_.tryAllocPixels(info) &&
......@@ -101,6 +102,7 @@ bool ImageFrame::TakeBitmapDataIfWritable(ImageFrame* other) {
if (other->bitmap_.isImmutable())
return false;
has_alpha_ = other->has_alpha_;
pixel_format_ = other->pixel_format_;
bitmap_.reset();
bitmap_.swap(other->bitmap_);
other->status_ = kFrameEmpty;
......@@ -157,24 +159,6 @@ void ImageFrame::ZeroFillFrameRect(const IntRect& rect) {
SetHasAlpha(true);
}
void ImageFrame::SetRGBAPremultiplyF16Buffer(PixelDataF16* dst,
PixelDataF16* src,
size_t num_pixels) {
sk_sp<SkColorSpace> color_space = SkColorSpace::MakeSRGBLinear();
auto color_format = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
SkColorSpaceXform::Apply(color_space.get(), color_format, dst,
color_space.get(), color_format, src, num_pixels,
SkColorSpaceXform::AlphaOp::kPremul_AlphaOp);
}
void ImageFrame::SetPixelsOpaqueF16Buffer(PixelDataF16* dst,
PixelDataF16* src,
size_t num_pixels) {
// We set the alpha half float to 0x3c00, which is equal to 1.
while (num_pixels-- > 0)
*dst++ = (*src++ & 0x0000ffffffffffff) | 0x3c00000000000000;
}
static void BlendRGBAF16Buffer(ImageFrame::PixelDataF16* dst,
ImageFrame::PixelDataF16* src,
size_t num_pixels,
......
......@@ -81,10 +81,6 @@ class PLATFORM_EXPORT ImageFrame final {
ImageFrame();
ImageFrame(PixelFormat pixel_format) : ImageFrame() {
pixel_format_ = pixel_format;
}
// The assignment operator reads has_alpha_ (inside SetStatus()) before it
// sets it (in SetHasAlpha()). This doesn't cause any problems, since the
// SetHasAlpha() call ensures all state is set correctly, but it means we
......@@ -157,6 +153,7 @@ class PLATFORM_EXPORT ImageFrame final {
return required_previous_frame_index_;
}
void SetHasAlpha(bool alpha);
void SetPixelFormat(PixelFormat format) { pixel_format_ = format; }
void SetOriginalFrameRect(const IntRect& r) { original_frame_rect_ = r; }
void SetStatus(Status);
void SetDuration(TimeDelta duration) { duration_ = duration; }
......@@ -234,10 +231,6 @@ class PLATFORM_EXPORT ImageFrame final {
*dest = SkPackARGB32NoCheck(a, r, g, b);
}
static void SetRGBAPremultiplyF16Buffer(PixelDataF16* dst,
PixelDataF16* src,
size_t num_pixels);
static inline void SetRGBARaw(PixelData* dest,
unsigned r,
unsigned g,
......@@ -246,10 +239,6 @@ class PLATFORM_EXPORT ImageFrame final {
*dest = SkPackARGB32NoCheck(a, r, g, b);
}
static void SetPixelsOpaqueF16Buffer(PixelDataF16* dst,
PixelDataF16* src,
size_t num_pixels);
// Blend the RGBA pixel provided by |red|, |green|, |blue| and |alpha| over
// the pixel in |dest|, without premultiplication, and overwrite |dest| with
// the result.
......
......@@ -65,80 +65,6 @@ class ImageFrameTest : public testing::Test {
}
};
TEST_F(ImageFrameTest, TestF16API) {
ImageFrame::PixelFormat kN32 = ImageFrame::PixelFormat::kN32;
ImageFrame::PixelFormat kRGBA_F16 = ImageFrame::PixelFormat::kRGBA_F16;
ImageFrame frame_no_pixel_format;
ASSERT_EQ(kN32, frame_no_pixel_format.GetPixelFormat());
ImageFrame frame_pixel_format_n32(kN32);
ASSERT_EQ(kN32, frame_pixel_format_n32.GetPixelFormat());
ImageFrame frame_pixel_format_f16(kRGBA_F16);
ASSERT_EQ(kRGBA_F16, frame_pixel_format_f16.GetPixelFormat());
ImageFrame frame_copy_ctor_n32(frame_pixel_format_n32);
ASSERT_EQ(kN32, frame_copy_ctor_n32.GetPixelFormat());
ImageFrame frame_copy_ctor_f16(frame_pixel_format_f16);
ASSERT_EQ(kRGBA_F16, frame_copy_ctor_f16.GetPixelFormat());
ImageFrame frame_test_assignment;
frame_test_assignment = frame_pixel_format_n32;
ASSERT_EQ(kN32, frame_test_assignment.GetPixelFormat());
frame_test_assignment = frame_pixel_format_f16;
ASSERT_EQ(kRGBA_F16, frame_test_assignment.GetPixelFormat());
SkBitmap bitmap(frame_pixel_format_f16.Bitmap());
ASSERT_EQ(0, bitmap.width());
ASSERT_EQ(0, bitmap.height());
ASSERT_EQ(nullptr, bitmap.colorSpace());
TestAllocator allocator;
frame_pixel_format_f16.SetMemoryAllocator(&allocator);
sk_sp<SkColorSpace> srgb_linear = SkColorSpace::MakeSRGBLinear();
ASSERT_TRUE(frame_pixel_format_f16.AllocatePixelData(2, 2, srgb_linear));
bitmap = frame_pixel_format_f16.Bitmap();
ASSERT_EQ(2, bitmap.width());
ASSERT_EQ(2, bitmap.height());
ASSERT_TRUE(SkColorSpace::Equals(srgb_linear.get(), bitmap.colorSpace()));
}
TEST_F(ImageFrameTest, SetRGBAPremultiplyF16Buffer) {
ImageFrame::PixelDataF16 premul_f16;
ImageFrame::SetRGBAPremultiplyF16Buffer(&premul_f16, &src_f16, 1);
float f32_from_src_f16[4];
ConvertF16ToF32(f32_from_src_f16, src_f16);
for (int i = 0; i < 3; i++)
f32_from_src_f16[i] *= f32_from_src_f16[3];
float f32_from_premul_f16[4];
ConvertF16ToF32(f32_from_premul_f16, premul_f16);
for (int i = 0; i < 4; i++) {
ASSERT_TRUE(fabs(f32_from_src_f16[i] - f32_from_premul_f16[i]) <
color_compoenent_tolerance);
}
}
TEST_F(ImageFrameTest, SetPixelsOpaqueF16Buffer) {
ImageFrame::PixelDataF16 opaque_f16;
ImageFrame::SetPixelsOpaqueF16Buffer(&opaque_f16, &src_f16, 1);
float f32_from_src_f16[4];
ConvertF16ToF32(f32_from_src_f16, src_f16);
float f32_from_opaque_f16[4];
ConvertF16ToF32(f32_from_opaque_f16, opaque_f16);
for (int i = 0; i < 3; i++)
ASSERT_EQ(f32_from_src_f16[i], f32_from_opaque_f16[i]);
ASSERT_EQ(1.0f, f32_from_opaque_f16[3]);
}
TEST_F(ImageFrameTest, BlendRGBARawF16Buffer) {
ImageFrame::PixelData blended_8888(dst_8888);
ImageFrame::BlendRGBARaw(&blended_8888, src_8888_r, src_8888_g, src_8888_b,
......
......@@ -48,6 +48,7 @@ class PLATFORM_EXPORT PNGImageDecoder final : public ImageDecoder {
String FilenameExtension() const override { return "png"; }
bool SetSize(unsigned, unsigned) override;
int RepetitionCount() const override;
bool ImageIsHighBitDepth() override;
bool FrameIsReceivedAtIndex(size_t) const override;
TimeDelta FrameDurationAtIndex(size_t) const override;
bool SetFailed() override;
......@@ -59,6 +60,7 @@ class PLATFORM_EXPORT PNGImageDecoder final : public ImageDecoder {
void SetColorSpace();
void SetRepetitionCount(int);
void SetBitDepth();
private:
using ParseQuery = PNGImageReader::ParseQuery;
......@@ -78,6 +80,8 @@ class PLATFORM_EXPORT PNGImageDecoder final : public ImageDecoder {
int repetition_count_;
bool has_alpha_channel_;
bool current_buffer_saw_alpha_;
bool decode_to_half_float_;
size_t bit_depth_;
std::unique_ptr<ImageFrame::PixelData[]> color_transform_scanline_;
};
......
......@@ -543,8 +543,14 @@ bool PNGImageReader::ParseSize(const FastSharedBufferReader& reader) {
fctl_needs_dat_chunk_ = false;
if (ignore_animation_)
is_animated_ = false;
// SetSize() requires bit depth information to correctly fallback to 8888
// decoding if there is not enough memory to decode to f16 pixel format.
// SetBitDepth() requires repition count to correctly fallback to 8888
// decoding for multi-frame APNGs (https://crbug.com/874057). Therefore,
// the order of the next three calls matters.
if (!is_animated_ || 1 == reported_frame_count_)
decoder_->SetRepetitionCount(kAnimationNone);
decoder_->SetBitDepth();
if (!decoder_->SetSize(width_, height_))
return false;
decoder_->SetColorSpace();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment