Very fuzzy layers under non-decompasable matrices
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 3 May 2014 20:32:24 +0000 (20:32 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 3 May 2014 20:32:24 +0000 (20:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=132516
<rdar://problem/16717478>

Reviewed by Sam Weinig.

Source/WebCore:
r155977 added code to modify layer contentsScale based on a root-relative
scale, so that scaled-up layers remained sharp. It does this by decomposing
an accumulated matrix, but failed to test whether the decomposition
succeeded. This would result in contentsScale of 0, which is clamped to 0.1,
resulting in very fuzzy layers.

Fix by testing for success of decomposition.

Test: compositing/contents-scale/non-decomposable-matrix.html

* platform/graphics/ca/GraphicsLayerCA.cpp:
(WebCore::maxScaleFromTransform):
* platform/graphics/transforms/TransformationMatrix.cpp:
(WebCore::TransformationMatrix::decompose2): Return early for identity matrices,
with fix for m11 and m22.
(WebCore::TransformationMatrix::decompose4): Return early for identity matrices.
* platform/graphics/transforms/TransformationMatrix.h:
Make Decomposed2Type and Decomposed4Type into C++ structs.
(WebCore::TransformationMatrix::Decomposed2Type::operator==): Added to make it easier
to write code that asserts that decomposition is correct.
(WebCore::TransformationMatrix::Decomposed4Type::operator==): Ditto.

LayoutTests:
Compare scaling under non-decomposable and decomposable matrices.

* compositing/contents-scale/non-decomposable-matrix-expected.html: Added.
* compositing/contents-scale/non-decomposable-matrix.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@168227 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/compositing/contents-scale/non-decomposable-matrix-expected.html [new file with mode: 0644]
LayoutTests/compositing/contents-scale/non-decomposable-matrix.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp
Source/WebCore/platform/graphics/transforms/TransformationMatrix.h

index d7dc718..be1f1ca 100644 (file)
@@ -1,3 +1,16 @@
+2014-05-03  Simon Fraser  <simon.fraser@apple.com>
+
+        Very fuzzy layers under non-decompasable matrices
+        https://bugs.webkit.org/show_bug.cgi?id=132516
+        <rdar://problem/16717478>
+
+        Reviewed by Sam Weinig.
+        
+        Compare scaling under non-decomposable and decomposable matrices.
+
+        * compositing/contents-scale/non-decomposable-matrix-expected.html: Added.
+        * compositing/contents-scale/non-decomposable-matrix.html: Added.
+
 2014-05-03  Zalan Bujtas  <zalan@apple.com>
 
         Subpixel rendering: Add hidpi fieldset/legend test case to check fieldset's cliprect when legend text is present.
 2014-05-03  Zalan Bujtas  <zalan@apple.com>
 
         Subpixel rendering: Add hidpi fieldset/legend test case to check fieldset's cliprect when legend text is present.
diff --git a/LayoutTests/compositing/contents-scale/non-decomposable-matrix-expected.html b/LayoutTests/compositing/contents-scale/non-decomposable-matrix-expected.html
new file mode 100644 (file)
index 0000000..728af61
--- /dev/null
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <style>
+        .box {
+            box-sizing: border-box;
+            margin: 20px;
+            width: 100px;
+            height: 100px;
+            background-color: orange;
+            border: 20px solid blue;
+        }
+        
+    </style>
+</head>
+<body>
+<div class="box" style="-webkit-transform: scale3d(1, 1, 1)"></div>
+</body>
+</html>
diff --git a/LayoutTests/compositing/contents-scale/non-decomposable-matrix.html b/LayoutTests/compositing/contents-scale/non-decomposable-matrix.html
new file mode 100644 (file)
index 0000000..3736c50
--- /dev/null
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+    <style>
+        .box {
+            box-sizing: border-box;
+            margin: 20px;
+            width: 100px;
+            height: 100px;
+            background-color: orange;
+            border: 20px solid blue;
+        }
+        
+    </style>
+</head>
+<body>
+<div class="box" style="-webkit-transform: scale3d(1, 1, 0)"></div>
+</body>
+</html>
index 0725ec9..04e9f3e 100644 (file)
@@ -1,5 +1,35 @@
 2014-05-03  Simon Fraser  <simon.fraser@apple.com>
 
 2014-05-03  Simon Fraser  <simon.fraser@apple.com>
 
+        Very fuzzy layers under non-decompasable matrices
+        https://bugs.webkit.org/show_bug.cgi?id=132516
+        <rdar://problem/16717478>
+
+        Reviewed by Sam Weinig.
+        
+        r155977 added code to modify layer contentsScale based on a root-relative
+        scale, so that scaled-up layers remained sharp. It does this by decomposing
+        an accumulated matrix, but failed to test whether the decomposition
+        succeeded. This would result in contentsScale of 0, which is clamped to 0.1,
+        resulting in very fuzzy layers.
+        
+        Fix by testing for success of decomposition.
+
+        Test: compositing/contents-scale/non-decomposable-matrix.html
+
+        * platform/graphics/ca/GraphicsLayerCA.cpp:
+        (WebCore::maxScaleFromTransform):
+        * platform/graphics/transforms/TransformationMatrix.cpp:
+        (WebCore::TransformationMatrix::decompose2): Return early for identity matrices,
+        with fix for m11 and m22.
+        (WebCore::TransformationMatrix::decompose4): Return early for identity matrices.
+        * platform/graphics/transforms/TransformationMatrix.h:
+        Make Decomposed2Type and Decomposed4Type into C++ structs.
+        (WebCore::TransformationMatrix::Decomposed2Type::operator==): Added to make it easier
+        to write code that asserts that decomposition is correct.
+        (WebCore::TransformationMatrix::Decomposed4Type::operator==): Ditto.
+
+2014-05-03  Simon Fraser  <simon.fraser@apple.com>
+
         Fix crash in WebKit client app when zooming
         https://bugs.webkit.org/show_bug.cgi?id=132475
         <rdar://problem/16703405>
         Fix crash in WebKit client app when zooming
         https://bugs.webkit.org/show_bug.cgi?id=132475
         <rdar://problem/16703405>
index 621c83e..092e527 100644 (file)
@@ -293,7 +293,9 @@ static float maxScaleFromTransform(const TransformationMatrix& t)
         return 1;
 
     TransformationMatrix::Decomposed4Type decomposeData;
         return 1;
 
     TransformationMatrix::Decomposed4Type decomposeData;
-    t.decompose4(decomposeData);
+    if (!t.decompose4(decomposeData))
+        return 1;
+
     return std::max(fabsf(narrowPrecisionToFloat(decomposeData.scaleX)), fabsf(narrowPrecisionToFloat(decomposeData.scaleY)));
 }
 
     return std::max(fabsf(narrowPrecisionToFloat(decomposeData.scaleX)), fabsf(narrowPrecisionToFloat(decomposeData.scaleY)));
 }
 
