extend fastbounds impls to include maskfilters and drawloopers. This allows
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
This commit is contained in:
parent
bf6c1e4aff
commit
9efd9a048a
@ -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) {}
|
||||
|
@ -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&) {}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
SkRect bounds = {
|
||||
x, y,
|
||||
x + SkIntToScalar(bitmap.width()),
|
||||
y + SkIntToScalar(bitmap.height()));
|
||||
if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
#include "SkPaint.h"
|
||||
#include "SkColorFilter.h"
|
||||
#include "SkDrawLooper.h"
|
||||
#include "SkFontHost.h"
|
||||
#include "SkImageFilter.h"
|
||||
#include "SkMaskFilter.h"
|
||||
@ -1847,11 +1846,17 @@ 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,
|
||||
const SkRect& SkPaint::doComputeFastBounds(const SkRect& src,
|
||||
SkRect* storage) const {
|
||||
SkASSERT(storage);
|
||||
SkASSERT(this->getStyle() != SkPaint::kFill_Style);
|
||||
|
||||
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
|
||||
@ -1864,6 +1869,15 @@ const SkRect& SkPaint::computeStrokeFastBounds(const SkRect& src,
|
||||
}
|
||||
storage->set(src.fLeft - radius, src.fTop - radius,
|
||||
src.fRight + radius, src.fBottom + radius);
|
||||
} else {
|
||||
*storage = src;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user