Title: [171210] trunk
Revision
171210
Author
[email protected]
Date
2014-07-17 18:56:12 -0700 (Thu, 17 Jul 2014)

Log Message

Subpixel rendering: Embedded non-compositing rotate transform paints to wrong position.
https://bugs.webkit.org/show_bug.cgi?id=135028

Reviewed by Simon Fraser.

CTM always translates to where the layer's renderer() is going to paint.
It ensures that the pixel snapped renderer() always end up painting to (0, 0) which is
required to be able to position properly on transformed context.

Source/WebCore:
Test: fast/layers/hidpi-transform-on-child-content-is-mispositioned.html

* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::beginTransparencyLayers):
(WebCore::RenderLayer::clipToRect):
(WebCore::RenderLayer::paintLayerByApplyingTransform):
(WebCore::RenderLayer::paintBackgroundForFragments):
(WebCore::RenderLayer::paintForegroundForFragmentsWithPhase):
(WebCore::RenderLayer::paintOutlineForFragments):
(WebCore::RenderLayer::paintMaskForFragments):
(WebCore::RenderLayer::paintOverflowControlsForFragments):
(WebCore::RenderLayer::calculateClipRects):
* rendering/RenderLayer.h:

LayoutTests:
* fast/layers/hidpi-transform-on-child-content-is-mispositioned-expected.html: Added.
* fast/layers/hidpi-transform-on-child-content-is-mispositioned.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (171209 => 171210)


--- trunk/LayoutTests/ChangeLog	2014-07-18 01:16:40 UTC (rev 171209)
+++ trunk/LayoutTests/ChangeLog	2014-07-18 01:56:12 UTC (rev 171210)
@@ -1,3 +1,17 @@
+2014-07-17  Zalan Bujtas  <[email protected]>
+
+        Subpixel rendering: Embedded non-compositing rotate transform paints to wrong position.
+        https://bugs.webkit.org/show_bug.cgi?id=135028
+
+        Reviewed by Simon Fraser.
+
+        CTM always translates to where the layer's renderer() is going to paint.
+        It ensures that the pixel snapped renderer() always end up painting to (0, 0) which is
+        required to be able to position properly on transformed context.
+
+        * fast/layers/hidpi-transform-on-child-content-is-mispositioned-expected.html: Added.
+        * fast/layers/hidpi-transform-on-child-content-is-mispositioned.html: Added.
+
 2014-07-17  Alexey Proskuryakov  <[email protected]>
 
         fast/repaint/obscured-background-no-repaint.html is very flaky

Added: trunk/LayoutTests/fast/layers/hidpi-transform-on-child-content-is-mispositioned-expected.html (0 => 171210)


--- trunk/LayoutTests/fast/layers/hidpi-transform-on-child-content-is-mispositioned-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/layers/hidpi-transform-on-child-content-is-mispositioned-expected.html	2014-07-18 01:56:12 UTC (rev 171210)
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<style>
+  div {
+    position: absolute;
+    left: 0.3px;
+    top: 0.3px;
+    width: 10px;
+    height: 10px;
+  }
+  
+  .child {
+    border: 0.5px solid red;
+    -webkit-transform: rotate(180deg);
+  }
+</style>
+</head>
+<body>
+<p id="container"></p>
+<script>
+  var container = document.getElementById("container");
+  adjustmentY = 0;
+  for (i = 0; i < 30; ++i) {
+    adjustmentX = 0;
+    adjustmentY += 0.1;
+    for (j = 0; j < 30; ++j) {
+      var e = document.createElement("div");
+      e.style.top = (14 * i) + j * adjustmentY + "px";
+      e.style.left = (14 * j) + i * adjustmentX + "px";
+
+      var c = document.createElement("div");
+      c.className = "child";
+      c.style.top = adjustmentY + "px";
+      c.style.left = adjustmentX + "px";
+
+      e.appendChild(c);
+      container.appendChild(e);
+      adjustmentX += 0.1;
+    }
+  }
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/layers/hidpi-transform-on-child-content-is-mispositioned.html (0 => 171210)


