add entry-point to SkMaskFilter to fast-path rectangles.

have blurmaskfilter override this new method.

#define SK_IGNORE_FAST_BLURRECT if you want to disable this (not sure if we'll need this)
Review URL: https://codereview.appspot.com/6815087

git-svn-id: http://skia.googlecode.com/svn/trunk@6363 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2012-11-09 14:30:48 +00:00
parent d9f7503e0c
commit d729b3e504
3 changed files with 320 additions and 1 deletions

View File

@ -105,6 +105,32 @@ protected:
// empty for now, but lets get our subclass to remember to init us for the future
SkMaskFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
enum FilterReturn {
kFalse_FilterReturn,
kTrue_FilterReturn,
kUnimplemented_FilterReturn
};
/**
* Override if your subclass can filter a rect, and return the answer as
* a ninepatch mask to be stretched over the returned outerRect. On success
* return kTrue_FilterReturn. On failure (e.g. out of memory) return
* kFalse_FilterReturn. If the normal filterMask() entry-point should be
* called (the default) return kUnimplemented_FilterReturn.
*
* By convention, the caller will take the center rol/col from the returned
* mask as the slice it can replicate horizontally and vertically as we
* stretch the mask to fit inside outerRect. It is an error for outerRect
* to be smaller than the mask's bounds. This would imply that the width
* and height of the mask should be odd. This is not required, just that
* the caller will call mask.fBounds.centerX() and centerY() to find the
* strips that will be replicated.
*/
virtual FilterReturn filterRectToNine(const SkRect&, const SkMatrix&,
const SkIRect& clipBounds,
SkMask* ninePatchMask,
SkIRect* outerRect);
private:
friend class SkDraw;

View File

