Skip to content

Commit

Permalink
Merge r185152 - Subpixel rendering: Composited layer with subpixel ga…
Browse files Browse the repository at this point in the history
…p does not get painted properly when its position changes.

https://bugs.webkit.org/show_bug.cgi?id=145587

Reviewed by Simon Fraser.

The composited layer always snaps to an enclosing device pixel (floors) while the renderer rounds.
At certain positions (for example 0.5px on a 1x display), a gap is formed between the layer(0px) and its renderer(1px).
In such cases, when the the renderer moves to a position (1.1px) where the gap is closed, we need to issue repaint on the layer
in order to get the renderering right.

Source/WebCore:

Test: compositing/child-layer-with-subpixel-gap-needs-repaint-when-parent-moves.html

* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateAfterLayout):
(WebCore::devicePixelFractionGapFromRendererChanged):
(WebCore::RenderLayerBacking::updateGeometry):
* rendering/RenderLayerBacking.h:

LayoutTests:

* compositing/child-layer-with-subpixel-gap-needs-repaint-when-parent-moves-expected.html: Added.
* compositing/child-layer-with-subpixel-gap-needs-repaint-when-parent-moves.html: Added.
  • Loading branch information
alanbaradlay authored and carlosgcampos committed Jul 6, 2015
1 parent 2387ad3 commit cbe91c9
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 1 deletion.
15 changes: 15 additions & 0 deletions LayoutTests/ChangeLog
@@ -1,3 +1,18 @@
2015-06-03 Zalan Bujtas <zalan@apple.com>

Subpixel rendering: Composited layer with subpixel gap does not get painted properly when its position changes.
https://bugs.webkit.org/show_bug.cgi?id=145587

Reviewed by Simon Fraser.

The composited layer always snaps to an enclosing device pixel (floors) while the renderer rounds.
At certain positions (for example 0.5px on a 1x display), a gap is formed between the layer(0px) and its renderer(1px).
In such cases, when the the renderer moves to a position (1.1px) where the gap is closed, we need to issue repaint on the layer
in order to get the renderering right.

* compositing/child-layer-with-subpixel-gap-needs-repaint-when-parent-moves-expected.html: Added.
* compositing/child-layer-with-subpixel-gap-needs-repaint-when-parent-moves.html: Added.

2015-05-30 Zalan Bujtas <zalan@apple.com>

REGRESSION (179771): zooming on facebook images covers image
Expand Down
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<title>This tests that we repaint child layer if the subpixel gap changes between layers when parent layer moves.</title>
<style>
.outer {
position: relative;
left: 0.7px;
height: 200px;
width: 200px;
background-color: rgba(0, 0, 255, 1);
}

.container-background {
position: relative;
transform: translateZ(0);
height: 100px;
width: 100px;
background-color: green;
}

</style>
<body>
<div id=container class=outer>foo
<div class=container-background>bar
</div>
</div>
</body>
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>This tests that we repaint child layer if the subpixel gap changes between layers when parent layer moves.</title>
<style>
.outer {
position: relative;
left: 0.3px;
height: 200px;
width: 200px;
background-color: rgba(0, 0, 255, 1);
}

.container-background {
position: relative;
transform: translateZ(0);
height: 100px;
width: 100px;
background-color: green;
}

</style>
<script>
if (window.testRunner)
testRunner.waitUntilDone();

function runTest() {
setTimeout(function () {
document.getElementById("container").style.left = "0.7px";
if (window.testRunner)
testRunner.notifyDone();
}, 0);
}
window.addEventListener('load', runTest, false);
</script>
<body>
<div id=container class=outer>foo
<div class=container-background>bar
</div>
</div>
</body>
20 changes: 20 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,23 @@
2015-06-03 Zalan Bujtas <zalan@apple.com>

Subpixel rendering: Composited layer with subpixel gap does not get painted properly when its position changes.
https://bugs.webkit.org/show_bug.cgi?id=145587

Reviewed by Simon Fraser.