--- trunk/LayoutTests/fast/layers/hidpi-transform-on-child-content-is-mispositioned.html	                        (rev 0)
+++ trunk/LayoutTests/fast/layers/hidpi-transform-on-child-content-is-mispositioned.html	2014-07-18 01:56:12 UTC (rev 171210)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+<style>
+  div {
+    position: absolute;
+    left: 0.3px;
+    top: 0.3px;
+    width: 10px;
+    height: 10px;
+  }
+  
+  .parent {
+    -webkit-transform: translateZ(0);
+  }
+
+  .child {
+    border: 0.5px solid red;
+    -webkit-transform: rotate(180deg);
+  }
+</style>
+</head>
+<body>
+<p id="container"></p>
+<script>
+  var container = document.getElementById("container");
+  adjustmentY = 0;
+  for (i = 0; i < 30; ++i) {
+    adjustmentX = 0;
+    adjustmentY += 0.1;
+    for (j = 0; j < 30; ++j) {
+      var e = document.createElement("div");
+      e.className = "parent";
+      e.style.top = (14 * i) + j * adjustmentY + "px";
+      e.style.left = (14 * j) + i * adjustmentX + "px";
+
+      var c = document.createElement("div");
+      c.className = "child";
+      c.style.top = adjustmentY + "px";
+      c.style.left = adjustmentX + "px";
+
+      e.appendChild(c);
+      container.appendChild(e);
+      adjustmentX += 0.1;
+    }
+  }
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (171209 => 171210)


--- trunk/Source/WebCore/ChangeLog	2014-07-18 01:16:40 UTC (rev 171209)
+++ trunk/Source/WebCore/ChangeLog	2014-07-18 01:56:12 UTC (rev 171210)
@@ -1,3 +1,28 @@
+2014-07-17  Zalan Bujtas  <[email protected]>
+
+        Subpixel rendering: Embedded non-compositing rotate transform paints to wrong position.
+        https://bugs.webkit.org/show_bug.cgi?id=135028
+
+        Reviewed by Simon Fraser.
+
+        CTM always translates to where the layer's renderer() is going to paint.
+        It ensures that the pixel snapped renderer() always end up painting to (0, 0) which is
+        required to be able to position properly on transformed context.
+
+        Test: fast/layers/hidpi-transform-on-child-content-is-mispositioned.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::beginTransparencyLayers):
+        (WebCore::RenderLayer::clipToRect):
+        (WebCore::RenderLayer::paintLayerByApplyingTransform):
+        (WebCore::RenderLayer::paintBackgroundForFragments):
+        (WebCore::RenderLayer::paintForegroundForFragmentsWithPhase):
+        (WebCore::RenderLayer::paintOutlineForFragments):
+        (WebCore::RenderLayer::paintMaskForFragments):
+        (WebCore::RenderLayer::paintOverflowControlsForFragments):
+        (WebCore::RenderLayer::calculateClipRects):
+        * rendering/RenderLayer.h:
+
 2014-07-17  Beth Dakin  <[email protected]>
 
         Fixed position elements are misplaced when a WK1 view has contentInsets set

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (171209 => 171210)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2014-07-18 01:16:40 UTC (rev 171209)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2014-07-18 01:56:12 UTC (rev 171210)
@@ -1694,7 +1694,7 @@
         m_usedTransparency = true;
         context->save();
         LayoutRect adjustedClipRect = paintingExtent(*this, paintingInfo.rootLayer, dirtyRect, paintingInfo.paintBehavior);
-        adjustedClipRect.move(paintingInfo.subPixelAccumulation);
+        adjustedClipRect.move(paintingInfo.subpixelAccumulation);
         FloatRect pixelSnappedClipRect = pixelSnappedForPainting(adjustedClipRect, renderer().document().deviceScaleFactor());
         context->clip(pixelSnappedClipRect);
 
@@ -3589,7 +3589,7 @@
     if (clipRect.rect() != paintingInfo.paintDirtyRect || clipRect.hasRadius()) {
         context->save();
         LayoutRect adjustedClipRect = clipRect.rect();
-        adjustedClipRect.move(paintingInfo.subPixelAccumulation);
+        adjustedClipRect.move(paintingInfo.subpixelAccumulation);
         context->clip(pixelSnappedForPainting(adjustedClipRect, deviceScaleFactor));
     }
 
