use conservative bounds to disable clipping
Bug: skia:6754
Change-Id: I658e7e7a176395eff7e239093fbad8038a4141ad
Reviewed-on: https://skia-review.googlesource.com/107884
Reviewed-by: Cary Clark <caryclark@google.com>
Commit-Queue: Mike Reed <reed@google.com>
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index 9c20527..2373e62 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -561,71 +561,49 @@
return true;
}
-/**
- * Variants of SkScalarRoundToInt, identical to SkDScalarRoundToInt except when the input fraction
- * is 0.5. When SK_RASTERIZE_EVEN_ROUNDING is enabled, we must bias the result before rounding to
- * account for potential FDot6 rounding edge-cases.
- */
-#ifdef SK_RASTERIZE_EVEN_ROUNDING
-static const double kRoundBias = 0.5 / SK_FDot6One;
-#else
-static const double kRoundBias = 0.0;
-#endif
+// Bias used for conservative rounding of float rects to int rects, to nudge the irects a little
+// larger, so we don't "think" a path's bounds are inside a clip, when (due to numeric drift in
+// the scan-converter) we might walk beyond the predicted limits.
+static const double kConservativeRoundBias = 0.5 + 0.5 / SK_FDot6One;
/**
- * Round the value down. This is used to round the top and left of a rectangle,
- * and corresponds to the way the scan converter treats the top and left edges.
- */
+ * Round the value down. This is used to round the top and left of a rectangle,
+ * and corresponds to the way the scan converter treats the top and left edges.
+ * It has a slight bias to make the "rounded" int smaller than a normal round, to create a more
+ * conservative int-bounds (larger) from a float rect.
+ */
static inline int round_down_to_int(SkScalar x) {
double xx = x;
- xx -= 0.5 + kRoundBias;
+ xx -= kConservativeRoundBias;
return sk_double_saturate2int(ceil(xx));
}
/**
- * Round the value up. This is used to round the bottom and right of a rectangle,
- * and corresponds to the way the scan converter treats the bottom and right edges.
+ * Round the value up. This is used to round the right and bottom of a rectangle.
+ * It has a slight bias to make the "rounded" int smaller than a normal round, to create a more
+ * conservative int-bounds (larger) from a float rect.
*/
static inline int round_up_to_int(SkScalar x) {
double xx = x;
- xx += 0.5 + kRoundBias;
+ xx += kConservativeRoundBias;
return sk_double_saturate2int(floor(xx));
}
-/**
- * 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;
- * int ileft = SkScalarRoundToInt(left);
- * 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 we 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.
+/*
+ * Conservative rounding function, which effectively nudges the int-rect to be slightly larger
+ * than SkRect::round() might have produced. This is a safety-net for the scan-converter, which
+ * inspects the returned int-rect, and may disable clipping (for speed) if it thinks all of the
+ * edges will fit inside the clip's bounds. The scan-converter introduces slight numeric errors
+ * due to accumulated += of the slope, so this function is used to return a conservatively large
+ * int-bounds, and thus we will only disable clipping if we're sure the edges will stay in-bounds.
*/
-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),
- round_up_to_int(src.fRight), round_up_to_int(src.fBottom));
+static SkIRect conservative_round_to_int(const SkRect& src) {
+ return {
+ round_down_to_int(src.fLeft),
+ round_down_to_int(src.fTop),
+ round_up_to_int(src.fRight),
+ round_up_to_int(src.fBottom),
+ };
}
void SkScan::FillPath(const SkPath& path, const SkRegion& origClip,
@@ -644,7 +622,7 @@
}
clipPtr = &finiteClip;
}
- // don't reference "origClip" any more, just use clipPtr
+ // don't reference "origClip" any more, just use clipPtr
SkRect bounds = path.getBounds();
@@ -655,12 +633,8 @@
}
irPreClipped = true;
}
- SkIRect ir;
- // We deliberately call round_asymmetric_to_int() instead of round(), since we can't afford
- // to generate a bounds that is tighter than the corresponding SkEdges. The edge code basically
- // converts the floats to fixed, and then "rounds". If we called round() instead of
- // round_asymmetric_to_int() here, we could generate the wrong ir for values like 0.4999997.
- round_asymmetric_to_int(bounds, &ir);
+
+ SkIRect ir = conservative_round_to_int(bounds);
if (ir.isEmpty()) {
if (path.isInverseFillType()) {
blitter->blitRegion(*clipPtr);