Chromium Code Reviews| Index: src/effects/SkBlurMaskFilter.cpp |
| diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp |
| index 6a5a39778cb73a6b1bdeeaf62a221437da389c34..227be9d35a6a7db336c241231fbf10a95cc5ae60 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,39 +160,62 @@ 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); |
| +static SkCanvas* prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) { |
| + SkASSERT(mask != NULL); |
| + |
| + bounds.roundOut(&mask->fBounds); |
| mask->fRowBytes = SkAlign4(mask->fBounds.width()); |
| mask->fFormat = SkMask::kA8_Format; |
| - size_t size = mask->computeImageSize(); |
| + const size_t size = mask->computeImageSize(); |
| mask->fImage = SkMask::AllocImage(size); |
| if (NULL == mask->fImage) { |
| - return false; |
| + return NULL; |
| } |
| - sk_bzero(mask->fImage, size); |
| + // FIXME: use sk_calloc in AllocImage? |
| + 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())); |
| + SkCanvas* canvas = SkNEW_ARGS(SkCanvas, (bitmap)); |
|
scroggo
2013/10/30 22:51:31
In trying to share code, I've added an allocation!
robertphillips
2013/10/31 00:24:58
Can you just set up the mask then return true or f
scroggo
2013/11/01 21:45:46
I could, but that means I'm still duplicating code
|
| + canvas->translate(-SkIntToScalar(mask->fBounds.left()), |
| + -SkIntToScalar(mask->fBounds.top())); |
| + return canvas; |
| +} |
| + |
| +static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) { |
| + SkAutoTUnref<SkCanvas> canvas(prepare_to_draw_into_mask(rrect.rect(), mask)); |
| + if (NULL == canvas.get()) { |
| + return false; |
| + } |
| + |
| + SkPaint paint; |
| + paint.setAntiAlias(true); |
| + canvas->drawRRect(rrect, paint); |
| + return true; |
| +} |
| + |
| +static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) { |
| + SkAutoTUnref<SkCanvas> canvas(prepare_to_draw_into_mask(rects[0], mask)); |
| + if (NULL == canvas.get()) { |
| + return false; |
| + } |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| if (1 == count) { |
| - canvas.drawRect(rects[0], paint); |
| + canvas->drawRect(rects[0], paint); |
| } else { |
| // todo: do I need a fast way to do this? |
| SkPath path; |
| path.addRect(rects[0]); |
| path.addRect(rects[1]); |
| path.setFillType(SkPath::kEvenOdd_FillType); |
| - canvas.drawPath(path, paint); |
| + canvas->drawPath(path, paint); |
| } |
| return true; |
| } |
| @@ -197,6 +225,105 @@ 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 { |
| + 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: |
|
robertphillips
2013/10/31 00:24:58
I think we should assert here (that the RRect isn'
scroggo
2013/11/01 21:45:46
Done.
|
| + // 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; |
| + } |
| + |
|
robertphillips
2013/10/31 13:30:31
Does this behave correctly if the RR is 100x100 wi
scroggo
2013/11/01 21:45:46
No. Latest patch fixes this. Thank you for the sug
|
| + // Now we figure out the appropriate width and height of the stretchy |
| + // rectangle. |
| + const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); |
| + const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); |
| + const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); |
| + const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); |
| + |
| + // The smaller rectangle must be large enough to include the larger |
| + // radius of all four sides, plus some extra for stretching. |
| + const SkScalar maxLeftXRadius = SkTMax(UL.fX, LL.fX); |
| + const SkScalar maxRightXRadius = SkTMax(UR.fX, LR.fX); |
| + |
|
robertphillips
2013/10/31 00:24:58
I think there should be a +3 in here and maybe mak
scroggo
2013/11/01 21:45:46
Done.
|
| + if (maxLeftXRadius + maxRightXRadius >= rrect.rect().width()) { |
| + // There is no valid piece to stretch. |
| + return kUnimplemented_FilterReturn; |
| + } |
| + |
| + const SkScalar maxTopYRadius = SkTMax(UL.fY, UR.fY); |
| + const SkScalar maxBottomYRadius = SkTMax(LL.fY, LR.fY); |
| + |
|
robertphillips
2013/10/31 00:24:58
Here too
scroggo
2013/11/01 21:45:46
Done.
|
| + if (maxTopYRadius + maxBottomYRadius >= rrect.rect().height()) { |
| + // There is no valid piece to stretch. |
| + return kUnimplemented_FilterReturn; |
| + } |
| + |
| + // Add some extra space to make sure we have a stretch row/col |
|
robertphillips
2013/10/31 00:24:58
Are the x & y values correct? Can they be 0 & 0?
scroggo
2013/11/01 21:45:46
The x and y values are irrelevant. I have switched
|
| + SkRect smallR = SkRect::MakeXYWH(rrect.rect().left(), rrect.rect().top(), |
| + maxLeftXRadius + maxRightXRadius + SkIntToScalar(3), |
| + maxTopYRadius + maxBottomYRadius + SkIntToScalar(3)); |
| + |
| + SkRRect smallRR; |
|
robertphillips
2013/10/31 00:24:58
Maybe change setRectRadii to setAllRadii?
scroggo
2013/11/01 21:45:46
I think the current name makes sense. It sets both
|
| + 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; |
| + // Stretch just beyond the radii. |
| + patch->fCenter.fX = SkScalarCeilToInt(maxLeftXRadius) + 1; |
| + patch->fCenter.fY = SkScalarCeilToInt(maxTopYRadius) + 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 |