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:
parent
d9f7503e0c
commit
d729b3e504
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user