@@ -3602,7 +3602,7 @@
     for (RenderLayer* layer = rule == IncludeSelfForBorderRadius ? this : parent(); layer; layer = layer->parent()) {
         if (layer->renderer().hasOverflowClip() && layer->renderer().style().hasBorderRadius() && inContainingBlockChain(this, layer)) {
             LayoutRect adjustedClipRect = LayoutRect(toLayoutPoint(layer->offsetFromAncestor(paintingInfo.rootLayer)), layer->size());
-            adjustedClipRect.move(paintingInfo.subPixelAccumulation);
+            adjustedClipRect.move(paintingInfo.subpixelAccumulation);
             context->clipRoundedRect(layer->renderer().style().getRoundedInnerBorderFor(adjustedClipRect).pixelSnappedRoundedRectForPainting(deviceScaleFactor));
         }
 
@@ -4149,21 +4149,21 @@
     LayoutSize offsetFromParent = offsetFromAncestor(paintingInfo.rootLayer);
     offsetFromParent += translationOffset;
     TransformationMatrix transform(renderableTransform(paintingInfo.paintBehavior));
-    FloatSize devicePixelSnappedOffsetFromParent = toFloatSize(roundedForPainting(toLayoutPoint(offsetFromParent), deviceScaleFactor));
+    // Add the subpixel accumulation to the current layer's offset so that we can always snap the translateRight value to where the renderer() is supposed to be painting.
+    LayoutSize offsetForThisLayer = offsetFromParent + paintingInfo.subpixelAccumulation;
+    FloatSize devicePixelSnappedOffsetForThisLayer = toFloatSize(roundedForPainting(toLayoutPoint(offsetForThisLayer), deviceScaleFactor));
+    // We handle accumulated subpixels through nested layers here. Since the context gets translated to device pixels,
+    // all we need to do is add the delta to the accumulated pixels coming from ancestor layers.
     // Translate the graphics context to the snapping position to avoid off-device-pixel positing.
-    transform.translateRight(devicePixelSnappedOffsetFromParent.width(), devicePixelSnappedOffsetFromParent.height());
-    // We handle accumulated subpixels through nested layers here. Since the context gets translated to device pixels,
-    // all we need to do is add the delta to the accumulated pixels coming from ancestor layers. With deep nesting of subpixel positioned
-    // boxes, this could grow to a relatively large number, but the translateRight() balances it.
-    FloatSize delta = offsetFromParent - devicePixelSnappedOffsetFromParent;
-    LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + LayoutSize(delta);
+    transform.translateRight(devicePixelSnappedOffsetForThisLayer.width(), devicePixelSnappedOffsetForThisLayer.height());
     // Apply the transform.
     GraphicsContextStateSaver stateSaver(*context);
     context->concatCTM(transform.toAffineTransform());
 
     // Now do a paint with the root layer shifted to be us.
-    LayerPaintingInfo transformedPaintingInfo(this, LayoutRect(enclosingRectForPainting(transform.inverse().mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor)), paintingInfo.paintBehavior,
-        adjustedSubPixelAccumulation, paintingInfo.subtreePaintRoot, paintingInfo.overlapTestRequests);
+    LayoutSize adjustedSubpixelAccumulation = offsetForThisLayer - LayoutSize(devicePixelSnappedOffsetForThisLayer);
+    LayerPaintingInfo transformedPaintingInfo(this, LayoutRect(enclosingRectForPainting(transform.inverse().mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor)),
+        paintingInfo.paintBehavior, adjustedSubpixelAccumulation, paintingInfo.subtreePaintRoot, paintingInfo.overlapTestRequests);
     paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags);
 }
 
@@ -4419,7 +4419,7 @@
         // Paint the background.
         // FIXME: Eventually we will collect the region from the fragment itself instead of just from the paint info.
         PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseBlockBackground, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
-        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation));
+        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelAccumulation));
 
         if (localPaintingInfo.clipToDirtyRect)
             restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect);
@@ -4494,7 +4494,7 @@
         PaintInfo paintInfo(context, fragment.foregroundRect.rect(), phase, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
         if (phase == PaintPhaseForeground)
             paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests;
-        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation));
+        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelAccumulation));
         
         if (shouldClip)
             restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.foregroundRect);
