*/
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) {}
*/
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&) {}
#define SkPaint_DEFINED
#include "SkColor.h"
+#include "SkDrawLooper.h"
#include "SkXfermode.h"
class SkAutoGlyphCache;
class SkPathEffect;
class SkRasterizer;
class SkShader;
-class SkDrawLooper;
class SkTypeface;
typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**,
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;
}
}
*/
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.
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
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;
}
}
// 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;
}
}
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);
+ }
+}
+
#include "SkPaint.h"
#include "SkColorFilter.h"
-#include "SkDrawLooper.h"
#include "SkFontHost.h"
#include "SkImageFilter.h"
#include "SkMaskFilter.h"
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;
}
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;
+ }
+ }
+}
+
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,
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);
}
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);
addScalar(x);
addScalar(y);
if (fast) {
- addFontMetricsTopBottom(paint, y);
+ addFontMetricsTopBottom(paint, y, y);
}
validate();
}
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);
#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));
}
else {
fWriter.writeMul4(pos, points * sizeof(SkPoint));
+ if (fastBounds) {
+ addFontMetricsTopBottom(paint, minY, maxY);
+ }
}
#ifdef SK_DEBUG_SIZE
fPointBytes += fWriter.size() - start;
size_t start = fWriter.size();
#endif
if (fast) {
- addFontMetricsTopBottom(paint, constY);
+ addFontMetricsTopBottom(paint, constY, constY);
}
addScalar(constY);
fWriter.writeMul4(xpos, points * sizeof(SkScalar));
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;
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;
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));
}