aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-11-08 18:02:53 +0000
committerGravatar scroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-11-08 18:02:53 +0000
commita8e33a92e27ca1523601226cad83c79a7e00c93b (patch)
treedc9418e3ba5b6876a2f1a8bb603665c2dc540999 /src
parent1510726d6044119fab42a887d46ba922b890531d (diff)
Add ability to ninepatch blurred rounded rectangle
Speed up drawing large blurry round rectangles by converting them to nine patches. SkDraw: Add drawRRect. SkBitmapDevice: Call SkDraw::drawRRect instead of converting SkRRect to an SkPath. SkMaskFilter/SkBlurMaskFilter: Create a nine patch of a blurred round rect and draw it instead of drawing the entire thing. SkPDFDevice: Override drawRRect to perform the old behavior in SkBitmapDevice::drawRect. Depends on https://codereview.chromium.org/52703003 Tests are in https://codereview.chromium.org/52793005 BUG=https://b.corp.google.com/issue?id=11174385 R=reed@google.com, robertphillips@google.com Review URL: https://codereview.chromium.org/48623006 git-svn-id: http://skia.googlecode.com/svn/trunk@12198 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/core/SkBitmapDevice.cpp6
-rw-r--r--src/core/SkDraw.cpp46
-rw-r--r--src/core/SkMaskFilter.cpp27
-rw-r--r--src/effects/SkBlurMaskFilter.cpp155
-rw-r--r--src/pdf/SkPDFDevice.cpp8
5 files changed, 233 insertions, 9 deletions
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index eac21e2d48..b5ce122eb8 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -232,11 +232,7 @@ void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPa
void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) {
CHECK_FOR_ANNOTATION(paint);
- SkPath path;
- path.addRRect(rrect);
- // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
- // required to override drawRRect.
- this->drawPath(draw, path, paint, NULL, true);
+ draw.drawRRect(rrect, paint);
}
void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path,
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index af4f1ca047..f282bf5b7b 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -18,6 +18,7 @@
#include "SkPathEffect.h"
#include "SkRasterClip.h"
#include "SkRasterizer.h"
+#include "SkRRect.h"
#include "SkScan.h"
#include "SkShader.h"
#include "SkString.h"
@@ -1022,6 +1023,51 @@ bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
return false;
}
+void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const {
+ SkDEBUGCODE(this->validate());
+
+ if (fRC->isEmpty()) {
+ return;
+ }
+
+ {
+ // TODO: Investigate optimizing these options. They are in the same
+ // order as SkDraw::drawPath, which handles each case. It may be
+ // that there is no way to optimize for these using the SkRRect path.
+ SkScalar coverage;
+ if (SkDrawTreatAsHairline(paint, *fMatrix, &coverage)) {
+ goto DRAW_PATH;
+ }
+
+ if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
+ goto DRAW_PATH;
+ }
+
+ if (paint.getRasterizer()) {
+ goto DRAW_PATH;
+ }
+ }
+
+ if (paint.getMaskFilter()) {
+ // Transform the rrect into device space.
+ SkRRect devRRect;
+ if (rrect.transform(*fMatrix, &devRRect)) {
+ SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
+ if (paint.getMaskFilter()->filterRRect(devRRect, *fMatrix, *fRC,
+ fBounder, blitter.get(),
+ SkPaint::kFill_Style)) {
+ return; // filterRRect() called the blitter, so we're done
+ }
+ }
+ }
+
+DRAW_PATH:
+ // Now fall back to the default case of using a path.
+ SkPath path;
+ path.addRRect(rrect);
+ this->drawPath(path, paint, NULL, true);
+}
+
void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
const SkMatrix* prePathMatrix, bool pathIsMutable,
bool drawCoverage) const {
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 9c367f946b..cd25716377 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -12,6 +12,7 @@
#include "SkBounder.h"
#include "SkDraw.h"
#include "SkRasterClip.h"
+#include "SkRRect.h"
#include "SkTypes.h"
#if SK_SUPPORT_GPU
@@ -204,6 +205,26 @@ static int countNestedRects(const SkPath& path, SkRect rects[2]) {
return path.isRect(&rects[0]);
}
+bool SkMaskFilter::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
+ const SkRasterClip& clip, SkBounder* bounder,
+ SkBlitter* blitter, SkPaint::Style style) const {
+ // Attempt to speed up drawing by creating a nine patch. If a nine patch
+ // cannot be used, return false to allow our caller to recover and perform
+ // the drawing another way.
+ NinePatch patch;
+ patch.fMask.fImage = NULL;
+ if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
+ clip.getBounds(),
+ &patch)) {
+ SkASSERT(NULL == patch.fMask.fImage);
+ return false;
+ }
+ draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip,
+ bounder, blitter);
+ SkMask::FreeImage(patch.fMask.fImage);
+ return true;
+}
+
bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
const SkRasterClip& clip, SkBounder* bounder,
SkBlitter* blitter, SkPaint::Style style) const {
@@ -267,6 +288,12 @@ bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
}
SkMaskFilter::FilterReturn
+SkMaskFilter::filterRRectToNine(const SkRRect&, const SkMatrix&,
+ const SkIRect& clipBounds, NinePatch*) const {
+ return kUnimplemented_FilterReturn;
+}
+
+SkMaskFilter::FilterReturn
SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
const SkIRect& clipBounds, NinePatch*) const {
return kUnimplemented_FilterReturn;
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index a2e8065284..17690236ff 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,16 +160,50 @@ 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 bool 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;
}
+
+ // FIXME: use sk_calloc in AllocImage?
sk_bzero(mask->fImage, size);
+ return true;
+}
+
+static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
+ if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
+ return false;
+ }
+
+ // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
+ // clean way to share more code?
+ 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);
+ canvas.drawRRect(rrect, paint);
+ return true;
+}
+
+static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
+ if (!prepare_to_draw_into_mask(rects[0], mask)) {
+ return false;
+ }
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kA8_Config,
@@ -197,6 +236,114 @@ 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);
+ 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;
+ SkVector radii[4];
+ radii[SkRRect::kUpperLeft_Corner] = UL;
+ radii[SkRRect::kUpperRight_Corner] = UR;
+ radii[SkRRect::kLowerRight_Corner] = LR;
+ radii[SkRRect::kLowerLeft_Corner] = LL;
+ smallRR.setRectRadii(smallR, radii);
+
+ 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
@@ -303,7 +450,7 @@ SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
}
if (count > 1 || !c_analyticBlurNinepatch) {
- if (!drawRectsIntoMask(smallR, count, &srcM)) {
+ if (!draw_rects_into_mask(smallR, count, &srcM)) {
return kFalse_FilterReturn;
}
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 77aa7a353c..de71253d92 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -27,6 +27,7 @@
#include "SkPDFTypes.h"
#include "SkPDFUtils.h"
#include "SkRect.h"
+#include "SkRRect.h"
#include "SkString.h"
#include "SkTextFormatParams.h"
#include "SkTemplates.h"
@@ -967,6 +968,13 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& rect,
&content.entry()->fContent);
}
+void SkPDFDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect,
+ const SkPaint& paint) {
+ SkPath path;
+ path.addRRect(rrect);
+ this->drawPath(draw, path, paint, NULL, true);
+}
+
void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
const SkPaint& paint, const SkMatrix* prePathMatrix,
bool pathIsMutable) {