Commit 4d60cbde authored by oliver@apple.com's avatar oliver@apple.com

2010-01-27 Oliver Hunt <oliver@apple.com>

        Reviewed by Simon Fraser.

        Animated scaling of background-image is too slow
        https://bugs.webkit.org/show_bug.cgi?id=33808

        Implement a version of the RenderImage animated scaling optimisation
        for background images.  Due to the possibility of arbitrary transforms
        being applied to containing elements we explicitly check the current
        CTM of the context for scaling or rotation.

        * platform/graphics/GraphicsContext.cpp:
        (WebCore::GraphicsContext::drawTiledImage):
        * platform/graphics/GraphicsContext.h:
        * platform/graphics/transforms/TransformationMatrix.h:
        (WebCore::TransformationMatrix::isIdentityOrTranslation):
        * rendering/RenderBoxModelObject.cpp:
        (WebCore::RenderBoxModelScaleData::RenderBoxModelScaleData):
        (WebCore::RenderBoxModelScaleData::~RenderBoxModelScaleData):
        (WebCore::RenderBoxModelScaleData::size):
        (WebCore::RenderBoxModelScaleData::time):
        (WebCore::RenderBoxModelScaleData::useLowQualityScale):
        (WebCore::RenderBoxModelScaleData::hiqhQualityRepaintTimer):
        (WebCore::RenderBoxModelScaleData::setSize):
        (WebCore::RenderBoxModelScaleData::setTime):
        (WebCore::RenderBoxModelScaleData::setUseLowQualityScale):
        (WebCore::RenderBoxModelScaleObserver::boxModelObjectDestroyed):
        (WebCore::RenderBoxModelScaleObserver::highQualityRepaintTimerFired):
        (WebCore::RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality):
        (WebCore::RenderBoxModelObject::highQualityRepaintTimerFired):
        (WebCore::RenderBoxModelObject::~RenderBoxModelObject):
        (WebCore::RenderBoxModelObject::paintFillLayerExtended):
        * rendering/RenderBoxModelObject.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@53949 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent d84125a5
