This source file includes following definitions.
- m_haveFrameCount
- m_haveFrameCount
- isBitmapImage
- destroyDecodedData
- destroyDecodedDataIfNecessary
- destroyMetadataAndNotify
- cacheFrame
- updateSize
- size
- sizeRespectingOrientation
- currentFrameSize
- getHotSpot
- dataChanged
- filenameExtension
- draw
- draw
- frameCount
- isSizeAvailable
- ensureFrameIsCached
- frameAtIndex
- frameIsCompleteAtIndex
- frameDurationAtIndex
- nativeImageForCurrentFrame
- frameHasAlphaAtIndex
- currentFrameKnownToBeOpaque
- currentFrameOrientation
- frameOrientationAtIndex
- notSolidColor
- repetitionCount
- shouldAnimate
- startAnimation
- stopAnimation
- resetAnimation
- maybeAnimated
- advanceAnimation
- internalAdvanceAnimation
- checkForSolidColor
- mayFillWithSolidColor
- solidColor
#include "config.h"
#include "platform/graphics/BitmapImage.h"
#include "platform/Timer.h"
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/GraphicsContextStateSaver.h"
#include "platform/graphics/ImageObserver.h"
#include "platform/graphics/skia/NativeImageSkia.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "wtf/CurrentTime.h"
#include "wtf/PassRefPtr.h"
#include "wtf/Vector.h"
#include "wtf/text/WTFString.h"
namespace WebCore {
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_frameCount(0)
, m_isSolidColor(false)
, m_checkedForSolidColor(false)
, m_animationFinished(false)
, m_allDataReceived(false)
, m_haveSize(false)
, m_sizeAvailable(false)
, m_hasUniformFrameSize(true)
, m_haveFrameCount(false)
{
}
BitmapImage::BitmapImage(PassRefPtr<NativeImageSkia> nativeImage, ImageObserver* observer)
: Image(observer)
, m_size(nativeImage->bitmap().width(), nativeImage->bitmap().height())
, m_currentFrame(0)
, m_frames(0)
, m_frameTimer(0)
, m_repetitionCount(cAnimationNone)
, m_repetitionCountStatus(Unknown)
, m_repetitionsComplete(0)
, m_frameCount(1)
, m_isSolidColor(false)
, m_checkedForSolidColor(false)
, m_animationFinished(true)
, m_allDataReceived(true)
, m_haveSize(true)
, m_sizeAvailable(true)
, m_haveFrameCount(true)
{
m_sizeRespectingOrientation = m_size;
m_frames.grow(1);
m_frames[0].m_hasAlpha = !nativeImage->bitmap().isOpaque();
m_frames[0].m_frame = nativeImage;
m_frames[0].m_haveMetadata = true;
checkForSolidColor();
}
BitmapImage::~BitmapImage()
{
stopAnimation();
}
bool BitmapImage::isBitmapImage() const
{
return true;
}
void BitmapImage::destroyDecodedData(bool destroyAll)
{
for (size_t i = 0; i < m_frames.size(); ++i) {
m_frames[i].clear(false);
}
destroyMetadataAndNotify(m_source.clearCacheExceptFrame(destroyAll ? kNotFound : m_currentFrame));
}
void BitmapImage::destroyDecodedDataIfNecessary()
{
static const size_t cLargeAnimationCutoff = 5242880;
size_t allFrameBytes = 0;
for (size_t i = 0; i < m_frames.size(); ++i)
allFrameBytes += m_frames[i].m_frameBytes;
if (allFrameBytes > cLargeAnimationCutoff)
destroyDecodedData(false);
}
void BitmapImage::destroyMetadataAndNotify(size_t frameBytesCleared)
{
m_isSolidColor = false;
m_checkedForSolidColor = false;
if (frameBytesCleared && imageObserver())
imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesCleared));
}
void BitmapImage::cacheFrame(size_t index)
{
size_t numFrames = frameCount();
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_orientation = m_source.orientationAtIndex(index);
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);
m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index);
const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size);
if (frameSize != m_size)
m_hasUniformFrameSize = false;
if (m_frames[index].m_frame) {
int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes);
if (imageObserver())
imageObserver()->decodedSizeChanged(this, deltaBytes);
}
}
void BitmapImage::updateSize() const
{
if (!m_sizeAvailable || m_haveSize)
return;
m_size = m_source.size();
m_sizeRespectingOrientation = m_source.size(RespectImageOrientation);
m_haveSize = true;
}
IntSize BitmapImage::size() const
{
updateSize();
return m_size;
}
IntSize BitmapImage::sizeRespectingOrientation() const
{
updateSize();
return m_sizeRespectingOrientation;
}
IntSize BitmapImage::currentFrameSize() const
{
if (!m_currentFrame || m_hasUniformFrameSize)
return size();
IntSize frameSize = m_source.frameSizeAtIndex(m_currentFrame);
return frameSize;
}
bool BitmapImage::getHotSpot(IntPoint& hotSpot) const
{
bool result = m_source.getHotSpot(hotSpot);
return result;
}
bool BitmapImage::dataChanged(bool allDataReceived)
{
TRACE_EVENT0("webkit", "BitmapImage::dataChanged");
unsigned frameBytesCleared = 0;
for (size_t i = 0; i < m_frames.size(); ++i) {
unsigned frameBytes = m_frames[i].m_frameBytes;
if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete)
frameBytesCleared += (m_frames[i].clear(true) ? frameBytes : 0);
}
destroyMetadataAndNotify(frameBytesCleared);
m_allDataReceived = allDataReceived;
m_source.setData(data(), allDataReceived);
m_haveFrameCount = false;
m_hasUniformFrameSize = true;
return isSizeAvailable();
}
String BitmapImage::filenameExtension() const
{
return m_source.filenameExtension();
}
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, blink::WebBlendMode blendMode)
{
draw(ctxt, dstRect, srcRect, compositeOp, blendMode, DoNotRespectImageOrientation);
}
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, blink::WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
{
startAnimation();
RefPtr<NativeImageSkia> bm = nativeImageForCurrentFrame();
if (!bm)
return;
FloatRect normDstRect = adjustForNegativeSize(dstRect);
FloatRect normSrcRect = adjustForNegativeSize(srcRect);
normSrcRect.intersect(FloatRect(0, 0, bm->bitmap().width(), bm->bitmap().height()));
if (normSrcRect.isEmpty() || normDstRect.isEmpty())
return;
ImageOrientation orientation = DefaultImageOrientation;
if (shouldRespectImageOrientation == RespectImageOrientation)
orientation = frameOrientationAtIndex(m_currentFrame);
GraphicsContextStateSaver saveContext(*ctxt, false);
if (orientation != DefaultImageOrientation) {
saveContext.save();
ctxt->translate(normDstRect.x(), normDstRect.y());
normDstRect.setLocation(FloatPoint());
ctxt->concatCTM(orientation.transformFromDefault(normDstRect.size()));
if (orientation.usesWidthAsHeight()) {
normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRect.height(), normDstRect.width());
}
}
bm->draw(ctxt, normSrcRect, normDstRect, WebCoreCompositeToSkiaComposite(compositeOp, blendMode));
if (ImageObserver* observer = imageObserver())
observer->didDraw(this);
}
size_t BitmapImage::frameCount()
{
if (!m_haveFrameCount) {
m_frameCount = m_source.frameCount();
if (m_frameCount) {
m_haveFrameCount = true;
}
}
return m_frameCount;
}
bool BitmapImage::isSizeAvailable()
{
if (m_sizeAvailable)
return true;
m_sizeAvailable = m_source.isSizeAvailable();
return m_sizeAvailable;
}
bool BitmapImage::ensureFrameIsCached(size_t index)
{
if (index >= frameCount())
return false;
if (index >= m_frames.size() || !m_frames[index].m_frame)
cacheFrame(index);
return true;
}
PassRefPtr<NativeImageSkia> BitmapImage::frameAtIndex(size_t index)
{
if (!ensureFrameIsCached(index))
return nullptr;
return m_frames[index].m_frame;
}
bool BitmapImage::frameIsCompleteAtIndex(size_t index)
{
if (index < m_frames.size() && m_frames[index].m_haveMetadata && m_frames[index].m_isComplete)
return true;
return m_source.frameIsCompleteAtIndex(index);
}
float BitmapImage::frameDurationAtIndex(size_t index)
{
if (index < m_frames.size() && m_frames[index].m_haveMetadata)
return m_frames[index].m_duration;
return m_source.frameDurationAtIndex(index);
}
PassRefPtr<NativeImageSkia> BitmapImage::nativeImageForCurrentFrame()
{
return frameAtIndex(currentFrame());
}
bool BitmapImage::frameHasAlphaAtIndex(size_t index)
{
if (m_frames.size() <= index)
return true;
if (m_frames[index].m_haveMetadata)
return m_frames[index].m_hasAlpha;
return m_source.frameHasAlphaAtIndex(index);
}
bool BitmapImage::currentFrameKnownToBeOpaque()
{
return !frameHasAlphaAtIndex(currentFrame());
}
ImageOrientation BitmapImage::currentFrameOrientation()
{
return frameOrientationAtIndex(currentFrame());
}
ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index)
{
if (m_frames.size() <= index)
return DefaultImageOrientation;
if (m_frames[index].m_haveMetadata)
return m_frames[index].m_orientation;
return m_source.orientationAtIndex(index);
}
#if !ASSERT_DISABLED
bool BitmapImage::notSolidColor()
{
return size().width() != 1 || size().height() != 1 || frameCount() > 1;
}
#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 = monotonicallyIncreasingTime();
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.), FROM_HERE);
} 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();
}
bool BitmapImage::maybeAnimated()
{
if (m_animationFinished)
return false;
if (frameCount() > 1)
return true;
return m_source.repetitionCount() != cAnimationNone;
}
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;
if (m_currentFrame >= frameCount()) {
++m_repetitionsComplete;
if (repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsComplete > m_repetitionCount) {
m_animationFinished = true;
m_desiredFrameStartTime = 0;
--m_currentFrame;
advancedAnimation = false;
} else
m_currentFrame = 0;
}
destroyDecodedDataIfNecessary();
if (skippingFrames != advancedAnimation)
imageObserver()->animationAdvanced(this);
return advancedAnimation;
}
void BitmapImage::checkForSolidColor()
{
m_isSolidColor = false;
m_checkedForSolidColor = true;
if (frameCount() > 1)
return;
RefPtr<NativeImageSkia> frame = frameAtIndex(0);
if (frame && size().width() == 1 && size().height() == 1) {
SkAutoLockPixels lock(frame->bitmap());
if (!frame->bitmap().getPixels())
return;
m_isSolidColor = true;
m_solidColor = Color(frame->bitmap().getColor(0, 0));
}
}
bool BitmapImage::mayFillWithSolidColor()
{
if (!m_checkedForSolidColor && frameCount() > 0) {
checkForSolidColor();
ASSERT(m_checkedForSolidColor);
}
return m_isSolidColor && !m_currentFrame;
}
Color BitmapImage::solidColor() const
{
return m_solidColor;
}
}