#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>
#include "SystemTime.h"
namespace WebCore {
static int frameBytes(const IntSize& frameSize)
{
return frameSize.width() * frameSize.height() * 4;
}
BitmapImage::BitmapImage(ImageObserver* observer)
: Image(observer)
, m_imageAnimationDisabled(false)
, m_progressiveLoadChunkTime(0)
, m_progressiveLoadChunkCount(0)
, 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 deltaBytes = 0;
const size_t clearBeforeFrame = destroyAll ? m_frames.size() : m_currentFrame;
for (size_t i = 0; i < clearBeforeFrame; ++i) {
int bytes = m_frames[i].m_bytes;
if (m_frames[i].clear(false))
deltaBytes -= bytes;
}
destroyMetadataAndNotify(deltaBytes);
m_source.clear(destroyAll, clearBeforeFrame, data(), m_allDataReceived);
return;
}
void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll)
{
static const unsigned cLargeAnimationCutoff = 2097152;
if (m_frames.size() * frameBytes(m_size) > cLargeAnimationCutoff)
destroyDecodedData(destroyAll);
}
void BitmapImage::destroyMetadataAndNotify(const int deltaBytes)
{
ASSERT(deltaBytes <= 0);
ASSERT(static_cast<int>(m_decodedSize) + deltaBytes >= 0);
m_isSolidColor = false;
invalidatePlatformData();
m_decodedSize += deltaBytes;
if (deltaBytes && imageObserver())
imageObserver()->decodedSizeChanged(this, deltaBytes);
}
void BitmapImage::cacheFrame(size_t index, float scaleHint)
{
size_t numFrames = frameCount();
ASSERT(m_decodedSize == 0 || numFrames > 1);
if (m_frames.size() < numFrames)
m_frames.grow(numFrames);
if (m_sizeAvailable && imageObserver() && !imageObserver()->shouldDecodeFrame(this, m_size))
return;
m_frames[index].m_frame = m_source.createFrameAtIndex(index, scaleHint, &m_frames[index].m_scale, &m_frames[index].m_bytes);
if (numFrames == 1 && m_frames[index].m_frame)
checkForSolidColor();
#if ENABLE(RESPECT_EXIF_ORIENTATION)
m_frames[index].m_orientation = m_source.orientationAtIndex(index);
#endif
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 = m_frames[index].m_bytes;
m_decodedSize += deltaBytes;
if (imageObserver())
imageObserver()->decodedSizeChanged(this, deltaBytes);
}
}
void BitmapImage::cacheFrameInfo(size_t index)
{
size_t numFrames = frameCount();
if (m_frames.size() < numFrames)
m_frames.resize(numFrames);
ASSERT(!m_frames[index].m_haveInfo);
if (shouldAnimate())
m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
m_frames[index].m_haveInfo = true;
}
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)
{
int deltaBytes = 0;
if (!m_frames.isEmpty()) {
int bytes = m_frames[m_frames.size() - 1].m_bytes;
if (m_frames[m_frames.size() - 1].clear(true))
deltaBytes -= bytes;
}
destroyMetadataAndNotify(deltaBytes);
m_allDataReceived = allDataReceived;
static const double chunkLoadIntervals[] = {0.0, 1.0, 3.0, 6.0, 15.0};
double interval = chunkLoadIntervals[std::min(m_progressiveLoadChunkCount, 4u)];
bool needsUpdate = false;
if (currentTime() - m_progressiveLoadChunkTime > interval) { needsUpdate = true;
m_progressiveLoadChunkTime = currentTime();
m_progressiveLoadChunkCount++;
}
if (needsUpdate || 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)
{
return frameAtIndex(index, 1.0f);
}
NativeImagePtr BitmapImage::frameAtIndex(size_t index, float scaleHint)
{
if (index >= frameCount())
return 0;
if (index >= m_frames.size() || !m_frames[index].m_frame)
cacheFrame(index, scaleHint);
else if (std::min(1.0f, scaleHint) > m_frames[index].m_scale) {
int sizeChange = -m_frames[index].m_bytes;
ASSERT(static_cast<int>(m_decodedSize) + sizeChange >= 0);
m_frames[index].clear(true);
invalidatePlatformData();
m_decodedSize += sizeChange;
if (imageObserver())
imageObserver()->decodedSizeChanged(this, sizeChange);
cacheFrame(index, scaleHint);
}
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_haveInfo)
cacheFrameInfo(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_haveInfo)
cacheFrameInfo(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_haveInfo)
cacheFrameInfo(index);
return m_frames[index].m_hasAlpha;
}
#if ENABLE(RESPECT_EXIF_ORIENTATION)
int BitmapImage::frameOrientationAtIndex(size_t index)
{
if (index >= frameCount())
return 1;
if (index >= m_frames.size() || !m_frames[index].m_haveInfo)
cacheFrameInfo(index);
return m_frames[index].m_orientation;
}
#endif
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;
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();
++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;
}
unsigned BitmapImage::animatedImageSize()
{
if (frameCount() <= 1)
return 0;
return (width() * height() * 4 * frameCount());
}
void BitmapImage::disableImageAnimation()
{
m_imageAnimationDisabled = true;
}
}