index dac207f..05b0388 100644 (file)
@@ -1579,6 +1579,9 @@ bool TransformationMatrix::decompose2(Decomposed2Type& decomp) const
         memset(&decomp, 0, sizeof(decomp));
         decomp.scaleX = 1;
         decomp.scaleY = 1;
         memset(&decomp, 0, sizeof(decomp));
         decomp.scaleX = 1;
         decomp.scaleY = 1;
+        decomp.m11 = 1;
+        decomp.m22 = 1;
+        return true;
     }
 
     return WebCore::decompose2(m_matrix, decomp);
     }
 
     return WebCore::decompose2(m_matrix, decomp);
@@ -1592,6 +1595,7 @@ bool TransformationMatrix::decompose4(Decomposed4Type& decomp) const
         decomp.scaleX = 1;
         decomp.scaleY = 1;
         decomp.scaleZ = 1;
         decomp.scaleX = 1;
         decomp.scaleY = 1;
         decomp.scaleZ = 1;
+        return true;
     }
 
     return WebCore::decompose4(m_matrix, decomp);
     }
 
     return WebCore::decompose4(m_matrix, decomp);
index 3697d49..31f8bfd 100644 (file)
@@ -126,13 +126,13 @@ public:
                m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1;
     }
 
                m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1;
     }
 
