Title: [113606] trunk/Source
Revision
113606
Author
[email protected]
Date
2012-04-09 12:31:56 -0700 (Mon, 09 Apr 2012)

Log Message

[chromium] Fix layer sorting perspective w if w becomes negative
https://bugs.webkit.org/show_bug.cgi?id=82997

Reviewed by Adrienne Walker.

Source/WebCore:

Unit test added to CCLayerSorterTest.cpp.

This is a follow-up patch after r113364. That other patch
implemented proper clipping so that perspective transforms do not
accidentally make geometry disappear. This patch fixes a similar
problem in the layer sorter code so that layers do not
accidentally get sorted incorrectly.

* platform/graphics/chromium/cc/CCLayerSorter.cpp:
(WebCore::CCLayerSorter::LayerShape::LayerShape):
* platform/graphics/chromium/cc/CCMathUtil.cpp:
(WebCore::computeEnclosingRect):
(WebCore::addVertexToClippedQuad):
(WebCore::CCMathUtil::mapClippedQuad):
(WebCore):
(WebCore::CCMathUtil::computeEnclosingRectOfVertices):
* platform/graphics/chromium/cc/CCMathUtil.h:
(WebCore):
(CCMathUtil):

Source/WebKit/chromium:

* tests/CCLayerSorterTest.cpp:
(WebCore::TEST):
(WebCore):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (113605 => 113606)


--- trunk/Source/WebCore/ChangeLog	2012-04-09 19:31:39 UTC (rev 113605)
+++ trunk/Source/WebCore/ChangeLog	2012-04-09 19:31:56 UTC (rev 113606)
@@ -1,3 +1,30 @@
+2012-04-09  Shawn Singh  <[email protected]>
+
+        [chromium] Fix layer sorting perspective w if w becomes negative
+        https://bugs.webkit.org/show_bug.cgi?id=82997
+
+        Reviewed by Adrienne Walker.
+
+        Unit test added to CCLayerSorterTest.cpp.
+
+        This is a follow-up patch after r113364. That other patch
+        implemented proper clipping so that perspective transforms do not
+        accidentally make geometry disappear. This patch fixes a similar
+        problem in the layer sorter code so that layers do not
+        accidentally get sorted incorrectly.
+
+        * platform/graphics/chromium/cc/CCLayerSorter.cpp:
+        (WebCore::CCLayerSorter::LayerShape::LayerShape):
+        * platform/graphics/chromium/cc/CCMathUtil.cpp:
+        (WebCore::computeEnclosingRect):
+        (WebCore::addVertexToClippedQuad):
+        (WebCore::CCMathUtil::mapClippedQuad):
+        (WebCore):
+        (WebCore::CCMathUtil::computeEnclosingRectOfVertices):
+        * platform/graphics/chromium/cc/CCMathUtil.h:
+        (WebCore):
+        (CCMathUtil):
+
 2012-04-09  Martin Robinson  <[email protected]>
 
         [soup] Crash while loading http://www.jusco.cn

Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.cpp (113605 => 113606)


--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.cpp	2012-04-09 19:31:39 UTC (rev 113605)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerSorter.cpp	2012-04-09 19:31:56 UTC (rev 113606)
@@ -27,6 +27,7 @@
 #include "cc/CCLayerSorter.h"
 
 #include "TransformationMatrix.h"
+#include "cc/CCMathUtil.h"
 #include "cc/CCRenderSurface.h"
 #include <limits.h>
 #include <wtf/Deque.h>
@@ -158,9 +159,31 @@
                         FloatPoint(-width * 0.5, -height * 0.5));
 
     // Compute the projection of the layer quad onto the z = 0 plane.
-    projectedQuad = drawTransform.mapQuad(layerQuad);
-    projectedBounds = projectedQuad.boundingBox();
 
