Index: src/effects/SkBlurMaskFilter.cpp |
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp |
index 6a5a39778cb73a6b1bdeeaf62a221437da389c34..df754d23bc0a0c3fe556379a131e5ef8d8abd2e7 100644 |
--- a/src/effects/SkBlurMaskFilter.cpp |
+++ b/src/effects/SkBlurMaskFilter.cpp |
@@ -11,6 +11,7 @@ |
#include "SkGpuBlurUtils.h" |
#include "SkFlattenableBuffers.h" |
#include "SkMaskFilter.h" |
+#include "SkRRect.h" |
#include "SkRTConf.h" |
#include "SkStringUtils.h" |
#include "SkStrokeRec.h" |
@@ -52,6 +53,10 @@ protected: |
const SkIRect& clipBounds, |
NinePatch*) const SK_OVERRIDE; |
+ virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, |
+ const SkIRect& clipBounds, |
+ NinePatch*) const SK_OVERRIDE; |
+ |
bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, |
SkIPoint* margin, SkMask::CreateMode createMode) const; |
@@ -155,29 +160,44 @@ bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, |
#include "SkCanvas.h" |
-static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) { |
- rects[0].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(-SkIntToScalar(mask->fBounds.left()), |
- -SkIntToScalar(mask->fBounds.top())); |
+// Common code used by both draw_rrect_into_mask and drawRectsIntoMask. |
reed1
2013/11/04 17:46:52
errrrrrr, a giant macro with an embedded return? D
scroggo
2013/11/04 19:52:54
Ideally, I'd like to share common code for creatin
|
+// After this macro, mask has been set to bounds, its fImage has been |
+// allocated, and there is a new SkCanvas (called 'canvas') with its |
+// translation set to draw into, and a new SkPaint with antialias exists |
+// for drawing into it. |
+#define PREPARE_TO_DRAW_INTO_MASK(bounds, mask) \ |
+ SkASSERT(mask != NULL); \ |
+ \ |
+ bounds.roundOut(&mask->fBounds); \ |
+ mask->fRowBytes = SkAlign4(mask->fBounds.width()); \ |
+ mask->fFormat = SkMask::kA8_Format; \ |
+ const 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(-SkIntToScalar(mask->fBounds.left()), \ |
+ -SkIntToScalar(mask->fBounds.top())); \ |
+ SkPaint paint; \ |
+ paint.setAntiAlias(true) |
+ |
+static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) { |
+ PREPARE_TO_DRAW_INTO_MASK(rrect.rect(), mask); |
+ canvas.drawRRect(rrect, paint); |
+ return true; |
+} |
- SkPaint paint; |
- paint.setAntiAlias(true); |
+static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) { |
+ PREPARE_TO_DRAW_INTO_MASK(rects[0], mask); |
if (1 == count) { |
canvas.drawRect(rects[0], paint); |
@@ -197,6 +217,109 @@ static bool rect_exceeds(const SkRect& r, SkScalar v) { |
r.width() > v || r.height() > v; |
} |
+SkMaskFilter::FilterReturn |
+SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, |
+ const SkIRect& clipBounds, |
+ NinePatch* patch) const { |
+ SkASSERT(patch != NULL); |
+ switch (rrect.getType()) { |
+ case SkRRect::kUnknown_Type: |
+ // Unknown should never be returned. |
+ SkASSERT(false); |
+ // Fall through. |
+ case SkRRect::kEmpty_Type: |
+ // Nothing to draw. |
+ return kFalse_FilterReturn; |
+ |
+ case SkRRect::kRect_Type: |
+ // We should have caught this earlier. |
+ SkASSERT(false); |
+ // Fall through. |
+ case SkRRect::kOval_Type: |
+ // The nine patch special case does not handle ovals, and we |
+ // already have code for rectangles. |
+ return kUnimplemented_FilterReturn; |
+ |
+ case SkRRect::kSimple_Type: |
+ // Fall through. |
+ case SkRRect::kComplex_Type: |
+ // These can take advantage of this fast path. |
+ break; |
+ } |
+ |
+ // TODO: report correct metrics for innerstyle, where we do not grow the |
+ // total bounds, but we do need an inset the size of our blur-radius |
+ if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) { |
+ return kUnimplemented_FilterReturn; |
+ } |
+ |
+ // TODO: take clipBounds into account to limit our coordinates up front |
+ // for now, just skip too-large src rects (to take the old code path). |
+ if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) { |
+ return kUnimplemented_FilterReturn; |
+ } |
+ |
+ SkIPoint margin; |
+ SkMask srcM, dstM; |
+ rrect.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; |
+ } |
+ |
+ // Now figure out the appropriate width and height of the smaller round rectangle |
+ // to stretch. It will take into account the larger radius per side as well as double |
+ // the margin, to account for inner and outer blur. |
+ const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); |
reed1
2013/11/04 17:46:52
we're not calling the new allRadii API here...? (w
scroggo
2013/11/04 19:52:54
Really getAllRadii() is just a convenience for my
|
+ const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); |
+ const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); |
+ const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); |
+ |
+ const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX); |
+ const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX); |
+ |
+ // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover |
+ // any fractional space on either side plus 1 for the part to stretch. |
+ const SkScalar stretchSize = SkIntToScalar(3); |
+ |
+ const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; |
+ if (totalSmallWidth >= rrect.rect().width()) { |
+ // There is no valid piece to stretch. |
+ return kUnimplemented_FilterReturn; |
+ } |
+ |
+ const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY); |
+ const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY); |
+ |
+ const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize; |
+ if (totalSmallHeight >= rrect.rect().height()) { |
+ // There is no valid piece to stretch. |
+ return kUnimplemented_FilterReturn; |
+ } |
+ |
+ SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight); |
+ |
+ SkRRect smallRR; |
+ smallRR.setRectRadii(smallR, rrect.getAllRadii()); |
+ |
+ if (!draw_rrect_into_mask(smallRR, &srcM)) { |
+ return kFalse_FilterReturn; |
+ } |
+ |
+ if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { |
+ return kFalse_FilterReturn; |
+ } |
+ |
+ patch->fMask.fBounds.offsetTo(0, 0); |
+ patch->fOuterRect = dstM.fBounds; |
+ patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1; |
+ patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1; |
+ return kTrue_FilterReturn; |
+} |
+ |
#ifdef SK_IGNORE_FAST_RECT_BLUR |
SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" ); |
#else |