The composited layer always snaps to an enclosing device pixel (floors) while the renderer rounds.
At certain positions (for example 0.5px on a 1x display), a gap is formed between the layer(0px) and its renderer(1px).
In such cases, when the the renderer moves to a position (1.1px) where the gap is closed, we need to issue repaint on the layer
in order to get the renderering right.

Test: compositing/child-layer-with-subpixel-gap-needs-repaint-when-parent-moves.html

* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateAfterLayout):
(WebCore::devicePixelFractionGapFromRendererChanged):
(WebCore::RenderLayerBacking::updateGeometry):
* rendering/RenderLayerBacking.h:

2015-05-30 Zalan Bujtas <zalan@apple.com>

REGRESSION (179771): zooming on facebook images covers image
Expand Down
13 changes: 12 additions & 1 deletion Source/WebCore/rendering/RenderLayerBacking.cpp
Expand Up @@ -517,7 +517,7 @@ void RenderLayerBacking::updateAfterLayout(UpdateAfterLayoutFlags flags)
}
}

if (flags & NeedsFullRepaint && !paintsIntoWindow() && !paintsIntoCompositedAncestor())
if (flags & NeedsFullRepaint && canIssueSetNeedsDisplay())
setContentsNeedDisplay();
}

Expand Down Expand Up @@ -636,6 +636,13 @@ static LayoutRect clipBox(RenderBox& renderer)
return result;
}

static bool devicePixelFractionGapFromRendererChanged(const LayoutSize& previousDevicePixelFractionFromRenderer, const LayoutSize& currentDevicePixelFractionFromRenderer, float deviceScaleFactor)
{
FloatSize previous = snapSizeToDevicePixel(previousDevicePixelFractionFromRenderer, LayoutPoint(), deviceScaleFactor);
FloatSize current = snapSizeToDevicePixel(currentDevicePixelFractionFromRenderer, LayoutPoint(), deviceScaleFactor);
return previous != current;
}

static FloatSize pixelFractionForLayerPainting(const LayoutPoint& point, float pixelSnappingFactor)
{
LayoutUnit x = point.x();
Expand Down Expand Up @@ -730,6 +737,7 @@ void RenderLayerBacking::updateGeometry()
FloatSize devicePixelOffsetFromRenderer;
LayoutSize devicePixelFractionFromRenderer;
calculateDevicePixelOffsetFromRenderer(rendererOffsetFromGraphicsLayer, devicePixelOffsetFromRenderer, devicePixelFractionFromRenderer, deviceScaleFactor);
LayoutSize oldDevicePixelFractionFromRenderer = m_devicePixelFractionFromRenderer;
m_devicePixelFractionFromRenderer = LayoutSize(-devicePixelFractionFromRenderer.width(), -devicePixelFractionFromRenderer.height());

adjustAncestorCompositingBoundsForFlowThread(ancestorCompositingBounds, compAncestor);
Expand Down Expand Up @@ -984,6 +992,9 @@ void RenderLayerBacking::updateGeometry()

updateAfterWidgetResize();

if (devicePixelFractionGapFromRendererChanged(oldDevicePixelFractionFromRenderer, m_devicePixelFractionFromRenderer, deviceScaleFactor) && canIssueSetNeedsDisplay())
setContentsNeedDisplay();

compositor().updateScrollCoordinatedStatus(m_owningLayer);
}

Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/rendering/RenderLayerBacking.h
Expand Up @@ -331,6 +331,8 @@ class RenderLayerBacking final : public GraphicsLayerClient {
static CSSPropertyID graphicsLayerToCSSProperty(AnimatedPropertyID);
static AnimatedPropertyID cssToGraphicsLayerProperty(CSSPropertyID);

bool canIssueSetNeedsDisplay() const { return !paintsIntoWindow() && !paintsIntoCompositedAncestor(); }

RenderLayer& m_owningLayer;

std::unique_ptr<GraphicsLayer> m_ancestorClippingLayer; // Only used if we are clipped by an ancestor which is not a stacking context.
Expand Down

0 comments on commit cbe91c9

Please sign in to comment.