-    // This form preserves the double math from input to output
+    // This form preserves the double math from input to output.
     void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); }
 
     void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); }
 
-    // Map a 3D point through the transform, returning a 3D point.
+    // Maps a 3D point through the transform, returning a 3D point.
     FloatPoint3D mapPoint(const FloatPoint3D&) const;
 
     FloatPoint3D mapPoint(const FloatPoint3D&) const;
 
-    // Map a 2D point through the transform, returning a 2D point.
+    // Maps a 2D point through the transform, returning a 2D point.
     // Note that this ignores the z component, effectively projecting the point into the z=0 plane.
     FloatPoint mapPoint(const FloatPoint&) const;
 
     // Note that this ignores the z component, effectively projecting the point into the z=0 plane.
     FloatPoint mapPoint(const FloatPoint&) const;
 
@@ -143,7 +143,7 @@ public:
     }
 
     // If the matrix has 3D components, the z component of the result is
     }
 
     // If the matrix has 3D components, the z component of the result is
-    // dropped, effectively projecting the rect into the z=0 plane
+    // dropped, effectively projecting the rect into the z=0 plane.
     FloatRect mapRect(const FloatRect&) const;
 
     // Rounds the resulting mapped rectangle out. This is helpful for bounding
     FloatRect mapRect(const FloatRect&) const;
 
     // Rounds the resulting mapped rectangle out. This is helpful for bounding
@@ -152,16 +152,14 @@ public:
     LayoutRect mapRect(const LayoutRect&) const;
 
     // If the matrix has 3D components, the z component of the result is
     LayoutRect mapRect(const LayoutRect&) const;
 
     // If the matrix has 3D components, the z component of the result is
-    // dropped, effectively projecting the quad into the z=0 plane
+    // dropped, effectively projecting the quad into the z=0 plane.
     FloatQuad mapQuad(const FloatQuad&) const;
 
     FloatQuad mapQuad(const FloatQuad&) const;
 
-    // Map a point on the z=0 plane into a point on
-    // the plane with with the transform applied, by extending
-    // a ray perpendicular to the source plane and computing
-    // the local x,y position of the point where that ray intersects
-    // with the destination plane.
+    // Maps a point on the z=0 plane into a point on the plane with with the transform applied, by
+    // extending a ray perpendicular to the source plane and computing the local x,y position of
+    // the point where that ray intersects with the destination plane.
     FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const;
     FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const;
-    // Projects the four corners of the quad
+    // Projects the four corners of the quad.
     FloatQuad projectQuad(const FloatQuad&,  bool* clamped = 0) const;
     // Projects the four corners of the quad and takes a bounding box,
     // while sanitizing values created when the w component is negative.
     FloatQuad projectQuad(const FloatQuad&,  bool* clamped = 0) const;
     // Projects the four corners of the quad and takes a bounding box,
     // while sanitizing values created when the w component is negative.
@@ -230,8 +228,7 @@ public:
     TransformationMatrix& rotateFromVector(double x, double y);
     TransformationMatrix& rotate3d(double rx, double ry, double rz);
     
     TransformationMatrix& rotateFromVector(double x, double y);
     TransformationMatrix& rotate3d(double rx, double ry, double rz);
     
-    // The vector (x,y,z) is normalized if it's not already. A vector of
-    // (0,0,0) uses a vector of (0,0,1).
+    // The vector (x,y,z) is normalized if it's not already. A vector of (0,0,0) uses a vector of (0,0,1).
     TransformationMatrix& rotate3d(double x, double y, double z, double angle);
     
     TransformationMatrix& translate(double tx, double ty);
     TransformationMatrix& rotate3d(double x, double y, double z, double angle);
     
     TransformationMatrix& translate(double tx, double ty);
@@ -250,30 +247,47 @@ public:
     TransformationMatrix& applyPerspective(double p);
     bool hasPerspective() const { return m_matrix[2][3] != 0.0f; }
 
     TransformationMatrix& applyPerspective(double p);
     bool hasPerspective() const { return m_matrix[2][3] != 0.0f; }
 