+    FloatPoint clippedQuad[8];
+    int numVerticesInClippedQuad;
+    CCMathUtil::mapClippedQuad(drawTransform, layerQuad, clippedQuad, numVerticesInClippedQuad);
+
+    if (numVerticesInClippedQuad < 3) {
+        projectedBounds = FloatRect();
+        return;
+    }
+
+    projectedBounds = CCMathUtil::computeEnclosingRectOfVertices(clippedQuad, numVerticesInClippedQuad);
+
+    // NOTE: it will require very significant refactoring and overhead to deal with
+    // generalized polygons or multiple quads per layer here. For the sake of layer
+    // sorting it is equally correct to take a subsection of the polygon that can be made
+    // into a quad. This will only be incorrect in the case of intersecting layers, which
+    // are not supported yet anyway.
+    projectedQuad.setP1(clippedQuad[0]);
+    projectedQuad.setP2(clippedQuad[1]);
+    projectedQuad.setP3(clippedQuad[2]);
+    if (numVerticesInClippedQuad >= 4)
+        projectedQuad.setP4(clippedQuad[3]);
+    else
+        projectedQuad.setP4(clippedQuad[2]); // this will be a degenerate quad that is actually a triangle.
+
     // Compute the normal of the layer's plane.
     FloatPoint3D c1 = drawTransform.mapPoint(FloatPoint3D(0, 0, 0));
     FloatPoint3D c2 = drawTransform.mapPoint(FloatPoint3D(0, 1, 0));

Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCMathUtil.cpp (113605 => 113606)


--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCMathUtil.cpp	2012-04-09 19:31:39 UTC (rev 113605)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCMathUtil.cpp	2012-04-09 19:31:56 UTC (rev 113606)
@@ -131,12 +131,23 @@
     ymax = std::max(p.y(), ymax);
 }
 
-static FloatRect computeEnclosingRectOfClippedQuad(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4)
+static FloatRect computeEnclosingRect(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4)
 {
     // This function performs clipping as necessary and computes the enclosing 2d
     // FloatRect of the vertices. Doing these two steps simultaneously allows us to avoid
     // the overhead of storing an unknown number of clipped vertices.
 
+    // If no vertices on the quad are clipped, then we can simply return the enclosing rect directly.
+    bool somethingClipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped() || h4.shouldBeClipped();
+    if (!somethingClipped) {
+        FloatQuad mappedQuad = FloatQuad(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
+        return mappedQuad.boundingBox();
+    }
+
+    bool everythingClipped = h1.shouldBeClipped() && h2.shouldBeClipped() && h3.shouldBeClipped() && h4.shouldBeClipped();
+    if (everythingClipped)
+        return FloatRect();
+
     float xmin = std::numeric_limits<float>::max();
     float xmax = std::numeric_limits<float>::min();
     float ymin = std::numeric_limits<float>::max();
@@ -169,16 +180,10 @@
     return FloatRect(FloatPoint(xmin, ymin), FloatSize(xmax - xmin, ymax - ymin));
 }
 
-static FloatRect computeEnclosingRect(const HomogeneousCoordinate& h1, const HomogeneousCoordinate& h2, const HomogeneousCoordinate& h3, const HomogeneousCoordinate& h4)
+static inline void addVertexToClippedQuad(const FloatPoint& newVertex, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad)
 {
-    // If no vertices on the quad are clipped, then we can simply return the enclosing rect directly.
-    bool clipped = h1.shouldBeClipped() || h2.shouldBeClipped() || h3.shouldBeClipped() || h4.shouldBeClipped();
-    if (!clipped) {
-        FloatQuad mappedQuad = FloatQuad(h1.cartesianPoint2d(), h2.cartesianPoint2d(), h3.cartesianPoint2d(), h4.cartesianPoint2d());
-        return mappedQuad.boundingBox();
-    }
-
-    return computeEnclosingRectOfClippedQuad(h1, h2, h3, h4);
+    clippedQuad[numVerticesInClippedQuad] = newVertex;
+    numVerticesInClippedQuad++;
 }
 
 IntRect CCMathUtil::mapClippedRect(const TransformationMatrix& transform, const IntRect& srcRect)
@@ -216,6 +221,60 @@
     return computeEnclosingRect(h1, h2, h3, h4);
 }
 
