blob: 0ae6fa600b641645ea143d42baa79c315673b1c1 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
class TestImageDecoder : public ImageDecoder {
public:
TestImageDecoder(
ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option)
: ImageDecoder(kAlphaNotPremultiplied,
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_; }
void ResetRequiredPreviousFrames(bool known_opaque = false) {
for (size_t i = 0; i < frame_buffer_cache_.size(); ++i)
frame_buffer_cache_[i].SetRequiredPreviousFrameIndex(
FindRequiredPreviousFrame(i, known_opaque));
}
void InitFrames(size_t num_frames,
unsigned width = 100,
unsigned height = 100) {
SetSize(width, height);
frame_buffer_cache_.resize(num_frames);
for (size_t i = 0; i < num_frames; ++i)
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) {
// 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) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(6);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
frame_buffers[1].SetDisposalMethod(ImageFrame::kDisposeKeep);
frame_buffers[2].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
frame_buffers[3].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
frame_buffers[4].SetDisposalMethod(ImageFrame::kDisposeKeep);
decoder->ResetRequiredPreviousFrames();
// The first frame doesn't require any previous frame.
EXPECT_EQ(kNotFound, frame_buffers[0].RequiredPreviousFrameIndex());
// The previous DisposeNotSpecified frame is required.
EXPECT_EQ(0u, frame_buffers[1].RequiredPreviousFrameIndex());
// DisposeKeep is treated as DisposeNotSpecified.
EXPECT_EQ(1u, frame_buffers[2].RequiredPreviousFrameIndex());
// Previous DisposeOverwritePrevious frames are skipped.
EXPECT_EQ(1u, frame_buffers[3].RequiredPreviousFrameIndex());
EXPECT_EQ(1u, frame_buffers[4].RequiredPreviousFrameIndex());
EXPECT_EQ(4u, frame_buffers[5].RequiredPreviousFrameIndex());
}
TEST(ImageDecoderTest, requiredPreviousFrameIndexDisposeOverwriteBgcolor) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(3);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
// Fully covering DisposeOverwriteBgcolor previous frame resets the starting
// state.
frame_buffers[1].SetDisposalMethod(ImageFrame::kDisposeOverwriteBgcolor);
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
// Partially covering DisposeOverwriteBgcolor previous frame is required by
// this frame.
frame_buffers[1].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(1u, frame_buffers[2].RequiredPreviousFrameIndex());
}
TEST(ImageDecoderTest, requiredPreviousFrameIndexForFrame1) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(2);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(0u, frame_buffers[1].RequiredPreviousFrameIndex());
// The first frame with DisposeOverwritePrevious or DisposeOverwriteBgcolor
// resets the starting state.
frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwriteBgcolor);
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
// ... even if it partially covers.
frame_buffers[0].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwritePrevious);
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
frame_buffers[0].SetDisposalMethod(ImageFrame::kDisposeOverwriteBgcolor);
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(kNotFound, frame_buffers[1].RequiredPreviousFrameIndex());
}
TEST(ImageDecoderTest, requiredPreviousFrameIndexBlendAtopBgcolor) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(3);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
frame_buffers[1].SetOriginalFrameRect(IntRect(25, 25, 50, 50));
frame_buffers[2].SetAlphaBlendSource(ImageFrame::kBlendAtopBgcolor);
// A full frame with 'blending method == BlendAtopBgcolor' doesn't depend on
// any prior frames.
for (int dispose_method = ImageFrame::kDisposeNotSpecified;
dispose_method <= ImageFrame::kDisposeOverwritePrevious;
++dispose_method) {
frame_buffers[1].SetDisposalMethod(
static_cast<ImageFrame::DisposalMethod>(dispose_method));
decoder->ResetRequiredPreviousFrames();
EXPECT_EQ(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
}
// A non-full frame with 'blending method == BlendAtopBgcolor' does depend on
// a prior frame.
frame_buffers[2].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
for (int dispose_method = ImageFrame::kDisposeNotSpecified;
dispose_method <= ImageFrame::kDisposeOverwritePrevious;
++dispose_method) {
frame_buffers[1].SetDisposalMethod(
static_cast<ImageFrame::DisposalMethod>(dispose_method));
decoder->ResetRequiredPreviousFrames();
EXPECT_NE(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
}
}
TEST(ImageDecoderTest, requiredPreviousFrameIndexKnownOpaque) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(3);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
frame_buffers[1].SetOriginalFrameRect(IntRect(25, 25, 50, 50));
// A full frame that is known to be opaque doesn't depend on any prior frames.
for (int dispose_method = ImageFrame::kDisposeNotSpecified;
dispose_method <= ImageFrame::kDisposeOverwritePrevious;
++dispose_method) {
frame_buffers[1].SetDisposalMethod(
static_cast<ImageFrame::DisposalMethod>(dispose_method));
decoder->ResetRequiredPreviousFrames(true);
EXPECT_EQ(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
}
// A non-full frame that is known to be opaque does depend on a prior frame.
frame_buffers[2].SetOriginalFrameRect(IntRect(50, 50, 50, 50));
for (int dispose_method = ImageFrame::kDisposeNotSpecified;
dispose_method <= ImageFrame::kDisposeOverwritePrevious;
++dispose_method) {
frame_buffers[1].SetDisposalMethod(
static_cast<ImageFrame::DisposalMethod>(dispose_method));
decoder->ResetRequiredPreviousFrames(true);
EXPECT_NE(kNotFound, frame_buffers[2].RequiredPreviousFrameIndex());
}
}
TEST(ImageDecoderTest, clearCacheExceptFrameDoNothing) {
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->ClearCacheExceptFrame(0);
// This should not crash.
decoder->InitFrames(20);
decoder->ClearCacheExceptFrame(kNotFound);
}
TEST(ImageDecoderTest, clearCacheExceptFrameAll) {
const size_t kNumFrames = 10;
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(kNumFrames);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
for (size_t i = 0; i < kNumFrames; ++i)
frame_buffers[i].SetStatus(i % 2 ? ImageFrame::kFramePartial
: ImageFrame::kFrameComplete);
decoder->ClearCacheExceptFrame(kNotFound);
for (size_t i = 0; i < kNumFrames; ++i) {
SCOPED_TRACE(testing::Message() << i);
EXPECT_EQ(ImageFrame::kFrameEmpty, frame_buffers[i].GetStatus());
}
}
TEST(ImageDecoderTest, clearCacheExceptFramePreverveClearExceptFrame) {
const size_t kNumFrames = 10;
std::unique_ptr<TestImageDecoder> decoder(
std::make_unique<TestImageDecoder>());
decoder->InitFrames(kNumFrames);
Vector<ImageFrame, 1>& frame_buffers = decoder->FrameBufferCache();
for (size_t i = 0; i < kNumFrames; ++i)
frame_buffers[i].SetStatus(ImageFrame::kFrameComplete);
decoder->ResetRequiredPreviousFrames();
decoder->ClearCacheExceptFrame(5);
for (size_t i = 0; i < kNumFrames; ++i) {
SCOPED_TRACE(testing::Message() << i);
if (i == 5)
EXPECT_EQ(ImageFrame::kFrameComplete, frame_buffers[i].GetStatus());
else
EXPECT_EQ(ImageFrame::kFrameEmpty, frame_buffers[i].GetStatus());
}
}
} // namespace blink