@@ -4512,7 +4512,7 @@
         // Paint our own outline
         PaintInfo paintInfo(context, fragment.outlineRect.rect(), PaintPhaseSelfOutline, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
         clipToRect(localPaintingInfo, context, fragment.outlineRect, DoNotIncludeSelfForBorderRadius);
-        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation));
+        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelAccumulation));
         restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.outlineRect);
     }
 }
@@ -4531,7 +4531,7 @@
         // Paint the mask.
         // FIXME: Eventually we will collect the region from the fragment itself instead of just from the paint info.
         PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseMask, PaintBehaviorNormal, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
-        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation));
+        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelAccumulation));
         
         if (localPaintingInfo.clipToDirtyRect)
             restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect);
@@ -4543,7 +4543,7 @@
     for (size_t i = 0; i < layerFragments.size(); ++i) {
         const LayerFragment& fragment = layerFragments.at(i);
         clipToRect(localPaintingInfo, context, fragment.backgroundRect);
-        paintOverflowControls(context, roundedIntPoint(toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)),
+        paintOverflowControls(context, roundedIntPoint(toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelAccumulation)),
             pixelSnappedIntRect(fragment.backgroundRect.rect()), true);
         restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect);
     }
@@ -6731,7 +6731,8 @@
         if (shouldClip)
             clipToRect(paintingInfo, context, clipRect);
 
-        flowThreadLayer->paintNamedFlowThreadInsideRegion(context, flowFragment, paintingInfo.paintDirtyRect, fragment.layerBounds.location() + paintingInfo.subPixelAccumulation, paintingInfo.paintBehavior, paintFlags);
+        flowThreadLayer->paintNamedFlowThreadInsideRegion(context, flowFragment, paintingInfo.paintDirtyRect, fragment.layerBounds.location() + paintingInfo.subpixelAccumulation,
+            paintingInfo.paintBehavior, paintFlags);
 
         if (shouldClip)
             restoreClip(context, paintingInfo.paintDirtyRect, clipRect);

Modified: trunk/Source/WebCore/rendering/RenderLayer.h (171209 => 171210)


--- trunk/Source/WebCore/rendering/RenderLayer.h	2014-07-18 01:16:40 UTC (rev 171209)
+++ trunk/Source/WebCore/rendering/RenderLayer.h	2014-07-18 01:56:12 UTC (rev 171210)
@@ -887,11 +887,11 @@
     enum CollectLayersBehavior { StopAtStackingContexts, StopAtStackingContainers };
 
     struct LayerPaintingInfo {
-        LayerPaintingInfo(RenderLayer* inRootLayer, const LayoutRect& inDirtyRect, PaintBehavior inPaintBehavior, const LayoutSize& inSubPixelAccumulation, RenderObject* inSubtreePaintRoot = nullptr, OverlapTestRequestMap* inOverlapTestRequests = nullptr)
+        LayerPaintingInfo(RenderLayer* inRootLayer, const LayoutRect& inDirtyRect, PaintBehavior inPaintBehavior, const LayoutSize& inSubpixelAccumulation, RenderObject* inSubtreePaintRoot = nullptr, OverlapTestRequestMap* inOverlapTestRequests = nullptr)
             : rootLayer(inRootLayer)
             , subtreePaintRoot(inSubtreePaintRoot)
             , paintDirtyRect(inDirtyRect)
-            , subPixelAccumulation(inSubPixelAccumulation)
+            , subpixelAccumulation(inSubpixelAccumulation)
             , overlapTestRequests(inOverlapTestRequests)
             , paintBehavior(inPaintBehavior)
             , clipToDirtyRect(true)
@@ -899,7 +899,7 @@
         RenderLayer* rootLayer;
         RenderObject* subtreePaintRoot; // only paint descendants of this object
         LayoutRect paintDirtyRect; // relative to rootLayer;
-        LayoutSize subPixelAccumulation;
+        LayoutSize subpixelAccumulation;
         OverlapTestRequestMap* overlapTestRequests; // May be null.
         PaintBehavior paintBehavior;
         bool clipToDirtyRect;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to