+void CCMathUtil::mapClippedQuad(const TransformationMatrix& transform, const FloatQuad& srcQuad, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad)
+{
+    HomogeneousCoordinate h1 = mapPoint(transform, srcQuad.p1());
+    HomogeneousCoordinate h2 = mapPoint(transform, srcQuad.p2());
+    HomogeneousCoordinate h3 = mapPoint(transform, srcQuad.p3());
+    HomogeneousCoordinate h4 = mapPoint(transform, srcQuad.p4());
+
+    // The order of adding the vertices to the array is chosen so that clockwise / counter-clockwise orientation is retained.
+
+    numVerticesInClippedQuad = 0;
+
+    if (!h1.shouldBeClipped())
+        addVertexToClippedQuad(h1.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (h1.shouldBeClipped() ^ h2.shouldBeClipped())
+        addVertexToClippedQuad(computeClippedPointForEdge(h1, h2).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (!h2.shouldBeClipped())
+        addVertexToClippedQuad(h2.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (h2.shouldBeClipped() ^ h3.shouldBeClipped())
+        addVertexToClippedQuad(computeClippedPointForEdge(h2, h3).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (!h3.shouldBeClipped())
+        addVertexToClippedQuad(h3.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (h3.shouldBeClipped() ^ h4.shouldBeClipped())
+        addVertexToClippedQuad(computeClippedPointForEdge(h3, h4).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (!h4.shouldBeClipped())
+        addVertexToClippedQuad(h4.cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    if (h4.shouldBeClipped() ^ h1.shouldBeClipped())
+        addVertexToClippedQuad(computeClippedPointForEdge(h4, h1).cartesianPoint2d(), clippedQuad, numVerticesInClippedQuad);
+
+    ASSERT(numVerticesInClippedQuad <= 8);
+}
+
+FloatRect CCMathUtil::computeEnclosingRectOfVertices(FloatPoint vertices[], int numVertices)
+{
+    if (numVertices < 2)
+        return FloatRect();
+
+    float xmin = std::numeric_limits<float>::max();
+    float xmax = std::numeric_limits<float>::min();
+    float ymin = std::numeric_limits<float>::max();
+    float ymax = std::numeric_limits<float>::min();
+
+    for (int i = 0; i < numVertices; ++i)
+        expandBoundsToIncludePoint(xmin, xmax, ymin, ymax, vertices[i]);
+
+    return FloatRect(FloatPoint(xmin, ymin), FloatSize(xmax - xmin, ymax - ymin));
+}
+
 FloatQuad CCMathUtil::mapQuad(const TransformationMatrix& transform, const FloatQuad& q, bool& clipped)
 {
     if (transform.isIdentityOrTranslation()) {

Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCMathUtil.h (113605 => 113606)


--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCMathUtil.h	2012-04-09 19:31:39 UTC (rev 113605)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCMathUtil.h	2012-04-09 19:31:56 UTC (rev 113606)
@@ -28,6 +28,7 @@
 namespace WebCore {
 
 class IntRect;
+class FloatPoint;
 class FloatRect;
 class FloatQuad;
 class TransformationMatrix;
@@ -49,8 +50,15 @@
     static FloatRect mapClippedRect(const TransformationMatrix&, const FloatRect&);
     static FloatRect projectClippedRect(const TransformationMatrix&, const FloatRect&);
 
-    // NOTE: This function does not do correct clipping against w = 0 plane, but it
-    // correctly detects the clipped condition via the boolean clipped.
+    // Returns an array of vertices that represent the clipped polygon. After returning, indexes from
+    // 0 to numVerticesInClippedQuad are valid in the clippedQuad array. Note that
+    // numVerticesInClippedQuad may be zero, which means the entire quad was clipped, and
+    // none of the vertices in the array are valid.
+    static void mapClippedQuad(const TransformationMatrix&, const FloatQuad& srcQuad, FloatPoint clippedQuad[8], int& numVerticesInClippedQuad);
+    static FloatRect computeEnclosingRectOfVertices(FloatPoint vertices[], int numVertices);
+
+    // NOTE: These functions do not do correct clipping against w = 0 plane, but they
+    // correctly detect the clipped condition via the boolean clipped.
     static FloatQuad mapQuad(const TransformationMatrix&, const FloatQuad&, bool& clipped);
     static FloatQuad projectQuad(const TransformationMatrix&, const FloatQuad&, bool& clipped);
 };

Modified: trunk/Source/WebKit/chromium/ChangeLog (113605 => 113606)


--- trunk/Source/WebKit/chromium/ChangeLog	2012-04-09 19:31:39 UTC (rev 113605)
+++ trunk/Source/WebKit/chromium/ChangeLog	2012-04-09 19:31:56 UTC (rev 113606)
@@ -1,3 +1,14 @@
+2012-04-09  Shawn Singh  <[email protected]>
+
+        [chromium] Fix layer sorting perspective w if w becomes negative
+        https://bugs.webkit.org/show_bug.cgi?id=82997
+
+        Reviewed by Adrienne Walker.
+
+        * tests/CCLayerSorterTest.cpp:
+        (WebCore::TEST):
+        (WebCore):
+
 2012-04-09  Dana Jansens  <[email protected]>
 
         [chromium] Flip transition painting delayed with threaded animations

Modified: trunk/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp (113605 => 113606)


--- trunk/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp	2012-04-09 19:31:39 UTC (rev 113605)
+++ trunk/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp	2012-04-09 19:31:56 UTC (rev 113606)
@@ -27,6 +27,7 @@
 #include "cc/CCLayerSorter.h"
 
 #include "cc/CCLayerImpl.h"
+#include "cc/CCMathUtil.h"
 #include "cc/CCSingleThreadProxy.h"
 #include <gtest/gtest.h>
 
@@ -155,6 +156,44 @@
     EXPECT_EQ(CCLayerSorter::None, overlapResult);
 }
 
+TEST(CCLayerSorterTest, LayersUnderPathologicalPerspectiveTransform)
+{
+    CCLayerSorter::ABCompareResult overlapResult;
+    const float zThreshold = 0.1f;
+    float weight = 0;
+
+    // On perspective projection, if w becomes negative, the re-projected point will be
+    // invalid and un-usable. Correct code needs to clip away portions of the geometry
+    // where w < 0. If the code uses the invalid value, it will think that a layer has
+    // different bounds than it really does, which can cause things to sort incorrectly.
+
+    TransformationMatrix perspectiveMatrix;
+    perspectiveMatrix.applyPerspective(1);
+
+    TransformationMatrix transformA;
+    transformA.translate3d(-15, 0, -2);
+    CCLayerSorter::LayerShape layerA(10, 10, perspectiveMatrix * transformA);
+
+    // With this sequence of transforms, when layer B is correctly clipped, it will be
+    // visible on the left half of the projection plane, in front of layerA. When it is
+    // not clipped, its bounds will actually incorrectly appear much smaller and the
+    // correct sorting dependency will not be found.
+    TransformationMatrix transformB;
+    transformB.translate3d(0, 0, 0.7);
+    transformB.rotate3d(0, 45, 0);
+    CCLayerSorter::LayerShape layerB(10, 10, perspectiveMatrix * transformB);
+
+    // Sanity check that the test case actually covers the intended scenario, where part
+    // of layer B go behind the w = 0 plane.
+    FloatQuad testQuad = FloatQuad(FloatRect(FloatPoint(-0.5, -0.5), FloatSize(1, 1)));
+    bool clipped = false;
+    CCMathUtil::mapQuad(perspectiveMatrix * transformB, testQuad, clipped);
+    ASSERT_TRUE(clipped);
+
+    overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerB, zThreshold, weight);
+    EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult);
+}
+
 TEST(CCLayerSorterTest, verifyExistingOrderingPreservedWhenNoZDiff)
 {
     DebugScopedSetImplThread thisScopeIsOnImplThread;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to