-    // returns a transformation that maps a rect to a rect
+    // Returns a transformation that maps a rect to a rect.
     static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&);
 
     bool isInvertible() const;
 
     static TransformationMatrix rectToRect(const FloatRect&, const FloatRect&);
 
     bool isInvertible() const;
 
-    // This method returns the identity matrix if it is not invertible.
+    // Returns the identity matrix if it is not invertible.
     // Use isInvertible() before calling this if you need to know.
     TransformationMatrix inverse() const;
 
     // Use isInvertible() before calling this if you need to know.
     TransformationMatrix inverse() const;
 
-    // decompose the matrix into its component parts
-    typedef struct {
+    // Decompose the matrix into its component parts.
+    struct Decomposed2Type {
         double scaleX, scaleY;
         double translateX, translateY;
         double angle;
         double m11, m12, m21, m22;
         double scaleX, scaleY;
         double translateX, translateY;
         double angle;
         double m11, m12, m21, m22;
-    } Decomposed2Type;
-
-    typedef struct {
+        
+        bool operator==(const Decomposed2Type& other) const
+        {
+            return scaleX == other.scaleX && scaleY == other.scaleY
+                && translateX == other.translateX && translateY == other.translateY
+                && angle == other.angle
+                && m11 == other.m11 && m12 == other.m12 && m21 == other.m21 && m22 == other.m22;
+        }
+    };
+
+    struct Decomposed4Type {
         double scaleX, scaleY, scaleZ;
         double skewXY, skewXZ, skewYZ;
         double quaternionX, quaternionY, quaternionZ, quaternionW;
         double translateX, translateY, translateZ;
         double perspectiveX, perspectiveY, perspectiveZ, perspectiveW;
         double scaleX, scaleY, scaleZ;
         double skewXY, skewXZ, skewYZ;
         double quaternionX, quaternionY, quaternionZ, quaternionW;
         double translateX, translateY, translateZ;
         double perspectiveX, perspectiveY, perspectiveZ, perspectiveW;
-    } Decomposed4Type;
+
+        bool operator==(const Decomposed4Type& other) const
+        {
+            return scaleX == other.scaleX && scaleY == other.scaleY && scaleZ == other.scaleZ
+                && skewXY == other.skewXY && skewXZ == other.skewXZ && skewYZ == other.skewYZ
+                && quaternionX == other.quaternionX && quaternionY == other.quaternionY && quaternionZ == other.quaternionZ && quaternionW == other.quaternionW
+                && translateX == other.translateX && translateY == other.translateY && translateZ == other.translateZ
+                && perspectiveX == other.perspectiveX && perspectiveY == other.perspectiveY && perspectiveZ == other.perspectiveZ && perspectiveW == other.perspectiveW;
+        }
+    };
     
     bool decompose2(Decomposed2Type&) const;
     void recompose2(const Decomposed2Type&);
     
     bool decompose2(Decomposed2Type&) const;
     void recompose2(const Decomposed2Type&);
@@ -291,7 +305,7 @@ public:
                 m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1);
     }
 
                 m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1);
     }
 
-    // Throw away the non-affine parts of the matrix (lossy!)
+    // Throw away the non-affine parts of the matrix (lossy!).
     void makeAffine();
 
     AffineTransform toAffineTransform() const;
     void makeAffine();
 
     AffineTransform toAffineTransform() const;
@@ -357,7 +371,7 @@ public:
 
     bool isIntegerTranslation() const;
 
 
     bool isIntegerTranslation() const;
 
-    // This method returns the matrix without 3D components.
+    // Returns the matrix without 3D components.
     TransformationMatrix to2dTransform() const;
     
     typedef float FloatMatrix4[16];
     TransformationMatrix to2dTransform() const;
     
     typedef float FloatMatrix4[16];
@@ -380,7 +394,6 @@ private:
         return FloatPoint(static_cast<float>(resultX), static_cast<float>(resultY));
     }
 
         return FloatPoint(static_cast<float>(resultX), static_cast<float>(resultY));
     }
 
-    // multiply passed 3D point by matrix
     void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const;
     FloatPoint3D internalMapPoint(const FloatPoint3D& sourcePoint) const
     {
     void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const;
     FloatPoint3D internalMapPoint(const FloatPoint3D& sourcePoint) const
     {