void CachedImage::clear() { destroyDecodedData(); clearImage(); m_pendingContainerSizeRequests.clear(); setEncodedSize(0); }
Image::~Image() { // Null out the image observer so that we don't incorrectly communicate that decoded data is being destroyed during destruction. m_imageObserver = 0; destroyDecodedData(); stopAnimation(); destroyNativeData(); }
TEST_F(BitmapImageTest, destroyDecodedData) { loadImage("/LayoutTests/fast/images/resources/animated-10color.gif"); size_t totalSize = decodedSize(); EXPECT_GT(totalSize, 0u); destroyDecodedData(); EXPECT_EQ(-static_cast<int>(totalSize), lastDecodedSizeChange()); EXPECT_EQ(0u, decodedSize()); }
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll) { // Animated images >5MB are considered large enough that we'll only hang on // to one frame at a time. static const unsigned cLargeAnimationCutoff = 5242880; if (frameCount() * frameBytes(m_size) > cLargeAnimationCutoff) destroyDecodedData(destroyAll); }
TEST_F(BitmapImageTest, destroyAllDecodedData) { loadImage("/tests/fast/images/resources/animated-10color.gif"); size_t totalSize = decodedSize(); EXPECT_GT(totalSize, 0u); destroyDecodedData(true); EXPECT_EQ(-static_cast<int>(totalSize), m_imageObserver.m_lastDecodedSizeChangedDelta); EXPECT_EQ(0u, decodedSize()); }
TEST_F(BitmapImageTest, pngHasColorProfile) { loadImage("/LayoutTests/fast/images/resources/palatted-color-png-gamma-one-color-profile.png"); EXPECT_EQ(1u, decodedFramesCount()); EXPECT_EQ(65536u, decodedSize()); EXPECT_TRUE(m_image->hasColorProfile()); destroyDecodedData(true); }
TEST_F(BitmapImageTest, jpegHasColorProfile) { loadImage("/LayoutTests/fast/images/resources/icc-v2-gbr.jpg"); EXPECT_EQ(1u, decodedFramesCount()); EXPECT_EQ(227700u, decodedSize()); EXPECT_TRUE(m_image->hasColorProfile()); destroyDecodedData(true); }
TEST_F(BitmapImageTest, webpHasColorProfile) { loadImage("/LayoutTests/fast/images/resources/webp-color-profile-lossy.webp"); EXPECT_EQ(1u, decodedFramesCount()); EXPECT_EQ(2560000u, decodedSize()); EXPECT_TRUE(m_image->hasColorProfile()); destroyDecodedData(true); }
TEST_F(BitmapImageTest, destroyDecodedDataExceptCurrentFrame) { loadImage("/tests/fast/images/resources/animated-10color.gif"); size_t totalSize = decodedSize(); size_t frame = frameCount() / 2; setCurrentFrame(frame); size_t size = frameDecodedSize(frame); destroyDecodedData(false); EXPECT_LT(m_imageObserver.m_lastDecodedSizeChangedDelta, 0); EXPECT_GE(m_imageObserver.m_lastDecodedSizeChangedDelta, -static_cast<int>(totalSize - size)); }
bool ImageFrameCache::destroyDecodedDataIfNecessary(bool destroyAll, size_t count) { unsigned decodedSize = 0; for (auto& frame : m_frames) decodedSize += frame.frameBytes(); if (decodedSize < LargeAnimationCutoff) return false; destroyDecodedData(destroyAll, count); return true; }
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll) { // Animated images >5MB are considered large enough that we'll only hang on // to one frame at a time. static const unsigned cLargeAnimationCutoff = 5242880; unsigned allFrameBytes = 0; for (size_t i = 0; i < m_frames.size(); ++i) allFrameBytes += m_frames[i].m_frameBytes; if (allFrameBytes > cLargeAnimationCutoff) destroyDecodedData(destroyAll); }
void BitmapImage::resetAnimation() { stopAnimation(); m_currentFrame = 0; m_repetitionsComplete = 0; m_animationFinished = false; int frameSize = m_size.width() * m_size.height() * 4; // For extremely large animations, when the animation is reset, we just throw everything away. if (frameCount() * frameSize > cLargeAnimationCutoff) destroyDecodedData(); }
bool BitmapImage::dataChanged(bool allDataReceived) { destroyDecodedData(true); // Feed all the data we've seen so far to the image decoder. m_allDataReceived = allDataReceived; m_source.setData(m_data.get(), allDataReceived); // Image properties will not be available until the first frame of the file // reaches kCGImageStatusIncomplete. return isSizeAvailable(); }
void BitmapImage::notifyObserverAndTrimDecodedData() { // Notify our observer that the animation has advanced. imageObserver()->animationAdvanced(this); // For large animated images, go ahead and throw away frames as we go to // save footprint. int frameSize = m_size.width() * m_size.height() * 4; if (frameCount() * frameSize > cLargeAnimationCutoff) { // Destroy all of our frames and just redecode every time. We save the // current frame since we'll need it in draw() anyway. destroyDecodedData(false, true); } }
TEST_F(BitmapImageDeferredDecodingTest, correctDecodedDataSize) { // When deferred decoding is enabled, requesting any one frame shouldn't // result in decoding any other frames. loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false); frameAtIndex(1); int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame::PixelData)); EXPECT_EQ(frameSize, m_imageObserver->m_lastDecodedSizeChangedDelta); frameAtIndex(0); // Trying to destroy all data except an undecoded frame should go ahead and // destroy all other frames. setCurrentFrame(2); destroyDecodedData(false); EXPECT_EQ(-frameSize * 2, m_imageObserver->m_lastDecodedSizeChangedDelta); }
void BitmapImage::notifyObserverAndTrimDecodedData() { // Notify our observer that the animation has advanced. imageObserver()->animationAdvanced(this); // For large animated images, go ahead and throw away frames as we go to save // footprint. int frameSize = m_size.width() * m_size.height() * 4; if (frameCount() * frameSize > cLargeAnimationCutoff) { // Destroy all of our frames and just redecode every time. destroyDecodedData(); // Go ahead and decode the next frame. frameAtIndex(m_currentFrame); } }
TEST_F(BitmapImageTest, correctDecodedDataSize) { // When requesting a frame of a multi-frame GIF causes another frame to be // decoded as well, both frames' sizes should be reported by the source and // thus included in the decoded size changed notification. loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false); frameAtIndex(1); int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame::PixelData)); EXPECT_EQ(frameSize * 2, m_imageObserver->m_lastDecodedSizeChangedDelta); // Trying to destroy all data except an undecoded frame should cause the // decoder to seek backwards and preserve the most recent previous frame // necessary to decode that undecoded frame, and destroy all other frames. setCurrentFrame(2); destroyDecodedData(false); EXPECT_EQ(-frameSize, m_imageObserver->m_lastDecodedSizeChangedDelta); }
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll) { // Animated images >5MB are considered large enough that we'll only hang on // to one frame at a time. static const unsigned cLargeAnimationCutoff = 5242880; // If we have decoded frames but there is no encoded data, we shouldn't destroy // the decoded image since we won't be able to reconstruct it later. if (!data() && m_frames.size()) return; unsigned allFrameBytes = 0; for (size_t i = 0; i < m_frames.size(); ++i) allFrameBytes += m_frames[i].m_frameBytes; if (allFrameBytes > cLargeAnimationCutoff) destroyDecodedData(destroyAll); }
bool Image::setNativeData(NativeBytePtr data, bool allDataReceived) { #if __APPLE__ // FIXME: Will go away when we make PDF a subclass. if (m_isPDF) { if (allDataReceived && !m_PDFDoc) m_PDFDoc = new PDFDocumentImage(data); return m_PDFDoc; } #endif destroyDecodedData(true); // Feed all the data we've seen so far to the image decoder. m_source.setData(data, allDataReceived); // Image properties will not be available until the first frame of the file // reaches kCGImageStatusIncomplete. return isSizeAvailable(); }
void Image::advanceAnimation(Timer<Image>* timer) { // Stop the animation. stopAnimation(); // See if anyone is still paying attention to this animation. If not, we don't // advance and will remain suspended at the current frame until the animation is resumed. if (imageObserver()->shouldPauseAnimation(this)) return; m_currentFrame++; if (m_currentFrame >= frameCount()) { m_repetitionsComplete += 1; if (m_repetitionCount && m_repetitionsComplete >= m_repetitionCount) { m_animationFinished = true; m_currentFrame--; return; } m_currentFrame = 0; } // Notify our observer that the animation has advanced. imageObserver()->animationAdvanced(this); // For large animated images, go ahead and throw away frames as we go to save // footprint. int frameSize = m_size.width() * m_size.height() * 4; if (frameCount() * frameSize > cLargeAnimationCutoff) { // Destroy all of our frames and just redecode every time. destroyDecodedData(); // Go ahead and decode the next frame. frameAtIndex(m_currentFrame); } // We do not advance the animation explicitly. We rely on a subsequent draw of the image // to force a request for the next frame via startAnimation(). This allows images that move offscreen while // scrolling to stop animating (thus saving memory from additional decoded frames and // CPU time spent doing the decoding). }
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll) { // Animated images over a certain size are considered large enough that we'll only hang on // to one frame at a time. #if PLATFORM(IOS) const unsigned largeAnimationCutoff = 2097152; #else const unsigned largeAnimationCutoff = 5242880; #endif // If we have decoded frames but there is no encoded data, we shouldn't destroy // the decoded image since we won't be able to reconstruct it later. if (!data() && m_frames.size()) return; unsigned allFrameBytes = 0; for (size_t i = 0; i < m_frames.size(); ++i) allFrameBytes += m_frames[i].usedFrameBytes(); if (allFrameBytes > largeAnimationCutoff) { LOG(Images, "BitmapImage %p destroyDecodedDataIfNecessary destroyingData: allFrameBytes=%u cutoff=%u", this, allFrameBytes, largeAnimationCutoff); destroyDecodedData(destroyAll); } }
void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*) { ASSERT(!hasClients()); destroyDecodedData(); }
void CachedImage::clear() { destroyDecodedData(); m_image = 0; setEncodedSize(0); }
void CachedScript::decodedDataDeletionTimerFired(Timer<CachedScript>*) { destroyDecodedData(); }
void CachedResource::decodedDataDeletionTimerFired() { destroyDecodedData(); }