- Revision
- 114529
- Author
- [email protected]
- Date
- 2012-04-18 10:37:41 -0700 (Wed, 18 Apr 2012)
Log Message
[CSS Filters] Drop-shadow and blur can avoid using full source image
https://bugs.webkit.org/show_bug.cgi?id=81263
Reviewed by Dean Jackson.
Instead of using the full bounding box of the RenderLayer we now compute the exact rectangle that is needed to
compute filter inside the dirty rectangle on screen. That's easy to calculate by reversing the filter outsets.
Even if the element is completely offscreen, but its shadow is in the viewport, we can still compute the source
rectangle that needs to be drawn in order to update the shadow correctly.
No new tests, this change doesn't have visible results and the functionality should
already be covered by previous filter tests.
* rendering/FilterEffectRenderer.cpp:
(WebCore::FilterEffectRenderer::FilterEffectRenderer):
(WebCore::FilterEffectRenderer::build): Save the outsets of the filters, so that we can use them in computeSourceImageRectForDirtyRect.
(WebCore::FilterEffectRenderer::updateBackingStoreRect): Only allocate a new source image if the size of the source rectangle changed.
(WebCore::FilterEffectRenderer::allocateBackingStoreIfNeeded): There's no need to call clearIntermediateResults() in this function. We do that after
the filter is computed.
(WebCore::FilterEffectRenderer::computeSourceImageRectForDirtyRect):
(WebCore):
(WebCore::FilterEffectRendererHelper::prepareFilterEffect):
(WebCore::FilterEffectRendererHelper::beginFilterEffect):
* rendering/FilterEffectRenderer.h:
(FilterEffectRendererHelper):
(WebCore::FilterEffectRendererHelper::repaintRect):
(FilterEffectRenderer):
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::paintLayerContents):
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (114528 => 114529)
--- trunk/Source/WebCore/ChangeLog 2012-04-18 17:23:15 UTC (rev 114528)
+++ trunk/Source/WebCore/ChangeLog 2012-04-18 17:37:41 UTC (rev 114529)
@@ -1,3 +1,35 @@
+2012-04-18 Alexandru Chiculita <[email protected]>
+
+ [CSS Filters] Drop-shadow and blur can avoid using full source image
+ https://bugs.webkit.org/show_bug.cgi?id=81263
+
+ Reviewed by Dean Jackson.
+
+ Instead of using the full bounding box of the RenderLayer we now compute the exact rectangle that is needed to
+ compute filter inside the dirty rectangle on screen. That's easy to calculate by reversing the filter outsets.
+ Even if the element is completely offscreen, but its shadow is in the viewport, we can still compute the source
+ rectangle that needs to be drawn in order to update the shadow correctly.
+
+ No new tests, this change doesn't have visible results and the functionality should
+ already be covered by previous filter tests.
+
+ * rendering/FilterEffectRenderer.cpp:
+ (WebCore::FilterEffectRenderer::FilterEffectRenderer):
+ (WebCore::FilterEffectRenderer::build): Save the outsets of the filters, so that we can use them in computeSourceImageRectForDirtyRect.
+ (WebCore::FilterEffectRenderer::updateBackingStoreRect): Only allocate a new source image if the size of the source rectangle changed.
+ (WebCore::FilterEffectRenderer::allocateBackingStoreIfNeeded): There's no need to call clearIntermediateResults() in this function. We do that after
+ the filter is computed.
+ (WebCore::FilterEffectRenderer::computeSourceImageRectForDirtyRect):
+ (WebCore):
+ (WebCore::FilterEffectRendererHelper::prepareFilterEffect):
+ (WebCore::FilterEffectRendererHelper::beginFilterEffect):
+ * rendering/FilterEffectRenderer.h:
+ (FilterEffectRendererHelper):
+ (WebCore::FilterEffectRendererHelper::repaintRect):
+ (FilterEffectRenderer):
+ * rendering/RenderLayer.cpp:
+ (WebCore::RenderLayer::paintLayerContents):
+
2012-04-18 Dominik Röttsches <[email protected]>
[EFL][DRT] @font-face support fails on EFL
Modified: trunk/Source/WebCore/rendering/FilterEffectRenderer.cpp (114528 => 114529)
--- trunk/Source/WebCore/rendering/FilterEffectRenderer.cpp 2012-04-18 17:23:15 UTC (rev 114528)
+++ trunk/Source/WebCore/rendering/FilterEffectRenderer.cpp 2012-04-18 17:37:41 UTC (rev 114529)
@@ -86,6 +86,10 @@
FilterEffectRenderer::FilterEffectRenderer(FilterEffectObserver* observer)
: m_observer(observer)
+ , m_topOutset(0)
+ , m_rightOutset(0)
+ , m_bottomOutset(0)
+ , m_leftOutset(0)
, m_graphicsBufferAttached(false)
, m_hasFilterThatMovesPixels(false)
{
@@ -114,6 +118,8 @@
#endif
m_hasFilterThatMovesPixels = operations.hasFilterThatMovesPixels();
+ if (m_hasFilterThatMovesPixels)
+ operations.getOutsets(m_topOutset, m_rightOutset, m_bottomOutset, m_leftOutset);
m_effects.clear();
RefPtr<FilterEffect> previousEffect;
@@ -306,7 +312,7 @@
return true;
}
-bool FilterEffectRenderer::updateBackingStore(const FloatRect& filterRect)
+bool FilterEffectRenderer::updateBackingStoreRect(const FloatRect& filterRect)
{
if (!filterRect.isZero() && isFilterSizeValid(filterRect)) {
FloatRect currentSourceRect = sourceImageRect();
@@ -331,17 +337,17 @@
}
#endif
-void FilterEffectRenderer::prepare()
+void FilterEffectRenderer::allocateBackingStoreIfNeeded()
{
// At this point the effect chain has been built, and the
// source image sizes set. We just need to attach the graphic
// buffer if we have not yet done so.
if (!m_graphicsBufferAttached) {
IntSize logicalSize(m_sourceDrawingRegion.width(), m_sourceDrawingRegion.height());
- setSourceImage(ImageBuffer::create(logicalSize, 1, ColorSpaceDeviceRGB, renderingMode()));
+ if (!sourceImage() || sourceImage()->logicalSize() != logicalSize)
+ setSourceImage(ImageBuffer::create(logicalSize, 1, ColorSpaceDeviceRGB, renderingMode()));
m_graphicsBufferAttached = true;
}
- clearIntermediateResults();
}
void FilterEffectRenderer::clearIntermediateResults()
@@ -356,30 +362,46 @@
lastEffect()->apply();
}
-const LayoutRect& FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
+LayoutRect FilterEffectRenderer::computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect)
{
+ // The result of this function is the area in the "filterBoxRect" that needs to be repainted, so that we fully cover the "dirtyRect".
+ LayoutRect rectForRepaint = dirtyRect;
+ if (hasFilterThatMovesPixels()) {
+ // Note that the outsets are reversed here because we are going backwards -> we have the dirty rect and
+ // need to find out what is the rectangle that might influence the result inside that dirty rect.
+ rectForRepaint.move(-m_rightOutset, -m_bottomOutset);
+ rectForRepaint.expand(m_leftOutset + m_rightOutset, m_topOutset + m_bottomOutset);
+ }
+ rectForRepaint.intersect(filterBoxRect);
+ return rectForRepaint;
+}
+
+bool FilterEffectRendererHelper::prepareFilterEffect(RenderLayer* renderLayer, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
+{
ASSERT(m_haveFilterEffect && renderLayer->filter());
m_renderLayer = renderLayer;
- m_dirtyRect = dirtyRect;
- m_dirtyRect.intersect(filterBoxRect);
+ m_repaintRect = dirtyRect;
FilterEffectRenderer* filter = renderLayer->filter();
-
- // Some filters need the whole original area in order to recalculate correctly.
- // Such filters include blur, drop-shadow and shaders. For that reason,
- // we keep the whole image buffer in memory and repaint only dirty areas.
- bool hasFilterThatMovesPixels = filter->hasFilterThatMovesPixels();
- LayoutRect filterSourceRect = hasFilterThatMovesPixels ? filterBoxRect : m_dirtyRect;
+ LayoutRect filterSourceRect = filter->computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
m_paintOffset = filterSourceRect.location();
- filterSourceRect.setLocation(LayoutPoint());
- bool hasUpdatedBackingStore = filter->updateBackingStore(filterSourceRect);
- if (hasFilterThatMovesPixels)
- m_dirtyRect.unite(hasUpdatedBackingStore ? filterBoxRect : layerRepaintRect);
+ if (filterSourceRect.isEmpty()) {
+ // The dirty rect is not in view, just bail out.
+ m_haveFilterEffect = false;
+ return false;
+ }
- filter->prepare();
-
- return m_dirtyRect;
+ bool hasUpdatedBackingStore = filter->updateBackingStoreRect(filterSourceRect);
+ if (filter->hasFilterThatMovesPixels()) {
+ if (hasUpdatedBackingStore)
+ m_repaintRect = filterSourceRect;
+ else {
+ m_repaintRect.unite(layerRepaintRect);
+ m_repaintRect.intersect(filterSourceRect);
+ }
+ }
+ return true;
}
GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(GraphicsContext* oldContext)
@@ -387,6 +409,7 @@
ASSERT(m_renderLayer);
FilterEffectRenderer* filter = m_renderLayer->filter();
+ filter->allocateBackingStoreIfNeeded();
// Paint into the context that represents the SourceGraphic of the filter.
GraphicsContext* sourceGraphicsContext = filter->inputContext();
if (!sourceGraphicsContext || !isFilterSizeValid(filter->filterRegion())) {
@@ -400,8 +423,8 @@
// Translate the context so that the contents of the layer is captuterd in the offscreen memory buffer.
sourceGraphicsContext->save();
sourceGraphicsContext->translate(-m_paintOffset.x(), -m_paintOffset.y());
- sourceGraphicsContext->clearRect(m_dirtyRect);
- sourceGraphicsContext->clip(m_dirtyRect);
+ sourceGraphicsContext->clearRect(m_repaintRect);
+ sourceGraphicsContext->clip(m_repaintRect);
return sourceGraphicsContext;
}
Modified: trunk/Source/WebCore/rendering/FilterEffectRenderer.h (114528 => 114529)
--- trunk/Source/WebCore/rendering/FilterEffectRenderer.h 2012-04-18 17:23:15 UTC (rev 114528)
+++ trunk/Source/WebCore/rendering/FilterEffectRenderer.h 2012-04-18 17:37:41 UTC (rev 114529)
@@ -67,15 +67,16 @@
bool haveFilterEffect() const { return m_haveFilterEffect; }
bool hasStartedFilterEffect() const { return m_savedGraphicsContext; }
- const LayoutRect& prepareFilterEffect(RenderLayer*, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect);
+ bool prepareFilterEffect(RenderLayer*, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect);
GraphicsContext* beginFilterEffect(GraphicsContext* oldContext);
GraphicsContext* applyFilterEffect();
+ const LayoutRect& repaintRect() const { return m_repaintRect; }
private:
GraphicsContext* m_savedGraphicsContext;
RenderLayer* m_renderLayer;
LayoutPoint m_paintOffset;
- LayoutRect m_dirtyRect;
+ LayoutRect m_repaintRect;
bool m_haveFilterEffect;
};
@@ -107,14 +108,15 @@
ImageBuffer* output() const { return lastEffect()->asImageBuffer(); }
bool build(Document*, const FilterOperations&);
- bool updateBackingStore(const FloatRect& filterRect);
+ bool updateBackingStoreRect(const FloatRect& filterRect);
+ void allocateBackingStoreIfNeeded();
void clearIntermediateResults();
- void prepare();
void apply();
IntRect outputRect() const { return lastEffect()->hasResult() ? lastEffect()->requestedRegionOfInputImageData(IntRect(m_filterRegion)) : IntRect(); }
bool hasFilterThatMovesPixels() const { return m_hasFilterThatMovesPixels; }
+ LayoutRect computeSourceImageRectForDirtyRect(const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect);
private:
#if ENABLE(CSS_SHADERS)
@@ -153,6 +155,11 @@
CustomFilterProgramList m_cachedCustomFilterPrograms;
#endif
+ int m_topOutset;
+ int m_rightOutset;
+ int m_bottomOutset;
+ int m_leftOutset;
+
bool m_graphicsBufferAttached;
bool m_hasFilterThatMovesPixels;
};
Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (114528 => 114529)
--- trunk/Source/WebCore/rendering/RenderLayer.cpp 2012-04-18 17:23:15 UTC (rev 114528)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp 2012-04-18 17:37:41 UTC (rev 114529)
@@ -2983,21 +2983,24 @@
LayoutPoint rootLayerOffset;
convertToLayerCoords(rootLayer, rootLayerOffset);
m_filterRepaintRect.move(rootLayerOffset.x(), rootLayerOffset.y());
- LayoutRect filterPaintDirtyRect = filterPainter.prepareFilterEffect(this, calculateLayerBounds(this, rootLayer, 0), parentPaintDirtyRect, m_filterRepaintRect);
- m_filterRepaintRect = IntRect();
- // Rewire the old context to a memory buffer, so that we can capture the contents of the layer.
- // NOTE: We saved the old context in the "transparencyLayerContext" local variable, to be able to start a transparency layer
- // on the original context and avoid duplicating "beginFilterEffect" after each transpareny layer call. Also, note that
- // beginTransparencyLayers will only create a single lazy transparency layer, even though it is called twice in this method.
- context = filterPainter.beginFilterEffect(context);
-
- // Check that we didn't fail to allocate the graphics context for the offscreen buffer.
- if (filterPainter.hasStartedFilterEffect()) {
- paintDirtyRect = filterPaintDirtyRect;
- // If the filter needs the full source image, we need to avoid using the clip rectangles.
- // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly.
- // Note that we will still apply the clipping on the final rendering of the filter.
- useClipRect = !filter()->hasFilterThatMovesPixels();
+ if (filterPainter.prepareFilterEffect(this, calculateLayerBounds(this, rootLayer, 0), parentPaintDirtyRect, m_filterRepaintRect)) {
+ // Now we know for sure, that the source image will be updated, so we can revert our tracking repaint rect back to zero.
+ m_filterRepaintRect = IntRect();
+
+ // Rewire the old context to a memory buffer, so that we can capture the contents of the layer.
+ // NOTE: We saved the old context in the "transparencyLayerContext" local variable, to be able to start a transparency layer
+ // on the original context and avoid duplicating "beginFilterEffect" after each transpareny layer call. Also, note that
+ // beginTransparencyLayers will only create a single lazy transparency layer, even though it is called twice in this method.
+ context = filterPainter.beginFilterEffect(context);
+
+ // Check that we didn't fail to allocate the graphics context for the offscreen buffer.
+ if (filterPainter.hasStartedFilterEffect()) {
+ paintDirtyRect = filterPainter.repaintRect();
+ // If the filter needs the full source image, we need to avoid using the clip rectangles.
+ // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly.
+ // Note that we will still apply the clipping on the final rendering of the filter.
+ useClipRect = !filter()->hasFilterThatMovesPixels();
+ }
}
}
#endif