Commit efd4fb63 authored by oliver@apple.com's avatar oliver@apple.com

2010-01-31 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

        Update checksum.

        * platform/mac/fast/backgrounds/size/backgroundSize16-expected.checksum:
2010-01-31  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::hiqhQualityRepaintTimer):
        (WebCore::RenderBoxModelScaleData::size):
        (WebCore::RenderBoxModelScaleData::setSize):
        (WebCore::RenderBoxModelScaleData::lastPaintTime):
        (WebCore::RenderBoxModelScaleData::setLastPaintTime):
        (WebCore::RenderBoxModelScaleData::useLowQualityScale):
        (WebCore::RenderBoxModelScaleData::transform):
        (WebCore::RenderBoxModelScaleData::setTransform):
        (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: svn://svn.chromium.org/blink/trunk@54113 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent c54e1871
2010-01-31 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
Update checksum.
* platform/mac/fast/backgrounds/size/backgroundSize16-expected.checksum:
2010-01-30 Simon Fraser <simon.fraser@apple.com> 2010-01-30 Simon Fraser <simon.fraser@apple.com>
Reviewed by Adele Peterson. Reviewed by Adele Peterson.
......
22f0d818aa277e9b1f449a4dafaca35f f515f98e1fbcaff744f69733836b2b0b
\ No newline at end of file \ No newline at end of file
2010-01-31 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::hiqhQualityRepaintTimer):
(WebCore::RenderBoxModelScaleData::size):
(WebCore::RenderBoxModelScaleData::setSize):
(WebCore::RenderBoxModelScaleData::lastPaintTime):
(WebCore::RenderBoxModelScaleData::setLastPaintTime):
(WebCore::RenderBoxModelScaleData::useLowQualityScale):
(WebCore::RenderBoxModelScaleData::transform):
(WebCore::RenderBoxModelScaleData::setTransform):
(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-31 Dirk Schulze <krit@webkit.org> 2010-01-31 Dirk Schulze <krit@webkit.org>
Reviewed by Simon Fraser. Reviewed by Simon Fraser.
......
...@@ -411,24 +411,35 @@ void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const ...@@ -411,24 +411,35 @@ void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const
restore(); 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) if (paintingDisabled() || !image)
return; return;
if (useLowQualityScale) {
save();
setImageInterpolationQuality(InterpolationLow);
}
image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op); 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) if (paintingDisabled() || !image)
return; return;
if (useLowQualityScale) {
save();
setImageInterpolationQuality(InterpolationLow);
}
if (hRule == Image::StretchTile && vRule == Image::StretchTile) if (hRule == Image::StretchTile && vRule == Image::StretchTile)
// Just do a scale. // Just do a scale.
return drawImage(image, styleColorSpace, dest, srcRect, op); drawImage(image, styleColorSpace, dest, srcRect, op);
else
image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op); image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
if (useLowQualityScale)
restore();
} }
void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
......
...@@ -226,10 +226,10 @@ namespace WebCore { ...@@ -226,10 +226,10 @@ namespace WebCore {
void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1), void drawImage(Image*, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect = FloatRect(0, 0, -1, -1),
CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false); CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false);
void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, 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, void drawTiledImage(Image*, ColorSpace styleColorSpace, const IntRect& destRect, const IntRect& srcRect,
Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile, Image::TileRule hRule = Image::StretchTile, Image::TileRule vRule = Image::StretchTile,
CompositeOperator = CompositeSourceOver); CompositeOperator = CompositeSourceOver, bool useLowQualityScale = false);
void setImageInterpolationQuality(InterpolationQuality); void setImageInterpolationQuality(InterpolationQuality);
InterpolationQuality imageInterpolationQuality() const; InterpolationQuality imageInterpolationQuality() const;
......
...@@ -323,27 +323,27 @@ public: ...@@ -323,27 +323,27 @@ public:
operator XFORM() const; operator XFORM() const;
#endif #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: private:
// multiply passed 2D point by matrix (assume z=0) // multiply passed 2D point by matrix (assume z=0)
void multVecMatrix(double x, double y, double& dstX, double& dstY) const; void multVecMatrix(double x, double y, double& dstX, double& dstY) const;
// multiply passed 3D point by matrix // multiply passed 3D point by matrix
void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const; void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const;
void setMatrix(const Matrix4 m) void setMatrix(const Matrix4 m)
{ {
if (m && m != m_matrix) if (m && m != m_matrix)
memcpy(m_matrix, m, sizeof(Matrix4)); 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; Matrix4 m_matrix;
}; };
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "RenderInline.h" #include "RenderInline.h"
#include "RenderLayer.h" #include "RenderLayer.h"
#include "RenderView.h" #include "RenderView.h"
#include <wtf/CurrentTime.h>
using namespace std; using namespace std;
...@@ -44,6 +45,140 @@ bool RenderBoxModelObject::s_wasFloating = false; ...@@ -44,6 +45,140 @@ bool RenderBoxModelObject::s_wasFloating = false;
bool RenderBoxModelObject::s_hadLayer = false; bool RenderBoxModelObject::s_hadLayer = false;
bool RenderBoxModelObject::s_layerWasSelfPainting = 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, const TransformationMatrix& transform, double time, bool lowQualityScale)
: m_size(size)
, m_transform(transform)
, 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; }
const TransformationMatrix& transform() const { return m_transform; }
void setTransform(const TransformationMatrix& transform) { m_transform = transform; }
void setUseLowQualityScale(bool b)
{
m_highQualityRepaintTimer.stop();
m_lowQualityScale = b;
if (b)
m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold);
}
private:
IntSize m_size;
TransformationMatrix m_transform;
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);
const TransformationMatrix& currentTransform = context->getCTM();
bool contextIsScaled = !currentTransform.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, currentTransform, 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->transform() == currentTransform) && 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->setTransform(currentTransform);
data->setSize(size);
return data->useLowQualityScale();
}
HashMap<RenderBoxModelObject*, RenderBoxModelScaleData*>* RenderBoxModelScaleObserver::gBoxModelObjects = 0;
void RenderBoxModelObject::highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*)
{
RenderBoxModelScaleObserver::highQualityRepaintTimerFired(this);
}
RenderBoxModelObject::RenderBoxModelObject(Node* node) RenderBoxModelObject::RenderBoxModelObject(Node* node)
: RenderObject(node) : RenderObject(node)
, m_layer(0) , m_layer(0)
...@@ -55,6 +190,7 @@ RenderBoxModelObject::~RenderBoxModelObject() ...@@ -55,6 +190,7 @@ RenderBoxModelObject::~RenderBoxModelObject()
// Our layer should have been destroyed and cleared by now // Our layer should have been destroyed and cleared by now
ASSERT(!hasLayer()); ASSERT(!hasLayer());
ASSERT(!m_layer); ASSERT(!m_layer);
RenderBoxModelScaleObserver::boxModelObjectDestroyed(this);
} }
void RenderBoxModelObject::destroyLayer() void RenderBoxModelObject::destroyLayer()
...@@ -307,6 +443,9 @@ int RenderBoxModelObject::paddingRight(bool) const ...@@ -307,6 +443,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) 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; GraphicsContext* context = paintInfo.context;
if (context->paintingDisabled())
return;
bool includeLeftEdge = box ? box->includeLeftEdge() : true; bool includeLeftEdge = box ? box->includeLeftEdge() : true;
bool includeRightEdge = box ? box->includeRightEdge() : true; bool includeRightEdge = box ? box->includeRightEdge() : true;
int bLeft = includeLeftEdge ? borderLeft() : 0; int bLeft = includeLeftEdge ? borderLeft() : 0;
...@@ -464,7 +603,9 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co ...@@ -464,7 +603,9 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
phase += destRect.location() - destOrigin; phase += destRect.location() - destOrigin;
CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; 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: ...@@ -98,6 +98,8 @@ public:
// Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed // Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed
void destroyLayer(); void destroyLayer();
void highQualityRepaintTimerFired(Timer<RenderBoxModelObject>*);
protected: protected:
void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); 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