Bug 1330166 - Ensure path bounds after rounding contain path edges when using SK_RASTERIZE_EVEN_ROUNDING. r=jrmuizel, a=lizzard
authorLee Salzman <lsalzman@mozilla.com>
Fri, 13 Jan 2017 12:10:40 -0500
changeset 359388 e309c57e703df7e38cb3c0422c73da709d74ae21
parent 359387 5581100c435992a1f76e172f3ece700f34b67e86
child 359389 3b73e8c0c9240d6fe875a1028902ae3a563e0404
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, lizzard
bugs1330166
milestone51.0
Bug 1330166 - Ensure path bounds after rounding contain path edges when using SK_RASTERIZE_EVEN_ROUNDING. r=jrmuizel, a=lizzard MozReview-Commit-ID: Jb48BkbHEPs
gfx/skia/skia/src/core/SkScan_Path.cpp
--- a/gfx/skia/skia/src/core/SkScan_Path.cpp
+++ b/gfx/skia/skia/src/core/SkScan_Path.cpp
@@ -598,16 +598,29 @@ static bool clip_to_limit(const SkRegion
   */
 static inline int round_down_to_int(SkScalar x) {
     double xx = x;
     xx += 0.5;
     double floorXX = floor(xx);
     return (int)floorXX - (xx == floorXX);
 }
 
+#ifdef SK_RASTERIZE_EVEN_ROUNDING
+/**
+  * Variant of SkDScalarRoundToInt that allows offseting the input by a small fraction
+  * while trying to preserve intermediate double-precision (rather than directly adding
+  * the bias to the input at lower single-precision).
+  */
+static inline int round_biased_to_int(SkScalar x, SkScalar bias) {
+    double xx = x;
+    xx += 0.5 + bias;
+    return (int)floor(xx);
+}
+#endif
+
 /**
   *  Variant of SkRect::round() that explicitly performs the rounding step (i.e. floor(x + 0.5))
   *  using double instead of SkScalar (float). It does this by calling SkDScalarRoundToInt(),
   *  which may be slower than calling SkScalarRountToInt(), but gives slightly more accurate
   *  results. Also rounds top and left using double, flooring when the fraction is exactly 0.5f.
   *
   *  e.g.
   *      SkScalar left = 0.5f;
@@ -615,21 +628,38 @@ static inline int round_down_to_int(SkSc
   *      SkASSERT(0 == ileft);  // <--- fails
   *      int ileft = round_down_to_int(left);
   *      SkASSERT(0 == ileft);  // <--- succeeds
   *      SkScalar right = 0.49999997f;
   *      int iright = SkScalarRoundToInt(right);
   *      SkASSERT(0 == iright);  // <--- fails
   *      iright = SkDScalarRoundToInt(right);
   *      SkASSERT(0 == iright);  // <--- succeeds
+  *
+  *
+  *  If using SK_RASTERIZE_EVEN_ROUNDING, we need to ensure that bottom and right account for
+  *  edges bounded by this rect being rounded to FDot6 format before being later rounded to an
+  *  integer. For example, a value like 0.499 can be below 0.5, but round to 0.5 as FDot6, which
+  *  would finally round to the integer 1, instead of just rounding to 0.
+  *
+  *  To handle this, a small bias of half an FDot6 increment is added before actually rounding to
+  *  an integer value. This simulates the rounding of SkScalarRoundToFDot6 without incurring the
+  *  range loss of converting to FDot6 format first, preserving the integer range for the SkIRect.
+  *  Thus, bottom and right are rounded in this manner (biased up), ensuring the rect is large enough.
+  *  Top and left can round as normal since they will round (biased down) to values less or equal
+  *  to the desired rect origin.
   */
 static void round_asymmetric_to_int(const SkRect& src, SkIRect* dst) {
     SkASSERT(dst);
     dst->set(round_down_to_int(src.fLeft), round_down_to_int(src.fTop),
+#ifdef SK_RASTERIZE_EVEN_ROUNDING
+             round_biased_to_int(src.fRight, 0.5f / SK_FDot6One), round_biased_to_int(src.fBottom, 0.5f / SK_FDot6One));
+#else
              SkDScalarRoundToInt(src.fRight), SkDScalarRoundToInt(src.fBottom));
+#endif
 }
 
 void SkScan::FillPath(const SkPath& path, const SkRegion& origClip,
                       SkBlitter* blitter) {
     if (origClip.isEmpty()) {
         return;
     }