extend fastbounds impls to include maskfilters and drawloopers. This allows
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 30 Jan 2012 15:41:43 +0000 (15:41 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 30 Jan 2012 15:41:43 +0000 (15:41 +0000)
us to perform quick-rejects when drawing objects with shadows (esp. text).
WebKit draws shadows w/ a looper (fg and shadow) and a maskfilter on the
shadow layer.

git-svn-id: http://skia.googlecode.com/svn/trunk@3103 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkDrawLooper.h
include/core/SkMaskFilter.h
include/core/SkPaint.h
src/core/SkCanvas.cpp
src/core/SkMaskFilter.cpp
src/core/SkPaint.cpp
src/core/SkPictureFlat.h
src/core/SkPicturePlayback.cpp
src/core/SkPictureRecord.cpp
src/core/SkPictureRecord.h
src/effects/SkBlurMaskFilter.cpp

index 4d9f718..e8265db 100644 (file)
@@ -45,6 +45,20 @@ public:
      */
     virtual bool next(SkCanvas*, SkPaint* paint) = 0;
     
+    /**
+     * The fast bounds functions are used to enable the paint to be culled early
+     * in the drawing pipeline. If a subclass can support this feature it must
+     * return true for the canComputeFastBounds() function.  If that function
+     * returns false then computeFastBounds behavior is undefined otherwise it
+     * is expected to have the following behavior. Given the parent paint and
+     * the parent's bounding rect the subclass must fill in and return the
+     * storage rect, where the storage rect is with the union of the src rect
+     * and the looper's bounding rect.
+     */
+    virtual bool canComputeFastBounds(const SkPaint& paint);
+    virtual void computeFastBounds(const SkPaint& paint,
+                                   const SkRect& src, SkRect* dst);
+
 protected:
     SkDrawLooper() {}
     SkDrawLooper(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
index 1f1d570..2808de7 100644 (file)
@@ -79,6 +79,19 @@ public:
      */
     virtual BlurType asABlur(BlurInfo*) const;
 
+    /**
+     * The fast bounds function is used to enable the paint to be culled early
+     * in the drawing pipeline. This function accepts the current bounds of the
+     * paint as its src param and the filter adjust those bounds using its
+     * current mask and returns the result using the dest param. Callers are
+     * allowed to provide the same struct for both src and dest so each
+     * implementation must accomodate that behavior.
+     *
+     *  The default impl calls filterMask with the src mask having no image,
+     *  but subclasses may override this if they can compute the rect faster.
+     */
+    virtual void computeFastBounds(const SkRect& src, SkRect* dest);
+
 protected:
     // empty for now, but lets get our subclass to remember to init us for the future
     SkMaskFilter(SkFlattenableReadBuffer&) {}
index f5d98a9..e4717af 100644 (file)
@@ -11,6 +11,7 @@
 #define SkPaint_DEFINED
 
 #include "SkColor.h"
+#include "SkDrawLooper.h"
 #include "SkXfermode.h"
 
 class SkAutoGlyphCache;
@@ -28,7 +29,6 @@ class SkPath;
 class SkPathEffect;
 class SkRasterizer;
 class SkShader;
-class SkDrawLooper;
 class SkTypeface;
 
 typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**,
@@ -438,10 +438,11 @@ public:
         the bounds computation expensive.
     */
     bool canComputeFastBounds() const {
+        if (this->getLooper()) {
+            return this->getLooper()->canComputeFastBounds(*this);
+        }
         // 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()) |
+        return (reinterpret_cast<uintptr_t>(this->getRasterizer()) |
                 reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
     }
 
@@ -467,8 +468,12 @@ public:
         }
     */
     const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const {
-        return this->getStyle() == kFill_Style ? orig :
-                    this->computeStrokeFastBounds(orig, storage);
+        if (this->getStyle() == kFill_Style &&
+                !this->getLooper() && !this->getMaskFilter()) {
+            return orig;
+        }
+
+        return this->doComputeFastBounds(orig, storage);
     }
 
     /** Get the paint's shader object.
@@ -892,8 +897,7 @@ private:
                         void (*proc)(const SkDescriptor*, void*),
                         void* context, bool ignoreGamma = false) const;
 
-    const SkRect& computeStrokeFastBounds(const SkRect& orig,
-                                          SkRect* storage) const;
+    const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage) const;
 
     enum {
         kCanonicalTextSizeForPaths = 64
index ed65eca..306a0f4 100644 (file)
@@ -1397,11 +1397,15 @@ void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
     SkDEBUGCODE(bitmap.validate();)
 
     if (NULL == paint || paint->canComputeFastBounds()) {
-        SkRect fastBounds;
-        fastBounds.set(x, y,
-                       x + SkIntToScalar(bitmap.width()),
-                       y + SkIntToScalar(bitmap.height()));
-        if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
+        SkRect bounds = {
+            x, y,
+            x + SkIntToScalar(bitmap.width()),
+            y + SkIntToScalar(bitmap.height())
+        };
+        if (paint) {
+            (void)paint->computeFastBounds(bounds, &bounds);
+        }
+        if (this->quickReject(bounds, paint2EdgeType(paint))) {
             return;
         }
     }
@@ -1420,7 +1424,12 @@ void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src
 
     // do this now, to avoid the cost of calling extract for RLE bitmaps
     if (NULL == paint || paint->canComputeFastBounds()) {
-        if (this->quickReject(dst, paint2EdgeType(paint))) {
+        SkRect storage;
+        const SkRect* bounds = &dst;
+        if (paint) {
+            bounds = &paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(*bounds, paint2EdgeType(paint))) {
             return;
         }
     }
index 3bdca10..42d07a6 100644 (file)
@@ -56,4 +56,20 @@ SkMaskFilter::BlurType SkMaskFilter::asABlur(BlurInfo*) const {
     return kNone_BlurType;
 }
 
+void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) {
+    SkMask  srcM, dstM;
+
+    srcM.fImage = NULL;
+    src.roundOut(&srcM.fBounds);
+    srcM.fRowBytes = 0;
+    srcM.fFormat = SkMask::kA8_Format;
+
+    SkIPoint margin;    // ignored
+    if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
+        dst->set(dstM.fBounds);
+    } else {
+        dst->set(srcM.fBounds);
+    }
+}
+
 
index cf25dbf..7d418f4 100644 (file)
@@ -9,7 +9,6 @@
 
 #include "SkPaint.h"
 #include "SkColorFilter.h"
-#include "SkDrawLooper.h"
 #include "SkFontHost.h"
 #include "SkImageFilter.h"
 #include "SkMaskFilter.h"
@@ -1847,23 +1846,38 @@ bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
     return width != 0;  // return true if we're filled, or false if we're hairline (width == 0)
 }
 
-const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
-                                               SkRect* storage) const {
+const SkRect& SkPaint::doComputeFastBounds(const SkRect& src,
+                                                 SkRect* storage) const {
     SkASSERT(storage);
-    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);
+
+    if (this->getLooper()) {
+        SkASSERT(this->getLooper()->canComputeFastBounds(*this));
+        this->getLooper()->computeFastBounds(*this, src, storage);
+        return *storage;
+    }
+
+    if (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);
+    } else {
+        *storage = src;
     }
-    storage->set(src.fLeft - radius, src.fTop - radius,
-                 src.fRight + radius, src.fBottom + radius);
+
+    // check the mask filter
+    if (this->getMaskFilter()) {
+        this->getMaskFilter()->computeFastBounds(*storage, storage);
+    }
+
     return *storage;
 }
 
@@ -2021,3 +2035,48 @@ bool SkImageFilter::asABlur(SkSize* sigma) const {
     return false;
 }
 
+//////
+
+bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
+    SkCanvas canvas;
+
+    this->init(&canvas);
+    for (;;) {
+        SkPaint p(paint);
+        if (this->next(&canvas, &p)) {
+            p.setLooper(NULL);
+            if (!p.canComputeFastBounds()) {
+                return false;
+            }
+        } else {
+            break;
+        }
+    }
+    return true;
+}
+
+void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src,
+                                     SkRect* dst) {
+    SkCanvas canvas;
+    
+    this->init(&canvas);
+    for (bool firstTime = true;; firstTime = false) {
+        SkPaint p(paint);
+        if (this->next(&canvas, &p)) {
+            SkRect r(src);
+
+            p.setLooper(NULL);
+            p.computeFastBounds(r, &r);
+            canvas.getTotalMatrix().mapRect(&r);
+
+            if (firstTime) {
+                *dst = r;
+            } else {
+                dst->join(r);
+            }
+        } else {
+            break;
+        }
+    }
+}
+
index bd212b8..983bfc5 100644 (file)
@@ -33,6 +33,7 @@ enum DrawType {
     DRAW_PICTURE,
     DRAW_POINTS,
     DRAW_POS_TEXT,
+    DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
     DRAW_POS_TEXT_H,
     DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
     DRAW_RECT,
index d922051..c2082c7 100644 (file)
@@ -606,6 +606,17 @@ void SkPicturePlayback::draw(SkCanvas& canvas) {
                 const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
                 canvas.drawPosText(text.text(), text.length(), pos, paint);
             } break;
+            case DRAW_POS_TEXT_TOP_BOTTOM: {
+                const SkPaint& paint = *getPaint();
+                getText(&text);
+                size_t points = getInt();
+                const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
+                const SkScalar top = fReader.readScalar();
+                const SkScalar bottom = fReader.readScalar();
+                if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+                    canvas.drawPosText(text.text(), text.length(), pos, paint);
+                }
+            } break;
             case DRAW_POS_TEXT_H: {
                 const SkPaint& paint = *getPaint();
                 getText(&text);
index 1c57c04..f31065b 100644 (file)
@@ -287,14 +287,14 @@ void SkPictureRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
 }
 
 void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
-                                              SkScalar baselineY) {
+                                              SkScalar minY, SkScalar maxY) {
     SkPaint::FontMetrics metrics;
     paint.getFontMetrics(&metrics);
     SkRect bounds;
     // construct a rect so we can see any adjustments from the paint.
     // we use 0,1 for left,right, just so the rect isn't empty
-    bounds.set(0, metrics.fTop + baselineY,
-               SK_Scalar1, metrics.fBottom + baselineY);
+    bounds.set(0, metrics.fTop + minY,
+               SK_Scalar1, metrics.fBottom + maxY);
     (void)paint.computeFastBounds(bounds, &bounds);
     // now record the top and bottom
     addScalar(bounds.fTop);
@@ -311,7 +311,7 @@ void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
     addScalar(x);
     addScalar(y);
     if (fast) {
-        addFontMetricsTopBottom(paint, y);
+        addFontMetricsTopBottom(paint, y, y);
     }
     validate();
 }
@@ -323,23 +323,34 @@ void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
         return;
 
     bool canUseDrawH = true;
+    SkScalar minY = pos[0].fY;
+    SkScalar maxY = pos[0].fY;
     // check if the caller really should have used drawPosTextH()
     {
         const SkScalar firstY = pos[0].fY;
         for (size_t index = 1; index < points; index++) {
             if (pos[index].fY != firstY) {
                 canUseDrawH = false;
-                break;
+                if (pos[index].fY < minY) {
+                    minY = pos[index].fY;
+                } else if (pos[index].fY > maxY) {
+                    maxY = pos[index].fY;
+                }
             }
         }
     }
 
-    bool fast = canUseDrawH && paint.canComputeFastBounds();
+    bool fastBounds = paint.canComputeFastBounds();
+    bool fast = canUseDrawH && fastBounds;
 
     if (fast) {
         addDraw(DRAW_POS_TEXT_H_TOP_BOTTOM);
+    } else if (canUseDrawH) {
+        addDraw(DRAW_POS_TEXT_H);
+    } else if (fastBounds) {
+        addDraw(DRAW_POS_TEXT_TOP_BOTTOM);
     } else {
-        addDraw(canUseDrawH ? DRAW_POS_TEXT_H : DRAW_POS_TEXT);
+        addDraw(DRAW_POS_TEXT);
     }
     addPaint(paint);
     addText(text, byteLength);
@@ -350,7 +361,7 @@ void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
 #endif
     if (canUseDrawH) {
         if (fast) {
-            addFontMetricsTopBottom(paint, pos[0].fY);
+            addFontMetricsTopBottom(paint, pos[0].fY, pos[0].fY);
         }
         addScalar(pos[0].fY);
         SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
@@ -359,6 +370,9 @@ void SkPictureRecord::drawPosText(const void* text, size_t byteLength,
     }
     else {
         fWriter.writeMul4(pos, points * sizeof(SkPoint));
+        if (fastBounds) {
+            addFontMetricsTopBottom(paint, minY, maxY);
+        }
     }
 #ifdef SK_DEBUG_SIZE
     fPointBytes += fWriter.size() - start;
@@ -385,7 +399,7 @@ void SkPictureRecord::drawPosTextH(const void* text, size_t byteLength,
     size_t start = fWriter.size();
 #endif
     if (fast) {
-        addFontMetricsTopBottom(paint, constY);
+        addFontMetricsTopBottom(paint, constY, constY);
     }
     addScalar(constY);
     fWriter.writeMul4(xpos, points * sizeof(SkScalar));
index 77f7173..ff6585d 100644 (file)
@@ -66,7 +66,7 @@ public:
                               const SkPaint&) SK_OVERRIDE;
     virtual void drawData(const void*, size_t) SK_OVERRIDE;
 
-    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar baselineY);
+    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar minY, SkScalar maxY);
 
     const SkTDArray<const SkFlatBitmap* >& getBitmaps() const {
         return fBitmaps;
index 4c677d4..83ee934 100644 (file)
@@ -22,6 +22,7 @@ public:
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
                             SkIPoint* margin) SK_OVERRIDE;
     virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
+    virtual void computeFastBounds(const SkRect& src, SkRect* dest) SK_OVERRIDE;
 
     // overrides from SkFlattenable
     virtual Factory getFactory() SK_OVERRIDE;
@@ -98,6 +99,11 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
                             blurQuality, margin);
 }
 
+void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) {
+    dst->set(src.fLeft - fRadius, src.fTop - fRadius,
+             src.fRight + fRadius, src.fBottom + fRadius);
+}
+
 SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkFlattenableReadBuffer& buffer) {
     return SkNEW_ARGS(SkBlurMaskFilterImpl, (buffer));
 }