#include "config.h"
#include "Image.h"
#include "FloatRect.h"
#include "Image.h"
#include "ImageObserver.h"
#include "IntRect.h"
#include "PlatformString.h"
#include "Timer.h"
#include <wtf/Vector.h>
#if __APPLE__
#include "PDFDocumentImage.h"
#endif
namespace WebCore {
const unsigned cLargeAnimationCutoff = 5242880;
Image::Image()
: m_currentFrame(0), m_frames(0), m_imageObserver(0),
m_frameTimer(0), m_repetitionCount(0), m_repetitionsComplete(0),
m_isSolidColor(false), m_animatingImageType(true), m_animationFinished(false),
m_haveSize(false), m_sizeAvailable(false), m_decodedSize(0)
{
initNativeData();
}
Image::Image(ImageObserver* observer, bool isPDF)
: m_currentFrame(0), m_frames(0), m_imageObserver(0),
m_frameTimer(0), m_repetitionCount(0), m_repetitionsComplete(0),
m_isSolidColor(false), m_animatingImageType(true), m_animationFinished(false),
m_haveSize(false), m_sizeAvailable(false), m_decodedSize(0)
{
initNativeData();
#if __APPLE__
if (isPDF)
setIsPDF(); #endif
m_imageObserver = observer;
}
Image::~Image()
{
m_imageObserver = 0;
destroyDecodedData();
stopAnimation();
destroyNativeData();
}
void Image::destroyDecodedData(bool incremental)
{
if (m_frames.size()) {
int sizeChange = 0;
for (unsigned i = incremental ? m_frames.size() - 1 : 0; i < m_frames.size(); i++) {
if (m_frames[i].m_frame) {
sizeChange -= m_frames[i].m_bytes;
m_frames[i].clear();
}
}
m_isSolidColor = false;
invalidateNativeData();
if (sizeChange) {
if (imageObserver())
imageObserver()->decodedSizeChanging(this, sizeChange);
m_decodedSize += sizeChange;
if (imageObserver())
imageObserver()->decodedSizeChanged(this, sizeChange);
}
if (!incremental) {
m_source.clear();
setData(true);
}
}
}
void Image::cacheFrame(size_t index, float scaleHint)
{
size_t numFrames = frameCount();
ASSERT(m_decodedSize == 0 || numFrames > 1);
if (!m_frames.size() && shouldAnimate()) {
m_repetitionCount = m_source.repetitionCount();
if (m_repetitionCount == cAnimationNone)
m_animatingImageType = false;
}
if (m_frames.size() < numFrames)
m_frames.resize(numFrames);
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 (!m_frames[index].m_haveInfo)
cacheFrameInfo(index);
int sizeChange = m_frames[index].m_bytes;
if (sizeChange) {
if (imageObserver())
imageObserver()->decodedSizeChanging(this, sizeChange);
m_decodedSize += sizeChange;
if (imageObserver())
imageObserver()->decodedSizeChanged(this, sizeChange);
}
}
void Image::cacheFrameInfo(size_t index)
{
size_t numFrames = frameCount();
ASSERT(!m_frames[index].m_haveInfo);
if (m_frames.size() < numFrames)
m_frames.resize(numFrames);
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;
}
bool Image::isNull() const
{
return size().isEmpty();
}
IntSize Image::size() const
{
#if __APPLE__
if (m_isPDF) {
if (m_PDFDoc) {
FloatSize size = m_PDFDoc->size();
return IntSize((int)size.width(), (int)size.height());
}
} else
#endif
if (m_sizeAvailable && !m_haveSize) {
m_size = m_source.size();
m_haveSize = true;
}
return m_size;
}
bool Image::setData(bool allDataReceived)
{
int length = m_data.size();
if (!length)
return true;
#ifdef kImageBytesCutoff
if (length > kImageBytesCutoff) {
length = kImageBytesCutoff;
allDataReceived = false;
}
#endif
#if __APPLE__
CFDataRef data = CFDataCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(m_data.data()), length, kCFAllocatorNull);
bool result = setNativeData(data, allDataReceived);
CFRelease(data);
#else
bool result = setNativeData(&m_data, allDataReceived);
#endif
return result;
}
bool Image::setNativeData(NativeBytePtr data, bool allDataReceived)
{
#if __APPLE__
if (m_isPDF) {
if (allDataReceived && !m_PDFDoc)
m_PDFDoc = new PDFDocumentImage(data);
return m_PDFDoc;
}
#endif
destroyDecodedData(true);
m_source.setData(data, allDataReceived);
return isSizeAvailable();
}
size_t Image::frameCount()
{
return m_source.frameCount();
}
bool Image::isSizeAvailable()
{
if (m_sizeAvailable)
return true;
m_sizeAvailable = m_source.isSizeAvailable();
return m_sizeAvailable;
}
NativeImagePtr Image::frameAtIndex(size_t index)
{
return frameAtIndex(index, 1.0f);
}
NativeImagePtr Image::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;
m_frames[index].clear();
invalidateNativeData();
if (imageObserver())
imageObserver()->decodedSizeChanging(this, sizeChange);
m_decodedSize += sizeChange;
if (imageObserver())
imageObserver()->decodedSizeChanged(this, sizeChange);
cacheFrame(index, scaleHint);
}
return m_frames[index].m_frame;
}
float Image::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 Image::frameHasAlphaAtIndex(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_hasAlpha;
}
bool Image::shouldAnimate()
{
bool shouldAnimateImage = false;
if (m_animatingImageType && frameCount() > 1 && !m_animationFinished && m_imageObserver) {
if (width() * height() * 4 * frameCount() < 2097152)
shouldAnimateImage = true;
}
return shouldAnimateImage;
}
void Image::startAnimation()
{
if (m_frameTimer || !shouldAnimate() || frameCount() <= 1)
return;
if (!m_source.frameIsCompleteAtIndex(m_currentFrame))
return;
m_frameTimer = new Timer<Image>(this, &Image::advanceAnimation);
m_frameTimer->startOneShot(frameDurationAtIndex(m_currentFrame));
}
void Image::stopAnimation()
{
delete m_frameTimer;
m_frameTimer = 0;
}
void Image::resetAnimation()
{
stopAnimation();
m_currentFrame = 0;
m_repetitionsComplete = 0;
m_animationFinished = false;
int frameSize = m_size.width() * m_size.height() * 4;
if (frameCount() * frameSize > cLargeAnimationCutoff)
destroyDecodedData();
}
void Image::advanceAnimation(Timer<Image>* timer)
{
stopAnimation();
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;
}
imageObserver()->animationAdvanced(this);
int frameSize = m_size.width() * m_size.height() * 4;
if (frameCount() * frameSize > cLargeAnimationCutoff) {
destroyDecodedData();
frameAtIndex(m_currentFrame);
}
}
IntRect Image::rect() const
{
return IntRect(IntPoint(), size());
}
int Image::width() const
{
return size().width();
}
int Image::height() const
{
return size().height();
}
}