Bug 1097437 - Work around Quartz bug where corners of stroked rects don't get a solid color when they should. r=jrmuizel, a=lmandel
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 12 Nov 2014 20:52:57 +1300
changeset 226057 9e4c3c78fe01
parent 226056 19296c34b1ca
child 226058 89c3e0133233
push id4127
push userryanvm@gmail.com
push date2014-11-13 21:27 +0000
treeherdermozilla-beta@9e4c3c78fe01 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, lmandel
bugs1097437
milestone34.0
Bug 1097437 - Work around Quartz bug where corners of stroked rects don't get a solid color when they should. r=jrmuizel, a=lmandel
gfx/2d/DrawTargetCG.cpp
gfx/2d/Matrix.h
gfx/thebes/gfxMatrix.h
layout/reftests/bugs/1097437-1-ref.html
layout/reftests/bugs/1097437-1.html
layout/reftests/bugs/reftest.list
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -971,16 +971,31 @@ DrawTargetCG::StrokeLine(const Point &p1
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
     CGContextStrokePath(cg);
   }
 
   fixer.Fix(mCg);
   CGContextRestoreGState(mCg);
 }
 
+static bool
+IsInteger(Float aValue)
+{
+  return floorf(aValue) == aValue;
+}
+
+static bool
+IsPixelAlignedStroke(const Rect& aRect, Float aLineWidth)
+{
+  Float halfWidth = aLineWidth/2;
+  return IsInteger(aLineWidth) &&
+         IsInteger(aRect.x - halfWidth) && IsInteger(aRect.y - halfWidth) &&
+         IsInteger(aRect.XMost() - halfWidth) && IsInteger(aRect.YMost() - halfWidth);
+}
+
 void
 DrawTargetCG::StrokeRect(const Rect &aRect,
                          const Pattern &aPattern,
                          const StrokeOptions &aStrokeOptions,
                          const DrawOptions &aDrawOptions)
 {
   if (!aRect.IsFinite()) {
     return;
@@ -988,19 +1003,30 @@ DrawTargetCG::StrokeRect(const Rect &aRe
 
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   UnboundnessFixer fixer;
   CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
   CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
-  CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
 
+  // Work around Quartz bug where antialiasing causes corner pixels to be off by
+  // 1 channel value (e.g. rgb(1,1,1) values appear at the corner of solid
+  // black stroke), by turning off antialiasing when the edges of the stroke
+  // are pixel-aligned. Note that when a transform's components are all
+  // integers, it maps integers coordinates to integer coordinates.
+  bool pixelAlignedStroke = mTransform.IsAllIntegers() &&
+    mTransform.PreservesAxisAlignedRectangles() &&
+    aPattern.GetType() == PatternType::COLOR &&
+    IsPixelAlignedStroke(aRect, aStrokeOptions.mLineWidth);
+  CGContextSetShouldAntialias(cg,
+    aDrawOptions.mAntialiasMode != AntialiasMode::NONE && !pixelAlignedStroke);
+
   CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
 
   SetStrokeOptions(cg, aStrokeOptions);
 
   if (isGradient(aPattern)) {
     // There's no CGContextClipStrokeRect so we do it by hand
     CGContextBeginPath(cg);
     CGContextAddRect(cg, RectToCGRect(aRect));
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -253,21 +253,31 @@ public:
   GFX2D_API void NudgeToIntegers();
 
   bool IsTranslation() const
   {
     return FuzzyEqual(_11, 1.0f) && FuzzyEqual(_12, 0.0f) &&
            FuzzyEqual(_21, 0.0f) && FuzzyEqual(_22, 1.0f);
   }
 
+  static bool FuzzyIsInteger(Float aValue)
+  {
+    return FuzzyEqual(aValue, floorf(aValue + 0.5f));
+  }
+
   bool IsIntegerTranslation() const
   {
-    return IsTranslation() &&
-           FuzzyEqual(_31, floorf(_31 + 0.5f)) &&
-           FuzzyEqual(_32, floorf(_32 + 0.5f));
+    return IsTranslation() && FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
+  }
+
+  bool IsAllIntegers() const
+  {
+    return FuzzyIsInteger(_11) && FuzzyIsInteger(_12) &&
+           FuzzyIsInteger(_21) && FuzzyIsInteger(_22) &&
+           FuzzyIsInteger(_31) && FuzzyIsInteger(_32);
   }
 
   Point GetTranslation() const {
     return Point(_31, _32);
   }
 
   /**
    * Returns true if matrix is multiple of 90 degrees rotation with flipping,
@@ -281,24 +291,16 @@ public:
   /**
    * Returns true if the matrix has any transform other
    * than a translation or scale; this is, if there is
    * no rotation.
    */
   bool HasNonAxisAlignedTransform() const {
       return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0);
   }
-
-  /**
-   * Returns true if the matrix has non-integer scale
-   */
-  bool HasNonIntegerScale() const {
-      return !FuzzyEqual(_11, floor(_11 + 0.5)) ||
-             !FuzzyEqual(_22, floor(_22 + 0.5));
-  }
 };
 
 class Matrix4x4
 {
 public:
   Matrix4x4()
     : _11(1.0f), _12(0.0f), _13(0.0f), _14(0.0f)
     , _21(0.0f), _22(1.0f), _23(0.0f), _24(0.0f)
--- a/gfx/thebes/gfxMatrix.h
+++ b/gfx/thebes/gfxMatrix.h
@@ -281,23 +281,15 @@ public:
      * Returns true if matrix is multiple of 90 degrees rotation with flipping,
      * scaling and translation.
      */
     bool PreservesAxisAlignedRectangles() const {
         return ((FuzzyEqual(_11, 0.0) && FuzzyEqual(_22, 0.0))
             || (FuzzyEqual(_21, 0.0) && FuzzyEqual(_12, 0.0)));
     }
 
-    /**
-     * Returns true if the matrix has non-integer scale
-     */
-    bool HasNonIntegerScale() const {
-        return !FuzzyEqual(_11, floor(_11 + 0.5)) ||
-               !FuzzyEqual(_22, floor(_22 + 0.5));
-    }
-
 private:
     static bool FuzzyEqual(gfxFloat aV1, gfxFloat aV2) {
         return fabs(aV2 - aV1) < 1e-6;
     }
 };
 
 #endif /* GFX_MATRIX_H */
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1097437-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+body { background-color: white; }
+#outer
+{
+  width: 100px; height: 100px;
+  padding: 1px;
+  background-color: black;
+}
+#inner
+{
+  width: 100px; height:100px;
+  background-color: white;
+}
+</style>
+</head>
+<body>
+<div id="outer"><div id="inner"></div></div>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1097437-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+body { background-color: white; }
+#outer
+{
+  width: 100px; height: 100px;
+  border: 1px solid black;
+}
+</style>
+</head>
+<body>
+<div id="outer"></div>
+</body>
+</html>
\ No newline at end of file
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1830,8 +1830,9 @@ pref(browser.display.use_document_fonts,
 == 1053035-1-flex.html 1053035-1-ref.html
 test-pref(layout.css.grid.enabled,true) == 1053035-1-grid.html 1053035-1-ref.html
 == 1059167-1.html 1059167-1-ref.html
 == 1059498-1.html 1059498-1-ref.html
 == 1059498-2.html 1059498-1-ref.html
 == 1059498-3.html 1059498-1-ref.html
 == 1078262-1.html about:blank
 == 1081185-1.html 1081185-1-ref.html
+== 1097437-1.html 1097437-1-ref.html