API change: SkPath computeBounds -> getBounds



git-svn-id: http://skia.googlecode.com/svn/trunk@140 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index 9208429..25303ac 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -784,6 +784,64 @@
     SkDELETE((SkBlitter*)blitter);
 }
 
+static bool just_solid_color(const SkPaint& paint) {
+    if (paint.getAlpha() == 0xFF && paint.getColorFilter() == NULL) {
+        SkShader* shader = paint.getShader();
+        if (NULL == shader ||
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+            return true;
+        }
+    }
+    return false;
+}
+    
+/** By analyzing the paint (with an xfermode), we may decide we can take
+    special action. This enum lists our possible actions
+ */
+enum XferInterp {
+    kNormal_XferInterp,         // no special interpretation, draw normally
+    kSrcOver_XferInterp,        // draw as if in srcover mode
+    kSkipDrawing_XferInterp     // draw nothing
+};
+
+static XferInterp interpret_xfermode(const SkPaint& paint, SkXfermode* xfer,
+                                     SkBitmap::Config deviceConfig) {
+    SkPorterDuff::Mode  mode;
+    
+    if (SkPorterDuff::IsMode(xfer, &mode)) {
+        switch (mode) {
+            case SkPorterDuff::kSrc_Mode:
+                if (just_solid_color(paint)) {
+                    return kSrcOver_XferInterp;
+                }
+                break;
+            case SkPorterDuff::kDst_Mode:
+                return kSkipDrawing_XferInterp;
+            case SkPorterDuff::kSrcOver_Mode:
+                return kSrcOver_XferInterp;
+            case SkPorterDuff::kDstOver_Mode:
+                if (SkBitmap::kRGB_565_Config == deviceConfig) {
+                    return kSkipDrawing_XferInterp;
+                }
+                break;
+            case SkPorterDuff::kSrcIn_Mode:
+                if (SkBitmap::kRGB_565_Config == deviceConfig &&
+                    just_solid_color(paint)) {
+                    return kSrcOver_XferInterp;
+                }
+                break;
+            case SkPorterDuff::kDstIn_Mode:
+                if (just_solid_color(paint)) {
+                    return kSkipDrawing_XferInterp;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+    return kNormal_XferInterp;
+}
+
 SkBlitter* SkBlitter::Choose(const SkBitmap& device,
                              const SkMatrix& matrix,
                              const SkPaint& paint,
@@ -813,6 +871,19 @@
     }
 
     SkXfermode* mode = paint.getXfermode();
+    if (NULL != mode) {
+        switch (interpret_xfermode(paint, mode, device.config())) {
+            case kSrcOver_XferInterp:
+                mode = NULL;
+                break;
+            case kSkipDrawing_XferInterp:
+                SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+                return blitter;
+            default:
+                break;
+        }
+    }
+
     if (NULL == shader && (NULL != mode || paint.getColorFilter() != NULL))
     {
         // xfermodes require shaders for our current set of blitters
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 16c94c2..199c1b0 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -879,58 +879,33 @@
     }
 }
 
+/*  current impl ignores edgetype, and relies on
+    getLocalClipBoundsCompareType(), which always returns a value assuming
+    antialiasing (worst case)
+ */
 bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
-    /*  current impl ignores edgetype, and relies on
-        getLocalClipBoundsCompareType(), which always returns a value assuming
-        antialiasing (worst case)
-     */
-
     if (fMCRec->fRegion->isEmpty()) {
         return true;
     }
-    
-    // check for empty user rect (horizontal)
-    SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
-    SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
-    if (userL >= userR) {
-        return true;
-    }
 
-    // check for empty user rect (vertical)
+    const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
+
+    // for speed, do the most likely reject compares first
     SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
     SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
-    if (userT >= userB) {
+    if (userT >= clipR.fBottom || userB <= clipR.fTop) {
         return true;
     }
-    
-    // check if we are completely outside of the local clip bounds
-    const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
-    return  userL >= clipR.fRight || userT >= clipR.fBottom ||
-            userR <= clipR.fLeft  || userB <= clipR.fTop;
+    SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
+    SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
+    if (userL >= clipR.fRight || userR <= clipR.fLeft) {
+        return true;
+    }    
+    return false;
 }
 
 bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
-    if (fMCRec->fRegion->isEmpty() || path.isEmpty()) {
-        return true;
-    }
-
-    if (fMCRec->fMatrix->rectStaysRect()) {
-        SkRect  r;
-        path.computeBounds(&r, SkPath::kFast_BoundsType);
-        return this->quickReject(r, et);
-    }
-
-    SkPath      dstPath;
-    SkRect      r;
-    SkIRect     ir;
-
-    path.transform(*fMCRec->fMatrix, &dstPath);
-    dstPath.computeBounds(&r, SkPath::kFast_BoundsType);
-    r.round(&ir);
-    if (kAA_EdgeType == et) {
-        ir.inset(-1, -1);
-    }
-    return fMCRec->fRegion->quickReject(ir);
+    return path.isEmpty() || this->quickReject(path.getBounds(), et);
 }
 
 bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
@@ -947,6 +922,7 @@
     SkScalarCompareType userB = SkScalarToCompareType(bottom);
     
     // check for invalid user Y coordinates (i.e. empty)
+    // reed: why do we need to do this check, since it slows us down?
     if (userT >= userB) {
         return true;
     }
@@ -1063,9 +1039,9 @@
 
 void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
     if (paint.canComputeFastBounds()) {
-        SkRect r;
-        path.computeBounds(&r, SkPath::kFast_BoundsType);
-        if (this->quickReject(paint.computeFastBounds(r, &r),
+        SkRect storage;
+        const SkRect& bounds = path.getBounds();
+        if (this->quickReject(paint.computeFastBounds(bounds, &storage),
                               paint2EdgeType(&paint))) {
             return;
         }
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 60c474a..93b5d4e 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -2236,10 +2236,8 @@
 }
 
 bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
-    SkRect      bounds;
-    SkIRect     r;
-
-    path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+    SkIRect       r;
+    const SkRect& bounds = path.getBounds();
 
     if (doFill) {
         bounds.round(&r);
@@ -2276,8 +2274,7 @@
 
     //  init our bounds from the path
     {
-        SkRect      pathBounds;
-        devPath.computeBounds(&pathBounds, SkPath::kExact_BoundsType);
+        SkRect pathBounds = devPath.getBounds();
         pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
         pathBounds.roundOut(bounds);
     }
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index da70c74..1441385 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -1517,36 +1517,24 @@
     return width != 0;  // return true if we're filled, or false if we're hairline (width == 0)
 }
 
-bool SkPaint::canComputeFastBounds() const {
-    // use bit-or since no need for early exit
-    return (reinterpret_cast<uintptr_t>(this->getMaskFilter()) |
-            reinterpret_cast<uintptr_t>(this->getLooper()) |
-            reinterpret_cast<uintptr_t>(this->getRasterizer()) |
-            reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
-}
-
-const SkRect& SkPaint::computeFastBounds(const SkRect& src,
-                                         SkRect* storage) const {
+const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
+                                               SkRect* storage) const {
     SkASSERT(storage);
-    
-    if (this->getStyle() != SkPaint::kFill_Style) {
-        // if we're stroked, outset the rect by the radius (and join type)
-        SkScalar radius = SkScalarHalf(this->getStrokeWidth());
-        
-        if (0 == radius) {  // hairline
-            radius = SK_Scalar1;
-        } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
-            SkScalar scale = this->getStrokeMiter();
-            if (scale > SK_Scalar1) {
-                radius = SkScalarMul(radius, scale);
-            }
+    SkASSERT(this->getStyle() != SkPaint::kFill_Style);
+
+    // since we're stroked, outset the rect by the radius (and join type)
+    SkScalar radius = SkScalarHalf(this->getStrokeWidth());
+    if (0 == radius) {  // hairline
+        radius = SK_Scalar1;
+    } else if (this->getStrokeJoin() == SkPaint::kMiter_Join) {
+        SkScalar scale = this->getStrokeMiter();
+        if (scale > SK_Scalar1) {
+            radius = SkScalarMul(radius, scale);
         }
-        storage->set(src.fLeft - radius, src.fTop - radius,
-                     src.fRight + radius, src.fBottom + radius);
-        return *storage;
     }
-    // no adjustments needed, just return the original rect
-    return src;
+    storage->set(src.fLeft - radius, src.fTop - radius,
+                 src.fRight + radius, src.fBottom + radius);
+    return *storage;
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index da90227..0cb50fb 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -27,7 +27,7 @@
  
     It captures some state about the path up front (i.e. if it already has a
     cached bounds), and the if it can, it updates the cache bounds explicitly,
-    avoiding the need to revisit all of the points in computeBounds().
+    avoiding the need to revisit all of the points in getBounds().
  */
 class SkAutoPathBoundsUpdate {
 public:
@@ -43,11 +43,11 @@
     
     ~SkAutoPathBoundsUpdate() {
         if (fEmpty) {
-            fPath->fFastBounds = fRect;
-            fPath->fFastBoundsIsDirty = false;
+            fPath->fBounds = fRect;
+            fPath->fBoundsIsDirty = false;
         } else if (!fDirty) {
-            fPath->fFastBounds.join(fRect);
-            fPath->fFastBoundsIsDirty = false;
+            fPath->fBounds.join(fRect);
+            fPath->fBoundsIsDirty = false;
         }
     }
     
@@ -60,14 +60,14 @@
     // returns true if we should proceed
     void init(const SkPath* path) {
         fPath = path;
-        fDirty = path->fFastBoundsIsDirty;
+        fDirty = path->fBoundsIsDirty;
         fEmpty = path->isEmpty();
         // Cannot use fRect for our bounds unless we know it is sorted
         fRect.sort();
     }
 };
 
-static void compute_fast_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
+static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
     if (pts.count() <= 1) {  // we ignore just 1 point (moveto)
         bounds->set(0, 0, 0, 0);
     } else {
@@ -91,7 +91,7 @@
 
 ////////////////////////////////////////////////////////////////////////////
 
-SkPath::SkPath() : fFastBoundsIsDirty(true), fFillType(kWinding_FillType) {}
+SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {}
 
 SkPath::SkPath(const SkPath& src) {
     SkDEBUGCODE(src.validate();)
@@ -106,11 +106,11 @@
     SkDEBUGCODE(src.validate();)
 
     if (this != &src) {
-        fFastBounds         = src.fFastBounds;
-        fPts                = src.fPts;
-        fVerbs              = src.fVerbs;
-        fFillType           = src.fFillType;
-        fFastBoundsIsDirty  = src.fFastBoundsIsDirty;
+        fBounds         = src.fBounds;
+        fPts            = src.fPts;
+        fVerbs          = src.fVerbs;
+        fFillType       = src.fFillType;
+        fBoundsIsDirty  = src.fBoundsIsDirty;
     }
     SkDEBUGCODE(this->validate();)
     return *this;
@@ -125,11 +125,11 @@
     SkASSERT(&other != NULL);
 
     if (this != &other) {
-        SkTSwap<SkRect>(fFastBounds, other.fFastBounds);
+        SkTSwap<SkRect>(fBounds, other.fBounds);
         fPts.swap(other.fPts);
         fVerbs.swap(other.fVerbs);
         SkTSwap<uint8_t>(fFillType, other.fFillType);
-        SkTSwap<uint8_t>(fFastBoundsIsDirty, other.fFastBoundsIsDirty);
+        SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
     }
 }
 
@@ -138,7 +138,7 @@
 
     fPts.reset();
     fVerbs.reset();
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 }
 
 void SkPath::rewind() {
@@ -146,7 +146,7 @@
 
     fPts.rewind();
     fVerbs.rewind();
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 }
 
 bool SkPath::isEmpty() const {
@@ -198,20 +198,12 @@
     }
 }
 
-#define ALWAYS_FAST_BOUNDS_FOR_NOW  true
-
-void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const {
+void SkPath::computeBounds() const {
     SkDEBUGCODE(this->validate();)
+    SkASSERT(fBoundsIsDirty);
 
-    SkASSERT(bounds);
-    
-    // we BoundsType for now
-
-    if (fFastBoundsIsDirty) {
-        fFastBoundsIsDirty = false;
-        compute_fast_bounds(&fFastBounds, fPts);
-    }
-    *bounds = fFastBounds;
+    fBoundsIsDirty = false;
+    compute_pt_bounds(&fBounds, fPts);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -240,7 +232,7 @@
     }
     pt->set(x, y);
 
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 }
 
 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
@@ -259,7 +251,7 @@
     fPts.append()->set(x, y);
     *fVerbs.append() = kLine_Verb;
 
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 }
 
 void SkPath::rLineTo(SkScalar x, SkScalar y) {
@@ -281,7 +273,7 @@
     pts[1].set(x2, y2);
     *fVerbs.append() = kQuad_Verb;
 
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 }
 
 void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
@@ -304,7 +296,7 @@
     pts[2].set(x3, y3);
     *fVerbs.append() = kCubic_Verb;
 
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 }
 
 void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
@@ -899,13 +891,13 @@
         matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
     } else {
         // remember that dst might == this, so be sure to check
-        // fFastBoundsIsDirty before we set it
-        if (!fFastBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
+        // fBoundsIsDirty before we set it
+        if (!fBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
             // if we're empty, fastbounds should not be mapped
-            matrix.mapRect(&dst->fFastBounds, fFastBounds);
-            dst->fFastBoundsIsDirty = false;
+            matrix.mapRect(&dst->fBounds, fBounds);
+            dst->fBoundsIsDirty = false;
         } else {
-            dst->fFastBoundsIsDirty = true;
+            dst->fBoundsIsDirty = true;
         }
 
         if (this != dst) {
@@ -918,14 +910,6 @@
     }
 }
 
-void SkPath::updateBoundsCache() const {
-    if (fFastBoundsIsDirty) {
-        SkRect  r;
-        this->computeBounds(&r, kFast_BoundsType);
-        SkASSERT(!fFastBoundsIsDirty);
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1220,7 +1204,7 @@
     buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
     buffer.read(fVerbs.begin(), fVerbs.count());
     
-    fFastBoundsIsDirty = true;
+    fBoundsIsDirty = true;
 
     SkDEBUGCODE(this->validate();)
 }
@@ -1291,14 +1275,14 @@
     fPts.validate();
     fVerbs.validate();
 
-    if (!fFastBoundsIsDirty) {
+    if (!fBoundsIsDirty) {
         SkRect bounds;
-        compute_fast_bounds(&bounds, fPts);
+        compute_pt_bounds(&bounds, fPts);
         // can't call contains(), since it returns false if the rect is empty
-        SkASSERT(fFastBounds.fLeft <= bounds.fLeft);
-        SkASSERT(fFastBounds.fTop <= bounds.fTop);
-        SkASSERT(fFastBounds.fRight >= bounds.fRight);
-        SkASSERT(fFastBounds.fBottom >= bounds.fBottom);
+        SkASSERT(fBounds.fLeft <= bounds.fLeft);
+        SkASSERT(fBounds.fTop <= bounds.fTop);
+        SkASSERT(fBounds.fRight >= bounds.fRight);
+        SkASSERT(fBounds.fBottom >= bounds.fBottom);
     }
 }
 
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index ed47d64..77756a9 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -138,9 +138,7 @@
     validate();
     
     if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
-        SkRect bounds;
-        path.computeBounds(&bounds, SkPath::kFast_BoundsType);
-        return this->INHERITED::clipRect(bounds, op);
+        return this->INHERITED::clipRect(path.getBounds(), op);
     } else {
         return this->INHERITED::clipPath(path, op);
     }
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index b271970..cc3eb1a 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -271,11 +271,8 @@
             }
         } else {
             // just use devPath
-            SkRect  r;
             SkIRect ir;
-
-            devPath.computeBounds(&r, SkPath::kExact_BoundsType);
-            r.roundOut(&ir);
+            devPath.getBounds().roundOut(&ir);
             
             glyph->fLeft    = ir.fLeft;
             glyph->fTop     = ir.fTop;
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index 422d060..f2f117a 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -350,11 +350,8 @@
         return;
     }
 
-    SkRect      r;
-    SkIRect     ir;
-
-    path.computeBounds(&r, SkPath::kFast_BoundsType);
-    r.roundOut(&ir);
+    SkIRect ir;
+    path.getBounds().roundOut(&ir);
     if (ir.isEmpty()) {
         return;
     }
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
index 6a66754..b959968 100644
--- a/src/core/SkScan_Hairline.cpp
+++ b/src/core/SkScan_Hairline.cpp
@@ -248,11 +248,8 @@
 
     if (clip)
     {
-        SkRect      bounds;
-        SkIRect     ibounds;
-
-        path.computeBounds(&bounds, SkPath::kFast_BoundsType);
-        bounds.roundOut(&ibounds);
+        SkIRect ibounds;
+        path.getBounds().roundOut(&ibounds);
         ibounds.inset(-1, -1);
 
         if (clip->quickReject(ibounds))
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index fcf1530..ead1b85 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -582,11 +582,8 @@
         return;
     }
 
-    SkRect  r;
     SkIRect ir;
-
-    path.computeBounds(&r, SkPath::kFast_BoundsType);
-    r.round(&ir);
+    path.getBounds().round(&ir);
     if (ir.isEmpty()) {
         if (path.isInverseFillType()) {
             blitter->blitRegion(clip);
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index 86dff48..30d524e 100644
--- a/src/core/SkStroke.cpp
+++ b/src/core/SkStroke.cpp
@@ -538,8 +538,7 @@
         routine
     */
     static int needs_to_shrink(const SkPath& path) {
-        SkRect r;
-        path.computeBounds(&r, SkPath::kFast_BoundsType);
+        const SkRect& r = path.getBounds();
         SkFixed mask = SkAbs32(r.fLeft);
         mask |= SkAbs32(r.fTop);
         mask |= SkAbs32(r.fRight);