#include "config.h"
#include "BitmapImage.h"
#include "FloatRect.h"
#include "ImageObserver.h"
#include "IntRect.h"
#include "MIMETypeRegistry.h"
#include "PlatformString.h"
#include "Timer.h"
#include <wtf/CurrentTime.h>
#include <wtf/Vector.h>
namespace WebCore {
static int frameBytes(const IntSize& frameSize)
{
return frameSize.width() * frameSize.height() * 4;
}
BitmapImage::BitmapImage(ImageObserver* observer)
: Image(observer)
, m_currentFrame(0)
, m_frames(0)
, m_frameTimer(0)
, m_repetitionCount(cAnimationNone)
, m_repetitionCountStatus(Unknown)
, m_repetitionsComplete(0)
, m_desiredFrameStartTime(0)
, m_isSolidColor(false)
, m_checkedForSolidColor(false)
, m_animationFinished(false)
, m_allDataReceived(false)
, m_haveSize(false)
, m_sizeAvailable(false)
, m_hasUniformFrameSize(true)
, m_decodedSize(0)
, m_haveFrameCount(false)
, m_frameCount(0)
{
initPlatformData();
}
BitmapImage::~BitmapImage()
{
invalidatePlatformData();
stopAnimation();
}
void BitmapImage::destroyDecodedData(bool destroyAll)
{
int framesCleared = 0;
const size_t clearBeforeFrame = destroyAll ? m_frames.size() : m_currentFrame;
for (size_t i = 0; i < clearBeforeFrame; ++i) {
if (m_frames[i].clear(false))
++framesCleared;
}
destroyMetadataAndNotify(framesCleared);
m_source.clear(destroyAll, clearBeforeFrame, data(), m_allDataReceived);
return;
}
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll)
{
static const unsigned cLargeAnimationCutoff = 5242880;
if (m_frames.size() * frameBytes(m_size) > cLargeAnimationCutoff)
destroyDecodedData(destroyAll);
}
void BitmapImage::destroyMetadataAndNotify(int framesCleared)
{
m_isSolidColor = false;
invalidatePlatformData();
const int deltaBytes = framesCleared * -frameBytes(m_size);
m_decodedSize += deltaBytes;
if (deltaBytes && imageObserver())
imageObserver()->decodedSizeChanged(this, deltaBytes);
}
void BitmapImage::cacheFrame(size_t index)
{
size_t numFrames = frameCount();
ASSERT(m_decodedSize == 0 || numFrames > 1);
if (m_frames.size() < numFrames)
m_frames.grow(numFrames);
m_frames[index].m_frame = m_source.createFrameAtIndex(index);
if (numFrames == 1 && m_frames[index].m_frame)
checkForSolidColor();
m_frames[index].m_haveMetadata = true;
m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
if (repetitionCount(false) != cAnimationNone)
m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size);
if (frameSize != m_size)
m_hasUniformFrameSize = false;
if (m_frames[index].m_frame) {
const int deltaBytes = frameBytes(frameSize);
m_decodedSize += deltaBytes;
if (imageObserver())
imageObserver()->decodedSizeChanged(this, deltaBytes);
}
}
IntSize BitmapImage::size() const
{
if (m_sizeAvailable && !m_haveSize) {
m_size = m_source.size();
m_haveSize = true;
}
return m_size;
}
IntSize BitmapImage::currentFrameSize() const
{
if (!m_currentFrame || m_hasUniformFrameSize)
return size();
return m_source.frameSizeAtIndex(m_currentFrame);
}
bool BitmapImage::dataChanged(bool allDataReceived)
{
destroyMetadataAndNotify((!m_frames.isEmpty() && m_frames[m_frames.size() - 1].clear(true)) ? 1 : 0);
m_allDataReceived = allDataReceived;
m_source.setData(data(), allDataReceived);
m_haveFrameCount = false;
m_hasUniformFrameSize = true;
return isSizeAvailable();
}
String BitmapImage::filenameExtension() const
{
return m_source.filenameExtension();
}
size_t BitmapImage::frameCount()
{
if (!m_haveFrameCount) {
m_haveFrameCount = true;
m_frameCount = m_source.frameCount();
}
return m_frameCount;
}
bool BitmapImage::isSizeAvailable()
{
if (m_sizeAvailable)
return true;
m_sizeAvailable = m_source.isSizeAvailable();
return m_sizeAvailable;
}
NativeImagePtr BitmapImage::frameAtIndex(size_t index)
{
if (index >= frameCount())
return 0;
if (index >= m_frames.size() || !m_frames[index].m_frame)
cacheFrame(index);
return m_frames[index].m_frame;
}
bool BitmapImage::frameIsCompleteAtIndex(size_t index)
{
if (index >= frameCount())
return true;
if (index >= m_frames.size() || !m_frames[index].m_haveMetadata)
cacheFrame(index);
return m_frames[index].m_isComplete;
}
float BitmapImage::frameDurationAtIndex(size_t index)
{
if (index >= frameCount())
return 0;
if (index >= m_frames.size() || !m_frames[index].m_haveMetadata)
cacheFrame(index);
return m_frames[index].m_duration;
}
bool BitmapImage::frameHasAlphaAtIndex(size_t index)
{
if (index >= frameCount())
return true;
if (index >= m_frames.size() || !m_frames[index].m_haveMetadata)
cacheFrame(index);
return m_frames[index].m_hasAlpha;
}
int BitmapImage::repetitionCount(bool imageKnownToBeComplete)
{
if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Uncertain) && imageKnownToBeComplete)) {
m_repetitionCount = m_source.repetitionCount();
m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount == cAnimationNone) ? Certain : Uncertain;
}
return m_repetitionCount;
}
bool BitmapImage::shouldAnimate()
{
return (repetitionCount(false) != cAnimationNone && !m_animationFinished && imageObserver());
}
void BitmapImage::startAnimation(bool catchUpIfNecessary)
{
if (m_frameTimer || !shouldAnimate() || frameCount() <= 1)
return;
const double time = currentTime();
if (!m_desiredFrameStartTime)
m_desiredFrameStartTime = time;
size_t nextFrame = (m_currentFrame + 1) % frameCount();
if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame))
return;
if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_currentFrame >= (frameCount() - 1))
return;
const double currentDuration = frameDurationAtIndex(m_currentFrame);
m_desiredFrameStartTime += currentDuration;
const double cAnimationResyncCutoff = 5 * 60;
if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff)
m_desiredFrameStartTime = time + currentDuration;
if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime < time)
m_desiredFrameStartTime = time;
if (!catchUpIfNecessary || time < m_desiredFrameStartTime) {
m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimation);
m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.));
} else {
for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsCompleteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) {
double frameAfterNextStartTime = m_desiredFrameStartTime + frameDurationAtIndex(nextFrame);
if (time < frameAfterNextStartTime)
break;
if (!internalAdvanceAnimation(true))
return;
m_desiredFrameStartTime = frameAfterNextStartTime;
nextFrame = frameAfterNext;
}
if (internalAdvanceAnimation(false)) {
startAnimation(false);
}
}
}
void BitmapImage::stopAnimation()
{
delete m_frameTimer;
m_frameTimer = 0;
}
void BitmapImage::resetAnimation()
{
stopAnimation();
m_currentFrame = 0;
m_repetitionsComplete = 0;
m_desiredFrameStartTime = 0;
m_animationFinished = false;
destroyDecodedDataIfNecessary(true);
}
void BitmapImage::advanceAnimation(Timer<BitmapImage>*)
{
internalAdvanceAnimation(false);
}
bool BitmapImage::internalAdvanceAnimation(bool skippingFrames)
{
stopAnimation();
if (!skippingFrames && imageObserver()->shouldPauseAnimation(this))
return false;
++m_currentFrame;
bool advancedAnimation = true;
bool destroyAll = false;
if (m_currentFrame >= frameCount()) {
++m_repetitionsComplete;
if (repetitionCount(true) && m_repetitionsComplete >= m_repetitionCount) {
m_animationFinished = true;
m_desiredFrameStartTime = 0;
--m_currentFrame;
advancedAnimation = false;
} else {
m_currentFrame = 0;
destroyAll = true;
}
}
destroyDecodedDataIfNecessary(destroyAll);
if (skippingFrames != advancedAnimation)
imageObserver()->animationAdvanced(this);
return advancedAnimation;
}
}