/* * 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 "config.h" #include "platform/image-decoders/ImageDecoder.h" #include "platform/image-decoders/ImageFrame.h" #include "wtf/OwnPtr.h" #include "wtf/PassOwnPtr.h" #include "wtf/Vector.h" #include using namespace WebCore; class TestImageDecoder : public ImageDecoder { public: TestImageDecoder() : ImageDecoder(ImageSource::AlphaNotPremultiplied, ImageSource::GammaAndColorProfileApplied, noDecodedImageByteLimit) { } virtual String filenameExtension() const OVERRIDE { return ""; } virtual ImageFrame* frameBufferAtIndex(size_t) OVERRIDE { return 0; } Vector& frameBufferCache() { return m_frameBufferCache; } void resetRequiredPreviousFrames(bool knownOpaque = false) { for (size_t i = 0; i < m_frameBufferCache.size(); ++i) m_frameBufferCache[i].setRequiredPreviousFrameIndex(findRequiredPreviousFrame(i, knownOpaque)); } void initFrames(size_t numFrames, unsigned width = 100, unsigned height = 100) { setSize(width, height); m_frameBufferCache.resize(numFrames); for (size_t i = 0; i < numFrames; ++i) m_frameBufferCache[i].setOriginalFrameRect(IntRect(0, 0, width, height)); } }; TEST(ImageDecoderTest, sizeCalculationMayOverflow) { OwnPtr decoder(adoptPtr(new 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(ImageDecoderTest, requiredPreviousFrameIndex) { OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(6); Vector& frameBuffers = decoder->frameBufferCache(); frameBuffers[1].setDisposalMethod(ImageFrame::DisposeKeep); frameBuffers[2].setDisposalMethod(ImageFrame::DisposeOverwritePrevious); frameBuffers[3].setDisposalMethod(ImageFrame::DisposeOverwritePrevious); frameBuffers[4].setDisposalMethod(ImageFrame::DisposeKeep); decoder->resetRequiredPreviousFrames(); // The first frame doesn't require any previous frame. EXPECT_EQ(kNotFound, frameBuffers[0].requiredPreviousFrameIndex()); // The previous DisposeNotSpecified frame is required. EXPECT_EQ(0u, frameBuffers[1].requiredPreviousFrameIndex()); // DisposeKeep is treated as DisposeNotSpecified. EXPECT_EQ(1u, frameBuffers[2].requiredPreviousFrameIndex()); // Previous DisposeOverwritePrevious frames are skipped. EXPECT_EQ(1u, frameBuffers[3].requiredPreviousFrameIndex()); EXPECT_EQ(1u, frameBuffers[4].requiredPreviousFrameIndex()); EXPECT_EQ(4u, frameBuffers[5].requiredPreviousFrameIndex()); } TEST(ImageDecoderTest, requiredPreviousFrameIndexDisposeOverwriteBgcolor) { OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(3); Vector& frameBuffers = decoder->frameBufferCache(); // Fully covering DisposeOverwriteBgcolor previous frame resets the starting state. frameBuffers[1].setDisposalMethod(ImageFrame::DisposeOverwriteBgcolor); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(kNotFound, frameBuffers[2].requiredPreviousFrameIndex()); // Partially covering DisposeOverwriteBgcolor previous frame is required by this frame. frameBuffers[1].setOriginalFrameRect(IntRect(50, 50, 50, 50)); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(1u, frameBuffers[2].requiredPreviousFrameIndex()); } TEST(ImageDecoderTest, requiredPreviousFrameIndexForFrame1) { OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(2); Vector& frameBuffers = decoder->frameBufferCache(); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(0u, frameBuffers[1].requiredPreviousFrameIndex()); // The first frame with DisposeOverwritePrevious or DisposeOverwriteBgcolor // resets the starting state. frameBuffers[0].setDisposalMethod(ImageFrame::DisposeOverwritePrevious); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(kNotFound, frameBuffers[1].requiredPreviousFrameIndex()); frameBuffers[0].setDisposalMethod(ImageFrame::DisposeOverwriteBgcolor); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(kNotFound, frameBuffers[1].requiredPreviousFrameIndex()); // ... even if it partially covers. frameBuffers[0].setOriginalFrameRect(IntRect(50, 50, 50, 50)); frameBuffers[0].setDisposalMethod(ImageFrame::DisposeOverwritePrevious); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(kNotFound, frameBuffers[1].requiredPreviousFrameIndex()); frameBuffers[0].setDisposalMethod(ImageFrame::DisposeOverwriteBgcolor); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(kNotFound, frameBuffers[1].requiredPreviousFrameIndex()); } TEST(ImageDecoderTest, requiredPreviousFrameIndexBlendAtopBgcolor) { OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(3); Vector& frameBuffers = decoder->frameBufferCache(); frameBuffers[1].setOriginalFrameRect(IntRect(25, 25, 50, 50)); frameBuffers[2].setAlphaBlendSource(ImageFrame::BlendAtopBgcolor); // A full frame with 'blending method == BlendAtopBgcolor' doesn't depend on any prior frames. for (int disposeMethod = ImageFrame::DisposeNotSpecified; disposeMethod <= ImageFrame::DisposeOverwritePrevious; ++disposeMethod) { frameBuffers[1].setDisposalMethod(static_cast(disposeMethod)); decoder->resetRequiredPreviousFrames(); EXPECT_EQ(kNotFound, frameBuffers[2].requiredPreviousFrameIndex()); } // A non-full frame with 'blending method == BlendAtopBgcolor' does depend on a prior frame. frameBuffers[2].setOriginalFrameRect(IntRect(50, 50, 50, 50)); for (int disposeMethod = ImageFrame::DisposeNotSpecified; disposeMethod <= ImageFrame::DisposeOverwritePrevious; ++disposeMethod) { frameBuffers[1].setDisposalMethod(static_cast(disposeMethod)); decoder->resetRequiredPreviousFrames(); EXPECT_NE(kNotFound, frameBuffers[2].requiredPreviousFrameIndex()); } } TEST(ImageDecoderTest, requiredPreviousFrameIndexKnownOpaque) { OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(3); Vector& frameBuffers = decoder->frameBufferCache(); frameBuffers[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 disposeMethod = ImageFrame::DisposeNotSpecified; disposeMethod <= ImageFrame::DisposeOverwritePrevious; ++disposeMethod) { frameBuffers[1].setDisposalMethod(static_cast(disposeMethod)); decoder->resetRequiredPreviousFrames(true); EXPECT_EQ(kNotFound, frameBuffers[2].requiredPreviousFrameIndex()); } // A non-full frame that is known to be opaque does depend on a prior frame. frameBuffers[2].setOriginalFrameRect(IntRect(50, 50, 50, 50)); for (int disposeMethod = ImageFrame::DisposeNotSpecified; disposeMethod <= ImageFrame::DisposeOverwritePrevious; ++disposeMethod) { frameBuffers[1].setDisposalMethod(static_cast(disposeMethod)); decoder->resetRequiredPreviousFrames(true); EXPECT_NE(kNotFound, frameBuffers[2].requiredPreviousFrameIndex()); } } TEST(ImageDecoderTest, clearCacheExceptFrameDoNothing) { OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->clearCacheExceptFrame(0); // This should not crash. decoder->initFrames(20); decoder->clearCacheExceptFrame(kNotFound); } TEST(ImageDecoderTest, clearCacheExceptFrameAll) { const size_t numFrames = 10; OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(numFrames); Vector& frameBuffers = decoder->frameBufferCache(); for (size_t i = 0; i < numFrames; ++i) frameBuffers[i].setStatus(i % 2 ? ImageFrame::FramePartial : ImageFrame::FrameComplete); decoder->clearCacheExceptFrame(kNotFound); for (size_t i = 0; i < numFrames; ++i) { SCOPED_TRACE(testing::Message() << i); EXPECT_EQ(ImageFrame::FrameEmpty, frameBuffers[i].status()); } } TEST(ImageDecoderTest, clearCacheExceptFramePreverveClearExceptFrame) { const size_t numFrames = 10; OwnPtr decoder(adoptPtr(new TestImageDecoder())); decoder->initFrames(numFrames); Vector& frameBuffers = decoder->frameBufferCache(); for (size_t i = 0; i < numFrames; ++i) frameBuffers[i].setStatus(ImageFrame::FrameComplete); decoder->resetRequiredPreviousFrames(); decoder->clearCacheExceptFrame(5); for (size_t i = 0; i < numFrames; ++i) { SCOPED_TRACE(testing::Message() << i); if (i == 5) EXPECT_EQ(ImageFrame::FrameComplete, frameBuffers[i].status()); else EXPECT_EQ(ImageFrame::FrameEmpty, frameBuffers[i].status()); } }