faster stroke rects
authormike@reedtribe.org <mike@reedtribe.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 4 Apr 2011 14:38:12 +0000 (14:38 +0000)
committermike@reedtribe.org <mike@reedtribe.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 4 Apr 2011 14:38:12 +0000 (14:38 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@1042 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkScan.h
src/core/SkDraw.cpp
src/core/SkScan_Antihair.cpp

index 037dc09f1681000b62e35e9f7e107496f88773d6..9a52aedf8be43cde7e195be1df401fe816a6da5d 100644 (file)
@@ -1,6 +1,6 @@
 /* libs/graphics/sgl/SkScan.h
 **
-** Copyright 2006, The Android Open Source Project
+** Copyright 2011, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
 ** you may not use this file except in compliance with the License. 
@@ -42,10 +42,9 @@ public:
 #else
     static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
 #endif
-
-    static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
     static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
 
+    static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
     static void FillTriangle(const SkPoint& a, const SkPoint& b,
                              const SkPoint& c, const SkRegion* clip,
                              SkBlitter* blitter) {
@@ -56,12 +55,11 @@ public:
         FillTriangle(pts, clip, blitter);
     }
 
-    static void HairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*);
+    static void HairLine(const SkPoint&, const SkPoint&, const SkRegion*,
+                         SkBlitter*);
     static void HairRect(const SkRect&, const SkRegion* clip, SkBlitter*);
     static void HairPath(const SkPath&, const SkRegion* clip, SkBlitter*);
 
-    static void FrameRect(const SkRect&, SkScalar width, const SkRegion* clip, SkBlitter*);
-
     static void AntiFillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
 #ifdef SK_SCALAR_IS_FIXED
     static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
@@ -74,9 +72,16 @@ public:
     
     static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
 
-    static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*);
+    static void AntiHairLine(const SkPoint&, const SkPoint&, const SkRegion*,
+                             SkBlitter*);
     static void AntiHairRect(const SkRect&, const SkRegion* clip, SkBlitter*);
     static void AntiHairPath(const SkPath&, const SkRegion* clip, SkBlitter*);
+
+    // draws with a miter-join
+    static void FrameRect(const SkRect&, SkScalar width, const SkRegion*,
+                          SkBlitter*);
+    static void AntiFrameRect(const SkRect&, SkScalar width, const SkRegion*,
+                              SkBlitter*);
 };
 
 /** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates
index 1767d226fe080ca8f7ccd7338e15d295de90ee4c..604155aae37fd0255a687a11423c5e37262ef383 100644 (file)
@@ -679,6 +679,18 @@ static inline SkPoint* as_rightbottom(SkRect* r) {
     return ((SkPoint*)(void*)r) + 1;
 }
 
+static bool easy_rect_join(const SkPaint& paint) {
+    return SkPaint::kMiter_Join == paint.getStrokeJoin() &&
+           paint.getStrokeMiter() >= SK_ScalarSqrt2;
+}
+
+enum RectType {
+    kHair_RectType,
+    kFill_RectType,
+    kStroke_RectType,
+    kPath_RectType
+};
+
 void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
     SkDEBUGCODE(this->validate();)
 
@@ -688,11 +700,30 @@ void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
         return;
     }
 
-    // complex enough to draw as a path
+    RectType rtype;
+    const SkScalar width = paint.getStrokeWidth();
+    bool zeroWidth = (0 == width);
+    SkPaint::Style style = paint.getStyle();
+
+    if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
+        style = SkPaint::kFill_Style;
+    }
+
     if (paint.getPathEffect() || paint.getMaskFilter() ||
-            paint.getRasterizer() || !fMatrix->rectStaysRect() ||
-            (paint.getStyle() != SkPaint::kFill_Style &&
-             SkScalarHalf(paint.getStrokeWidth()) > 0)) {
+        paint.getRasterizer() || !fMatrix->rectStaysRect() ||
+        SkPaint::kStrokeAndFill_Style == style) {
+            rtype = kPath_RectType;
+    } else if (SkPaint::kFill_Style == paint.getStyle()) {
+        rtype = kFill_RectType;
+    } else if (zeroWidth) {
+        rtype = kHair_RectType;
+    } else if (easy_rect_join(paint)) {
+        rtype = kStroke_RectType;
+    } else {
+        rtype = kPath_RectType;
+    }
+
+    if (kPath_RectType == rtype) {
         SkPath  tmp;
         tmp.addRect(rect);
         tmp.setFillType(SkPath::kWinding_FillType);
@@ -733,18 +764,30 @@ void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
     // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
     // case we are also hairline (if we've gotten to here), which devolves to
     // effectively just kFill
-    if (paint.getStyle() != SkPaint::kStroke_Style) {
-        if (paint.isAntiAlias()) {
-            SkScan::AntiFillRect(devRect, clip, blitter);
-        } else {
-            SkScan::FillRect(devRect, clip, blitter);
-        }
-    } else {
-        if (paint.isAntiAlias()) {
-            SkScan::AntiHairRect(devRect, clip, blitter);
-        } else {
-            SkScan::HairRect(devRect, clip, blitter);
-        }
+    switch (rtype) {
+        case kFill_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFillRect(devRect, clip, blitter);
+            } else {
+                SkScan::FillRect(devRect, clip, blitter);
+            }
+            break;
+        case kStroke_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiFrameRect(devRect, width, clip, blitter);
+            } else {
+                SkScan::FrameRect(devRect, width, clip, blitter);
+            }
+            break;
+        case kHair_RectType:
+            if (paint.isAntiAlias()) {
+                SkScan::AntiHairRect(devRect, clip, blitter);
+            } else {
+                SkScan::HairRect(devRect, clip, blitter);
+            }
+            break;
+        default:
+            SkASSERT(!"bad rtype");
     }
 }
 
index 0003298b38a5c08152451449133786690fa927d4..dd8c3d2bc22fbbfa3e79d624a19b8adf67e6d74f 100644 (file)
@@ -1,6 +1,6 @@
 /* libs/graphics/sgl/SkScan_Antihair.cpp
 **
-** Copyright 2006, The Android Open Source Project
+** Copyright 2011, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
 ** you may not use this file except in compliance with the License. 
@@ -530,13 +530,8 @@ static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, SkBlitter* blitt
         blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
 }
 
-static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
-{
-    FDot8 L = SkFixedToFDot8(xr.fLeft);
-    FDot8 T = SkFixedToFDot8(xr.fTop);
-    FDot8 R = SkFixedToFDot8(xr.fRight);
-    FDot8 B = SkFixedToFDot8(xr.fBottom);
-    
+static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
+                         bool fillInner) {
     // check for empty now that we're in our reduced precision space
     if (L >= R || T >= B)
         return;
@@ -566,7 +561,7 @@ static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
         }
         int rite = R >> 8;
         int width = rite - left;
-        if (width > 0)
+        if (width > 0 && fillInner)
             blitter->blitRect(left, top, width, height);
         if (R & 0xFF)
             blitter->blitV(rite, top, height, R & 0xFF);
@@ -576,6 +571,12 @@ static void antifillrect(const SkXRect& xr, SkBlitter* blitter)
         do_scanline(L, bot, R, B & 0xFF, blitter);
 }
 
+static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
+    antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
+                 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
+                 blitter, true);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
@@ -667,6 +668,159 @@ void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+// calls blitRect() if the rectangle is non-empty
+static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
+    if (L < R && T < B) {
+        blitter->blitRect(L, T, R - L, B - T);
+    }
+}
+
+static inline FDot8 SkScalarToFDot8(SkScalar x) {
+#ifdef SK_SCALAR_IS_FLOAT
+    return (int)(x * 256);
+#else
+    return x >> 8;
+#endif
+}
+
+static inline int FDot8Floor(FDot8 x) {
+    return x >> 8;
+}
+
+static inline int FDot8Ceil(FDot8 x) {
+    return (x + 0xFF) >> 8;
+}
+
+// 1 - (1 - a)*(1 - b)
+static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
+    return SkToU8(a + b - SkAlphaMul(a, b));
+}
+
+static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
+                           SkBlitter* blitter) {
+    SkASSERT(L < R);
+    
+    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
+        blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
+        return;
+    }
+    
+    int left = L >> 8;
+    if (L & 0xFF) {
+        blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
+        left += 1;
+    }
+    
+    int rite = R >> 8;
+    int width = rite - left;
+    if (width > 0) {
+        call_hline_blitter(blitter, left, top, width, alpha);
+    }
+    
+    if (R & 0xFF) {
+        blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
+    }
+}
+
+static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
+                            SkBlitter* blitter) {
+    SkASSERT(L < R && T < B);
+
+    int top = T >> 8;
+    if (top == ((B - 1) >> 8)) {   // just one scanline high
+        inner_scanline(L, top, R, B - T, blitter);
+        return;
+    }
+    
+    if (T & 0xFF) {
+        inner_scanline(L, top, R, T & 0xFF, blitter);
+        top += 1;
+    }
+    
+    int bot = B >> 8;
+    int height = bot - top;
+    if (height > 0) {
+        if (L & 0xFF) {
+            blitter->blitV(L >> 8, top, height, L & 0xFF);
+        }
+        if (R & 0xFF) {
+            blitter->blitV(R >> 8, top, height, ~R & 0xFF);
+        }
+    }
+    
+    if (B & 0xFF) {
+        inner_scanline(L, bot, R, ~B & 0xFF, blitter);
+    }
+}
+
+void SkScan::AntiFrameRect(const SkRect& r, SkScalar diameter,
+                           const SkRegion* clip, SkBlitter* blitter) {
+    SkASSERT(diameter > 0);
+
+    SkScalar radius = SkScalarHalf(diameter);
+
+    // outset by the radius
+    FDot8 L = SkScalarToFDot8(r.fLeft - radius);
+    FDot8 T = SkScalarToFDot8(r.fTop - radius);
+    FDot8 R = SkScalarToFDot8(r.fRight + radius);
+    FDot8 B = SkScalarToFDot8(r.fBottom + radius);
+
+    SkIRect outer;
+    // set outer to the outer rect of the outer section
+    outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
+
+    SkBlitterClipper clipper;
+    if (clip) {
+        if (clip->quickReject(outer)) {
+            return;
+        }
+        if (!clip->contains(outer)) {
+            blitter = clipper.apply(blitter, clip, &outer);
+        }
+        // now we can ignore clip for the rest of the function
+    }
+    
+    // stroke the outer hull
+    antifilldot8(L, T, R, B, blitter, false);
+
+    // set outer to the outer rect of the middle section
+    outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
+
+    // in case we lost a bit with diameter/2
+    radius = diameter - radius;
+    // inset by the radius
+    L = SkScalarToFDot8(r.fLeft + radius);
+    T = SkScalarToFDot8(r.fTop + radius);
+    R = SkScalarToFDot8(r.fRight - radius);
+    B = SkScalarToFDot8(r.fBottom - radius);
+
+    if (L >= R || T >= B) {
+        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
+                      blitter);
+    } else {
+        SkIRect inner;
+        // set inner to the inner rect of the middle section
+        inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
+
+        // draw the frame in 4 pieces
+        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
+                      blitter);
+        fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
+                      blitter);
+        fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
+                      blitter);
+        fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
+                      blitter);
+
+        // now stroke the inner rect, which is similar to antifilldot8() except that
+        // it treats the fractional coordinates with the inverse bias (since its
+        // inner).
+        innerstrokedot8(L, T, R, B, blitter);
+    }
+}
+
 #endif