@ -13,6 +13,14 @@
#include "SkDraw.h"
#include "SkRasterClip.h"
#ifdef SK_IGNORE_FAST_BLURRECT
#if (SK_IGNORE_FAST_BLURRECT != 0 && SK_IGNORE_FAST_BLURRECT != 1)
#error "SK_IGNORE_FAST_BLURRECT must be 0 or 1 or undefined"
#endif
#else
#define SK_IGNORE_FAST_BLURRECT 0
#endif
SK_DEFINE_INST_COUNT(SkMaskFilter)
bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
@ -20,9 +28,190 @@ bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
return false;
}
static void extractMaskSubset(const SkMask& src, SkMask* dst) {
SkASSERT(src.fBounds.contains(dst->fBounds));
const int dx = dst->fBounds.left() - src.fBounds.left();
const int dy = dst->fBounds.top() - src.fBounds.top();
dst->fImage = src.fImage + dy * src.fRowBytes + dx;
dst->fRowBytes = src.fRowBytes;
dst->fFormat = src.fFormat;
}
static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
const SkIRect& bounds, const SkIRect& clipR) {
SkIRect r;
if (r.intersect(bounds, clipR)) {
blitter->blitMask(mask, r);
}
}
static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
SkIRect r;
if (r.intersect(rect, clipR)) {
blitter->blitRect(r.left(), r.top(), r.width(), r.height());
}
}
#if 0
static void dump(const SkMask& mask) {
for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
SkDebugf("%02X", *mask.getAddr8(x, y));
}
SkDebugf("\n");
}
SkDebugf("\n");
}
#endif
static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
const SkIRect& clipR, SkBlitter* blitter) {
int cx = mask.fBounds.left() + mask.fBounds.right() >> 1;
int cy = mask.fBounds.top() + mask.fBounds.bottom() >> 1;
SkMask m;
// top-left
m.fBounds = mask.fBounds;
m.fBounds.fRight = cx;
m.fBounds.fBottom = cy;
extractMaskSubset(mask, &m);
m.fBounds.offsetTo(outerR.left(), outerR.top());
blitClippedMask(blitter, m, m.fBounds, clipR);
// top-right
m.fBounds = mask.fBounds;
m.fBounds.fLeft = cx + 1;
m.fBounds.fBottom = cy;
extractMaskSubset(mask, &m);
m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
blitClippedMask(blitter, m, m.fBounds, clipR);
// bottom-left
m.fBounds = mask.fBounds;
m.fBounds.fRight = cx;
m.fBounds.fTop = cy + 1;
extractMaskSubset(mask, &m);
m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
blitClippedMask(blitter, m, m.fBounds, clipR);
// bottom-right
m.fBounds = mask.fBounds;
m.fBounds.fLeft = cx + 1;
m.fBounds.fTop = cy + 1;
extractMaskSubset(mask, &m);
m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
outerR.bottom() - m.fBounds.height());
blitClippedMask(blitter, m, m.fBounds, clipR);
SkIRect innerR;
innerR.set(outerR.left() + cx - mask.fBounds.left(),
outerR.top() + cy - mask.fBounds.top(),
outerR.right() + (cx + 1 - mask.fBounds.right()),
outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
blitClippedRect(blitter, innerR, clipR);
const int innerW = innerR.width();
size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
SkAutoSMalloc<4*1024> storage(storageSize);
int16_t* runs = (int16_t*)storage.get();
uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
SkIRect r;
// top
r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
if (r.intersect(clipR)) {
int startY = SkMax32(0, r.top() - outerR.top());
int stopY = startY + r.height();
int width = r.width();
for (int y = startY; y < stopY; ++y) {
runs[0] = width;
runs[width] = 0;
alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
}
}
// bottom
r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
if (r.intersect(clipR)) {
int startY = outerR.bottom() - r.bottom();
int stopY = startY + r.height();
int width = r.width();
for (int y = startY; y < stopY; ++y) {
runs[0] = width;
runs[width] = 0;
alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
}
}
// left
r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
if (r.intersect(clipR)) {
int startX = r.left() - outerR.left();
int stopX = startX + r.width();
int height = r.height();
for (int x = startX; x < stopX; ++x) {
blitter->blitV(outerR.left() + x, r.top(), height,
*mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
}
}
// right
r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
if (r.intersect(clipR)) {
int startX = outerR.right() - r.right();
int stopX = startX + r.width();
int height = r.height();
for (int x = startX; x < stopX; ++x) {
blitter->blitV(outerR.right() - x - 1, r.top(), height,
*mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
}
}
}
static void draw_nine(const SkMask& mask, const SkIRect& outerR,
const SkRasterClip& clip, SkBounder* bounder,
SkBlitter* blitter) {
// if we get here, we need to (possibly) resolve the clip and blitter
SkAAClipBlitterWrapper wrapper(clip, blitter);
blitter = wrapper.getBlitter();
SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
if (!clipper.done() && (!bounder || bounder->doIRect(outerR))) {
const SkIRect& cr = clipper.rect();
do {
draw_nine_clipped(mask, outerR, cr, blitter);
clipper.next();
} while (!clipper.done());
}
}
bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
const SkRasterClip& clip, SkBounder* bounder,
SkBlitter* blitter, SkPaint::Style style) {
SkRect rect;
if (!SK_IGNORE_FAST_BLURRECT &&
devPath.isRect(&rect) && SkPaint::kFill_Style == style) {
SkMask mask;
SkIRect outerBounds;
mask.fImage = NULL;
switch (this->filterRectToNine(rect, matrix, clip.getBounds(), &mask,
&outerBounds)) {
case kFalse_FilterReturn:
SkASSERT(!mask.fImage);
return false;
case kTrue_FilterReturn:
draw_nine(mask, outerBounds, clip, bounder, blitter);
SkMask::FreeImage(mask.fImage);
return true;
case kUnimplemented_FilterReturn:
SkASSERT(!mask.fImage);
// fall through
break;
}
}
SkMask srcM, dstM;
if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
@ -54,6 +243,14 @@ bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
return true;
}
SkMaskFilter::FilterReturn
SkMaskFilter::filterRectToNine(const SkRect&, const SkMatrix&,
const SkIRect& clipBounds,
SkMask* ninePatchMask,
SkIRect* outerRect) {
return kUnimplemented_FilterReturn;
}
SkMaskFilter::BlurType SkMaskFilter::asABlur(BlurInfo*) const {
return kNone_BlurType;
}

View File

@ -6,7 +6,6 @@
* found in the LICENSE file.
*/
#include "SkBlurMaskFilter.h"
#include "SkBlurMask.h"
#include "SkFlattenableBuffers.h"
@ -27,6 +26,12 @@ public:
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
protected:
virtual FilterReturn filterRectToNine(const SkRect&, const SkMatrix&,
const SkIRect& clipBounds,
SkMask* ninePatchMask,
SkIRect* outerRect) SK_OVERRIDE;
private:
SkScalar fRadius;
SkBlurMaskFilter::BlurStyle fBlurStyle;
@ -97,6 +102,97 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
blurQuality, margin);
}
#include "SkCanvas.h"
static bool drawRectIntoMask(const SkRect& r, SkMask* mask) {
r.roundOut(&mask->fBounds);
mask->fRowBytes = SkAlign4(mask->fBounds.width());
mask->fFormat = SkMask::kA8_Format;
size_t size = mask->computeImageSize();
mask->fImage = SkMask::AllocImage(size);
if (NULL == mask->fImage) {
return false;
}
sk_bzero(mask->fImage, size);
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kA8_Config,
mask->fBounds.width(), mask->fBounds.height(),
mask->fRowBytes);
bitmap.setPixels(mask->fImage);
SkCanvas canvas(bitmap);
canvas.translate(-SkScalarFloorToScalar(r.left()),
-SkScalarFloorToScalar(r.top()));
SkPaint paint;
paint.setAntiAlias(true);
canvas.drawRect(r, paint);
return true;
}
SkMaskFilter::FilterReturn
SkBlurMaskFilterImpl::filterRectToNine(const SkRect& rect, const SkMatrix& matrix,
const SkIRect& clipBounds,
SkMask* ninePatchMask,
SkIRect* outerRect) {
SkIPoint margin;
SkMask srcM, dstM;
rect.roundOut(&srcM.fBounds);
srcM.fImage = NULL;
srcM.fFormat = SkMask::kA8_Format;
srcM.fRowBytes = 0;
if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
return kFalse_FilterReturn;
}
/*
* smallR is the smallest version of 'rect' that will still guarantee that
* we get the same blur results on all edges, plus 1 center row/col that is
* representative of the extendible/stretchable edges of the ninepatch.
* Since our actual edge may be fractional we inset 1 more to be sure we
* don't miss any interior blur.
* x is an added pixel of blur, and { and } are the (fractional) edge
* pixels from the original rect.
*
* x x { x x .... x x } x x
*
* Thus, in this case, we inset by a total of 5 (on each side) beginning
* with our outer-rect (dstM.fBounds)
*/
SkRect smallR = rect;
{
// +3 is from +1 for each edge (to account for possible fractional pixel
// edges, and +1 to make room for a center rol/col.
int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 3;
int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 3;
// we want the inset amounts to be integral, so we don't change any
// fractional phase on the fRight or fBottom of our smallR.
SkScalar dx = SkIntToScalar(srcM.fBounds.width() - smallW);
SkScalar dy = SkIntToScalar(srcM.fBounds.height() - smallH);
if (dx < 0 || dy < 0) {
// we're too small, relative to our blur, to break into nine-patch,
// so we ask to have our normal filterMask() be called.
return kUnimplemented_FilterReturn;
}
SkASSERT(dx >= 0 && dy >= 0);
smallR.set(rect.left(), rect.top(), rect.right() - dx, rect.bottom() - dy);
SkASSERT(!smallR.isEmpty());
}
if (!drawRectIntoMask(smallR, &srcM)) {
return kFalse_FilterReturn;
}
if (!this->filterMask(ninePatchMask, srcM, matrix, &margin)) {
return kFalse_FilterReturn;
}
ninePatchMask->fBounds.offsetTo(0, 0);
*outerRect = dstM.fBounds;
return kTrue_FilterReturn;
}
void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) {
dst->set(src.fLeft - fRadius, src.fTop - fRadius,
src.fRight + fRadius, src.fBottom + fRadius);