Commit ac1c9846 authored by cblume's avatar cblume Committed by Commit bot

Move GIF decoder's aggressive purge into ImageDecoder

Other animated image formats should have the same aggressive purge
behavior.

So move it out of the GIF decoder and into ImageDecoder.

BUG=651666

Review-Url: https://codereview.chromium.org/2376033005
Cr-Commit-Position: refs/heads/master@{#423691}
parent 255d6d6d
......@@ -234,6 +234,40 @@ void ImageDecoder::clearFrameBuffer(size_t frameIndex) {
m_frameBufferCache[frameIndex].clearPixelData();
}
void ImageDecoder::updateAggressivePurging(size_t index) {
if (m_purgeAggressively)
return;
// We don't want to cache so much that we cause a memory issue.
//
// If we used a LRU cache we would fill it and then on next animation loop
// we would need to decode all the frames again -- the LRU would give no
// benefit and would consume more memory.
// So instead, simply purge unused frames if caching all of the frames of
// the image would use more memory than the image decoder is allowed
// (m_maxDecodedBytes) or would overflow 32 bits..
//
// As we decode we will learn the total number of frames, and thus total
// possible image memory used.
const uint64_t frameArea = decodedSize().area();
const uint64_t frameMemoryUsage = frameArea * 4; // 4 bytes per pixel
if (frameMemoryUsage / 4 != frameArea) { // overflow occurred
m_purgeAggressively = true;
return;
}
const uint64_t totalMemoryUsage = frameMemoryUsage * index;
if (totalMemoryUsage / frameMemoryUsage != index) { // overflow occurred
m_purgeAggressively = true;
return;
}
if (totalMemoryUsage > m_maxDecodedBytes) {
m_purgeAggressively = true;
}
}
size_t ImageDecoder::findRequiredPreviousFrame(size_t frameIndex,
bool frameRectIsOpaque) {
ASSERT(frameIndex <= m_frameBufferCache.size());
......
......@@ -279,7 +279,8 @@ class PLATFORM_EXPORT ImageDecoder {
: m_premultiplyAlpha(alphaOption == AlphaPremultiplied),
m_ignoreGammaAndColorProfile(colorOptions ==
GammaAndColorProfileIgnored),
m_maxDecodedBytes(maxDecodedBytes) {}
m_maxDecodedBytes(maxDecodedBytes),
m_purgeAggressively(false) {}
// Calculates the most recent frame whose image data may be needed in
// order to decode frame |frameIndex|, based on frame disposal methods
......@@ -332,6 +333,13 @@ class PLATFORM_EXPORT ImageDecoder {
// memory devices.
const size_t m_maxDecodedBytes;
// While decoding, we may learn that there are so many animation frames that
// we would go beyond our cache budget.
// If that happens, m_purgeAggressively is set to true. This signals
// future decodes to purge old frames as it goes.
void updateAggressivePurging(size_t index);
bool m_purgeAggressively;
private:
enum class SniffResult { JPEG, PNG, GIF, WEBP, ICO, BMP, Invalid };
......
......@@ -36,7 +36,6 @@ GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption,
GammaAndColorProfileOption colorOptions,
size_t maxDecodedBytes)
: ImageDecoder(alphaOption, colorOptions, maxDecodedBytes),
m_purgeAggressively(false),
m_repetitionCount(cAnimationLoopOnce) {}
GIFImageDecoder::~GIFImageDecoder() {}
......@@ -403,47 +402,4 @@ bool GIFImageDecoder::initFrameBuffer(size_t frameIndex) {
m_currentBufferSawAlpha = false;
return true;
}
void GIFImageDecoder::updateAggressivePurging(size_t index) {
if (m_purgeAggressively)
return;
// We don't want to cache so much that we cause a memory issue.
//
// If we used a LRU cache we would fill it and then on next animation loop
// we would need to decode all the frames again -- the LRU would give no
// benefit and would consume more memory.
// So instead, simply purge unused frames if caching all of the frames of
// the image would use more memory than the image decoder is allowed
// (m_maxDecodedBytes) or would overflow 32 bits..
//
// As we decode we will learn the total number of frames, and thus total
// possible image memory used.
const uint64_t frameArea = decodedSize().area();
// We are about to multiply by 4, which may require an extra bit of storage
bool wouldOverflow = frameArea > (UINT64_C(1) << 62);
if (wouldOverflow) {
m_purgeAggressively = true;
return;
}
const uint64_t frameMemoryUsage = frameArea * 4; // 4 bytes per pixel
// We are about to multiply by a size_t, which does not have a fixed
// size.
// To simplify things, let's make sure our per-frame memory usage and
// index can be stored in 32 bits and store the multiplicand in a 64-bit
// number.
wouldOverflow =
(frameMemoryUsage > (UINT32_C(1) << 31)) || (index > (UINT32_C(1) << 31));
if (wouldOverflow) {
m_purgeAggressively = true;
return;
}
const uint64_t totalMemoryUsage = frameMemoryUsage * index;
if (totalMemoryUsage > m_maxDecodedBytes) {
m_purgeAggressively = true;
}
}
} // namespace blink
......@@ -92,10 +92,7 @@ class PLATFORM_EXPORT GIFImageDecoder final : public ImageDecoder {
// Like clearCacheExceptFrame(), but preserves two frames instead of one.
size_t clearCacheExceptTwoFrames(size_t, size_t);
void updateAggressivePurging(size_t index);
bool m_currentBufferSawAlpha;
bool m_purgeAggressively;
mutable int m_repetitionCount;
std::unique_ptr<GIFImageReader> m_reader;
};
......
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