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
|
// empty for now, but lets get our subclass to remember to init us for the future
|
||||||
SkMaskFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
|
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:
|
private:
|
||||||
friend class SkDraw;
|
friend class SkDraw;
|
||||||
|
|
||||||
|
@ -13,6 +13,14 @@
|
|||||||
#include "SkDraw.h"
|
#include "SkDraw.h"
|
||||||
#include "SkRasterClip.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)
|
SK_DEFINE_INST_COUNT(SkMaskFilter)
|
||||||
|
|
||||||
bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
|
bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
|
||||||
@ -20,9 +28,190 @@ bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
|
|||||||
return false;
|
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,
|
bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
|
||||||
const SkRasterClip& clip, SkBounder* bounder,
|
const SkRasterClip& clip, SkBounder* bounder,
|
||||||
SkBlitter* blitter, SkPaint::Style style) {
|
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;
|
SkMask srcM, dstM;
|
||||||
|
|
||||||
if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
|
if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
|
||||||
@ -54,6 +243,14 @@ bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
|
|||||||
return true;
|
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 {
|
SkMaskFilter::BlurType SkMaskFilter::asABlur(BlurInfo*) const {
|
||||||
return kNone_BlurType;
|
return kNone_BlurType;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "SkBlurMaskFilter.h"
|
#include "SkBlurMaskFilter.h"
|
||||||
#include "SkBlurMask.h"
|
#include "SkBlurMask.h"
|
||||||
#include "SkFlattenableBuffers.h"
|
#include "SkFlattenableBuffers.h"
|
||||||
@ -27,6 +26,12 @@ public:
|
|||||||
|
|
||||||
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
|
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:
|
private:
|
||||||
SkScalar fRadius;
|
SkScalar fRadius;
|
||||||
SkBlurMaskFilter::BlurStyle fBlurStyle;
|
SkBlurMaskFilter::BlurStyle fBlurStyle;
|
||||||
@ -97,6 +102,97 @@ bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
|
|||||||
blurQuality, margin);
|
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) {
|
void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) {
|
||||||
dst->set(src.fLeft - fRadius, src.fTop - fRadius,
|
dst->set(src.fLeft - fRadius, src.fTop - fRadius,
|
||||||
src.fRight + fRadius, src.fBottom + fRadius);
|
src.fRight + fRadius, src.fBottom + fRadius);
|
||||||
|
Loading…
Reference in New Issue
Block a user