2010-01-27 Oliver Hunt <oliver@apple.com>
Reviewed by Simon Fraser.
Animated scaling of background-image is too slow
https://bugs.webkit.org/show_bug.cgi?id=33808
Implement a version of the RenderImage animated scaling optimisation
for background images. Due to the possibility of arbitrary transforms
being applied to containing elements we explicitly check the current
CTM of the context for scaling or rotation.
* platform/graphics/GraphicsContext.cpp:
(WebCore::GraphicsContext::drawTiledImage):
* platform/graphics/GraphicsContext.h:
* platform/graphics/transforms/TransformationMatrix.h:
(WebCore::TransformationMatrix::isIdentityOrTranslation):
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelScaleData::RenderBoxModelScaleData):
(WebCore::RenderBoxModelScaleData::~RenderBoxModelScaleData):
(WebCore::RenderBoxModelScaleData::size):
(WebCore::RenderBoxModelScaleData::time):
(WebCore::RenderBoxModelScaleData::useLowQualityScale):
(WebCore::RenderBoxModelScaleData::hiqhQualityRepaintTimer):
(WebCore::RenderBoxModelScaleData::setSize):
(WebCore::RenderBoxModelScaleData::setTime):
(WebCore::RenderBoxModelScaleData::setUseLowQualityScale):
(WebCore::RenderBoxModelScaleObserver::boxModelObjectDestroyed):
(WebCore::RenderBoxModelScaleObserver::highQualityRepaintTimerFired):
(WebCore::RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality):
(WebCore::RenderBoxModelObject::highQualityRepaintTimerFired):
(WebCore::RenderBoxModelObject::~RenderBoxModelObject):
(WebCore::RenderBoxModelObject::paintFillLayerExtended):
* rendering/RenderBoxModelObject.h:
2010-01-27 Yael Aharon <yael.aharon@nokia.com>
Reviewed by Laszlo Gombos.
......@@ -411,24 +411,35 @@ void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const
restore();
}
void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op)
void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale)
{
if (paintingDisabled() || !image)
return;
if (useLowQualityScale) {
save();
setImageInterpolationQuality(InterpolationLow);
}
image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
if (useLowQualityScale)
restore();
}
void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
{
if (paintingDisabled() || !image)
return;
if (useLowQualityScale) {
save();
setImageInterpolationQuality(InterpolationLow);
}
if (hRule == Image::StretchTile && vRule == Image::StretchTile)
// Just do a scale.
return drawImage(image, styleColorSpace, dest, srcRect, op);
image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
drawImage(image, styleColorSpace, dest, srcRect, op);
else
image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
if (useLowQualityScale)
restore();
}
void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
......
......@@ -220,10 +220,10 @@ namespace WebCore {
void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1),
CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false);
void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize,
CompositeOperator = CompositeSourceOver);
CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false);
void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect,
Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile,
CompositeOperator = CompositeSourceOver);
CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false);
void setImageInterpolationQuality(InterpolationQuality);
InterpolationQuality imageInterpolationQuality() const;
......
......@@ -319,27 +319,27 @@ public:
operator XFORM() const;
#endif
bool isIdentityOrTranslation() const
{
return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0
&& m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0
&& m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0
&& m_matrix[3][3] == 1;
}
private:
// multiply passed 2D point by matrix (assume z=0)
void multVecMatrix(double x, double y, double& dstX, double& dstY) const;
// multiply passed 3D point by matrix
void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const;
void setMatrix(const Matrix4 m)
{
if (m && m != m_matrix)
memcpy(m_matrix, m, sizeof(Matrix4));
}
bool isIdentityOrTranslation() const
{
return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 &&
m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 &&
m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 &&
m_matrix[3][3] == 1;
}
Matrix4 m_matrix;
};
......
......@@ -44,6 +44,134 @@ bool RenderBoxModelObject::s_wasFloating = false;
bool RenderBoxModelObject::s_hadLayer = false;
bool RenderBoxModelObject::s_layerWasSelfPainting = false;
static const double cInterpolationCutoff = 800. * 800.;
static const double cLowQualityTimeThreshold = 0.500; // 500 ms
class RenderBoxModelScaleData : public Noncopyable {
public:
RenderBoxModelScaleData(RenderBoxModelObject* object, const IntSize& size, double time, bool lowQualityScale)
: m_size(size)
, m_lastPaintTime(time)
, m_lowQualityScale(lowQualityScale)
, m_highQualityRepaintTimer(object, &RenderBoxModelObject::highQualityRepaintTimerFired)
{
}
~RenderBoxModelScaleData()
{
m_highQualityRepaintTimer.stop();
}
Timer<RenderBoxModelObject>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; }
const IntSize& size() const { return m_size; }
void setSize(const IntSize& s) { m_size = s; }
double lastPaintTime() const { return m_lastPaintTime; }
void setLastPaintTime(double t) { m_lastPaintTime = t; }
bool useLowQualityScale() const { return m_lowQualityScale; }
void setUseLowQualityScale(bool b)
{
m_highQualityRepaintTimer.stop();
m_lowQualityScale = b;
if (b)
m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold);
}
private:
IntSize m_size;
double m_lastPaintTime;
bool m_lowQualityScale;
Timer<RenderBoxModelObject> m_highQualityRepaintTimer;
};
class RenderBoxModelScaleObserver {
public:
static bool shouldPaintBackgroundAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const IntSize&);
static void boxModelObjectDestroyed(RenderBoxModelObject* object)
{
if (gBoxModelObjects) {
RenderBoxModelScaleData* data = gBoxModelObjects->take(object);
delete data;
if (!gBoxModelObjects->size()) {
delete gBoxModelObjects;
gBoxModelObjects = 0;
}
}
}
static void highQualityRepaintTimerFired(RenderBoxModelObject* object)
{
RenderBoxModelScaleObserver::boxModelObjectDestroyed(object);
object->repaint();
}
static HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* gBoxModelObjects;
};
bool RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const IntSize& size)
{
// If the image is not a bitmap image, then none of this is relevant and we just paint at high
// quality.
if (!image->isBitmapImage())
return false;
// Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
// is actually being scaled.
IntSize imageSize(image->width(), image->height());
// Look ourselves up in the hashtable.
RenderBoxModelScaleData* data = 0;
if (gBoxModelObjects)
data = gBoxModelObjects->get(object);
bool contextIsScaled = !context->getCTM().isIdentityOrTranslation();
if (!contextIsScaled && imageSize == size) {
// There is no scale in effect. If we had a scale in effect before, we can just delete this data.
if (data) {
gBoxModelObjects->remove(object);
delete data;
}
return false;
}
// There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case.
if (object->document()->page()->inLowQualityImageInterpolationMode()) {
double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height());
if (totalPixels > cInterpolationCutoff)
return true;
}
// If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens
// very soon.
if (!data) {
data = new RenderBoxModelScaleData(object, size, currentTime(), false);
if (!gBoxModelObjects)
gBoxModelObjects = new HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>;
gBoxModelObjects->set(object, data);
return false;
}
// We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with.
if (!contextIsScaled && data->size() == size)
return data->useLowQualityScale();
// We have data and our size just changed. If this change happened quickly, go into low quality mode and then set a repaint
// timer to paint in high quality mode. Otherwise it is ok to just paint in high quality mode.
double newTime = currentTime();
data->setUseLowQualityScale(newTime - data->lastPaintTime() < cLowQualityTimeThreshold);
data->setLastPaintTime(newTime);
data->setSize(size);
return data->useLowQualityScale();
}
HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* RenderBoxModelScaleObserver::gBoxModelObjects = 0;
void RenderBoxModelObject::highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*)
{
RenderBoxModelScaleObserver::highQualityRepaintTimerFired(this);
}
RenderBoxModelObject::RenderBoxModelObject(Node* node)
: RenderObject(node)
, m_layer(0)
......@@ -55,6 +183,7 @@ RenderBoxModelObject::~RenderBoxModelObject()
// Our layer should have been destroyed and cleared by now
ASSERT(!hasLayer());
ASSERT(!m_layer);
RenderBoxModelScaleObserver::boxModelObjectDestroyed(this);
}
void RenderBoxModelObject::destroyLayer()
......@@ -307,6 +436,9 @@ int RenderBoxModelObject::paddingRight(bool) const
void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op, RenderObject* backgroundObject)
{
GraphicsContext* context = paintInfo.context;
if (context->paintingDisabled())
return;
bool includeLeftEdge = box ? box->includeLeftEdge() : true;
bool includeRightEdge = box ? box->includeRightEdge() : true;
int bLeft = includeLeftEdge ? borderLeft() : 0;
......@@ -464,7 +596,9 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
phase += destRect.location() - destOrigin;
CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
context->drawTiledImage(bg->image(clientForBackgroundImage, tileSize), style()->colorSpace(), destRect, phase, tileSize, compositeOp);
Image* image = bg->image(clientForBackgroundImage, tileSize);
bool useLowQualityScaling = RenderBoxModelScaleObserver::shouldPaintBackgroundAtLowQuality(context, this, image, destRect.size());
context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling);
}
}
......
......@@ -98,6 +98,8 @@ public:
// Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed
void destroyLayer();
void highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*);
protected:
void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize);
......
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