blob: 8c14bc88450a764066419a6cc7266d744a081d17 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkBlurMaskFilter.h"
10#include "SkBlurMask.h"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000011#include "SkFlattenableBuffers.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkMaskFilter.h"
humper@google.com7c5d7b72013-03-11 20:16:28 +000013#include "SkRTConf.h"
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +000014#include "SkStringUtils.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000015#include "SkStrokeRec.h"
16
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#include "GrTexture.h"
20#include "effects/GrSimpleTextureEffect.h"
21#include "SkGrPixelRef.h"
22#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
24class SkBlurMaskFilterImpl : public SkMaskFilter {
25public:
reed@google.com03016a32011-08-12 14:59:59 +000026 SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle,
27 uint32_t flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
29 // overrides from SkMaskFilter
reed@google.com30711b72012-12-18 19:18:39 +000030 virtual SkMask::Format getFormat() const SK_OVERRIDE;
reed@google.com03016a32011-08-12 14:59:59 +000031 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +000032 SkIPoint* margin) const SK_OVERRIDE;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000033
robertphillips@google.com49149312013-07-03 15:34:35 +000034#if SK_SUPPORT_GPU
35 virtual bool canFilterMaskGPU(const SkRect& devBounds,
36 const SkIRect& clipBounds,
37 const SkMatrix& ctm,
38 SkRect* maskRect) const SK_OVERRIDE;
39 virtual bool filterMaskGPU(GrTexture* src,
40 const SkRect& maskRect,
41 GrTexture** result,
42 bool canOverwriteSrc) const;
43#endif
44
reed@google.com30711b72012-12-18 19:18:39 +000045 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +000046
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +000047 SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
djsollen@google.comba28d032012-03-26 17:57:35 +000048 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
reed@google.comd729b3e2012-11-09 14:30:48 +000050protected:
reed@google.comdab9b4f2012-11-19 16:45:14 +000051 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
52 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +000053 NinePatch*) const SK_OVERRIDE;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000054
55 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
humper@google.com7c5d7b72013-03-11 20:16:28 +000056 SkIPoint* margin, SkMask::CreateMode createMode) const;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000057
reed@android.com8a1c16f2008-12-17 15:59:43 +000058private:
robertphillips@google.com49149312013-07-03 15:34:35 +000059 // To avoid unseemly allocation requests (esp. for finite platforms like
60 // handset) we limit the radius so something manageable. (as opposed to
61 // a request like 10,000)
62 static const SkScalar kMAX_RADIUS;
63 // This constant approximates the scaling done in the software path's
64 // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
65 // IMHO, it actually should be 1: we blur "less" than we should do
66 // according to the CSS and canvas specs, simply because Safari does the same.
67 // Firefox used to do the same too, until 4.0 where they fixed it. So at some
68 // point we should probably get rid of these scaling constants and rebaseline
69 // all the blur tests.
70 static const SkScalar kBLUR_SIGMA_SCALE;
71
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 SkScalar fRadius;
73 SkBlurMaskFilter::BlurStyle fBlurStyle;
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000074 uint32_t fBlurFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
76 SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
djsollen@google.com54924242012-03-29 15:18:04 +000077 virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
robertphillips@google.com49149312013-07-03 15:34:35 +000078#if SK_SUPPORT_GPU
79 SkScalar computeXformedRadius(const SkMatrix& ctm) const {
80 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
81
82 SkScalar xformedRadius = ignoreTransform ? fRadius
83 : ctm.mapRadius(fRadius);
84 return SkMinScalar(xformedRadius, kMAX_RADIUS);
85 }
86#endif
rmistry@google.comfbfcd562012-08-23 18:09:54 +000087
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 typedef SkMaskFilter INHERITED;
89};
90
robertphillips@google.com49149312013-07-03 15:34:35 +000091const SkScalar SkBlurMaskFilterImpl::kMAX_RADIUS = SkIntToScalar(128);
92const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = 0.6f;
93
reed@google.com03016a32011-08-12 14:59:59 +000094SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
95 SkBlurMaskFilter::BlurStyle style,
96 uint32_t flags) {
reed@google.come5828692012-01-11 14:07:27 +000097 // use !(radius > 0) instead of radius <= 0 to reject NaN values
rmistry@google.comfbfcd562012-08-23 18:09:54 +000098 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
reed@google.com03016a32011-08-12 14:59:59 +000099 || flags > SkBlurMaskFilter::kAll_BlurFlag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 return NULL;
reed@google.com03016a32011-08-12 14:59:59 +0000101 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000103 return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104}
105
reed@google.com03016a32011-08-12 14:59:59 +0000106///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107
reed@google.com03016a32011-08-12 14:59:59 +0000108SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius,
109 SkBlurMaskFilter::BlurStyle style,
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000110 uint32_t flags)
reed@google.com03016a32011-08-12 14:59:59 +0000111 : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112#if 0
113 fGamma = NULL;
reed@google.com03016a32011-08-12 14:59:59 +0000114 if (gammaScale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 fGamma = new U8[256];
116 if (gammaScale > 0)
117 SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
118 else
119 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
120 }
121#endif
122 SkASSERT(radius >= 0);
123 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000124 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125}
126
reed@google.com30711b72012-12-18 19:18:39 +0000127SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 return SkMask::kA8_Format;
129}
130
reed@google.com03016a32011-08-12 14:59:59 +0000131bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
reed@google.com30711b72012-12-18 19:18:39 +0000132 const SkMatrix& matrix,
133 SkIPoint* margin) const{
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000134 SkScalar radius;
reed@google.com03016a32011-08-12 14:59:59 +0000135 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000136 radius = fRadius;
reed@google.com03016a32011-08-12 14:59:59 +0000137 } else {
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000138 radius = matrix.mapRadius(fRadius);
reed@google.com03016a32011-08-12 14:59:59 +0000139 }
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000140
robertphillips@google.com49149312013-07-03 15:34:35 +0000141 radius = SkMinScalar(radius, kMAX_RADIUS);
reed@google.com03016a32011-08-12 14:59:59 +0000142 SkBlurMask::Quality blurQuality =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000143 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
reed@google.com03016a32011-08-12 14:59:59 +0000144 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145
senorblanco@chromium.org91f489a2012-11-29 17:09:27 +0000146 return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
147 blurQuality, margin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148}
149
humper@google.com7c5d7b72013-03-11 20:16:28 +0000150bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
151 const SkMatrix& matrix,
152 SkIPoint* margin, SkMask::CreateMode createMode) const{
153 SkScalar radius;
154 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
155 radius = fRadius;
156 } else {
157 radius = matrix.mapRadius(fRadius);
158 }
159
robertphillips@google.com49149312013-07-03 15:34:35 +0000160 radius = SkMinScalar(radius, kMAX_RADIUS);
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000161
humper@google.com7c5d7b72013-03-11 20:16:28 +0000162 return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle,
163 margin, createMode);
164}
165
reed@google.comd729b3e2012-11-09 14:30:48 +0000166#include "SkCanvas.h"
167
reed@google.comdab9b4f2012-11-19 16:45:14 +0000168static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
169 rects[0].roundOut(&mask->fBounds);
reed@google.comd729b3e2012-11-09 14:30:48 +0000170 mask->fRowBytes = SkAlign4(mask->fBounds.width());
171 mask->fFormat = SkMask::kA8_Format;
172 size_t size = mask->computeImageSize();
173 mask->fImage = SkMask::AllocImage(size);
174 if (NULL == mask->fImage) {
175 return false;
176 }
177 sk_bzero(mask->fImage, size);
178
179 SkBitmap bitmap;
180 bitmap.setConfig(SkBitmap::kA8_Config,
181 mask->fBounds.width(), mask->fBounds.height(),
182 mask->fRowBytes);
183 bitmap.setPixels(mask->fImage);
184
185 SkCanvas canvas(bitmap);
reed@google.comdab9b4f2012-11-19 16:45:14 +0000186 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
187 -SkIntToScalar(mask->fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000188
189 SkPaint paint;
190 paint.setAntiAlias(true);
191
reed@google.comdab9b4f2012-11-19 16:45:14 +0000192 if (1 == count) {
193 canvas.drawRect(rects[0], paint);
194 } else {
195 // todo: do I need a fast way to do this?
196 SkPath path;
197 path.addRect(rects[0]);
198 path.addRect(rects[1]);
199 path.setFillType(SkPath::kEvenOdd_FillType);
200 canvas.drawPath(path, paint);
201 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000202 return true;
203}
204
reed@google.comf276fa72012-11-21 14:14:10 +0000205static bool rect_exceeds(const SkRect& r, SkScalar v) {
206 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
207 r.width() > v || r.height() > v;
reed@google.com07784a02012-11-19 21:09:14 +0000208}
209
humper@google.com7c5d7b72013-03-11 20:16:28 +0000210#ifdef SK_IGNORE_FAST_RECT_BLUR
humper@google.come86af1f2013-03-11 21:23:36 +0000211SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" );
humper@google.com7c5d7b72013-03-11 20:16:28 +0000212#else
humper@google.come86af1f2013-03-11 21:23:36 +0000213SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
humper@google.com7c5d7b72013-03-11 20:16:28 +0000214#endif
humper@google.com7c5d7b72013-03-11 20:16:28 +0000215
reed@google.comd729b3e2012-11-09 14:30:48 +0000216SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000217SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
218 const SkMatrix& matrix,
219 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +0000220 NinePatch* patch) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000221 if (count < 1 || count > 2) {
222 return kUnimplemented_FilterReturn;
223 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000224
reed@google.com57850b92012-12-17 21:20:53 +0000225 // TODO: report correct metrics for innerstyle, where we do not grow the
226 // total bounds, but we do need an inset the size of our blur-radius
227 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
228 return kUnimplemented_FilterReturn;
229 }
230
reed@google.com07784a02012-11-19 21:09:14 +0000231 // TODO: take clipBounds into account to limit our coordinates up front
232 // for now, just skip too-large src rects (to take the old code path).
reed@google.comf276fa72012-11-21 14:14:10 +0000233 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
reed@google.com07784a02012-11-19 21:09:14 +0000234 return kUnimplemented_FilterReturn;
235 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000236
reed@google.comd729b3e2012-11-09 14:30:48 +0000237 SkIPoint margin;
238 SkMask srcM, dstM;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000239 rects[0].roundOut(&srcM.fBounds);
reed@google.comd729b3e2012-11-09 14:30:48 +0000240 srcM.fImage = NULL;
241 srcM.fFormat = SkMask::kA8_Format;
242 srcM.fRowBytes = 0;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000243
humper@google.com7c5d7b72013-03-11 20:16:28 +0000244 bool filterResult = false;
245 if (count == 1 && c_analyticBlurNinepatch) {
246 // special case for fast rect blur
247 // don't actually do the blur the first time, just compute the correct size
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000248 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000249 SkMask::kJustComputeBounds_CreateMode);
250 } else {
251 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
252 }
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000253
humper@google.com7c5d7b72013-03-11 20:16:28 +0000254 if (!filterResult) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000255 return kFalse_FilterReturn;
256 }
257
258 /*
259 * smallR is the smallest version of 'rect' that will still guarantee that
260 * we get the same blur results on all edges, plus 1 center row/col that is
261 * representative of the extendible/stretchable edges of the ninepatch.
262 * Since our actual edge may be fractional we inset 1 more to be sure we
263 * don't miss any interior blur.
264 * x is an added pixel of blur, and { and } are the (fractional) edge
265 * pixels from the original rect.
266 *
267 * x x { x x .... x x } x x
268 *
269 * Thus, in this case, we inset by a total of 5 (on each side) beginning
270 * with our outer-rect (dstM.fBounds)
271 */
reed@google.comdab9b4f2012-11-19 16:45:14 +0000272 SkRect smallR[2];
273 SkIPoint center;
274
275 // +2 is from +1 for each edge (to account for possible fractional edges
276 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
277 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
278 SkIRect innerIR;
279
280 if (1 == count) {
281 innerIR = srcM.fBounds;
282 center.set(smallW, smallH);
283 } else {
284 SkASSERT(2 == count);
285 rects[1].roundIn(&innerIR);
286 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
287 smallH + (innerIR.top() - srcM.fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000288 }
289
reed@google.comdab9b4f2012-11-19 16:45:14 +0000290 // +1 so we get a clean, stretchable, center row/col
291 smallW += 1;
292 smallH += 1;
293
294 // we want the inset amounts to be integral, so we don't change any
295 // fractional phase on the fRight or fBottom of our smallR.
296 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
297 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
298 if (dx < 0 || dy < 0) {
299 // we're too small, relative to our blur, to break into nine-patch,
300 // so we ask to have our normal filterMask() be called.
301 return kUnimplemented_FilterReturn;
302 }
303
304 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
305 SkASSERT(!smallR[0].isEmpty());
306 if (2 == count) {
307 smallR[1].set(rects[1].left(), rects[1].top(),
308 rects[1].right() - dx, rects[1].bottom() - dy);
309 SkASSERT(!smallR[1].isEmpty());
310 }
311
humper@google.com7c5d7b72013-03-11 20:16:28 +0000312 if (count > 1 || !c_analyticBlurNinepatch) {
313 if (!drawRectsIntoMask(smallR, count, &srcM)) {
314 return kFalse_FilterReturn;
315 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000316
humper@google.com7c5d7b72013-03-11 20:16:28 +0000317 SkAutoMaskFreeImage amf(srcM.fImage);
reed@google.comd198a5b2012-11-27 15:18:04 +0000318
humper@google.com7c5d7b72013-03-11 20:16:28 +0000319 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
320 return kFalse_FilterReturn;
321 }
322 } else {
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000323 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000324 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
325 return kFalse_FilterReturn;
326 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000327 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000328 patch->fMask.fBounds.offsetTo(0, 0);
329 patch->fOuterRect = dstM.fBounds;
330 patch->fCenter = center;
reed@google.comd729b3e2012-11-09 14:30:48 +0000331 return kTrue_FilterReturn;
332}
333
reed@google.com30711b72012-12-18 19:18:39 +0000334void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
335 SkRect* dst) const {
reed@google.com9efd9a02012-01-30 15:41:43 +0000336 dst->set(src.fLeft - fRadius, src.fTop - fRadius,
337 src.fRight + fRadius, src.fBottom + fRadius);
338}
339
reed@google.com03016a32011-08-12 14:59:59 +0000340SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
341 : SkMaskFilter(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 fRadius = buffer.readScalar();
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000343 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
344 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345 SkASSERT(fRadius >= 0);
346 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
347}
348
djsollen@google.com54924242012-03-29 15:18:04 +0000349void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 this->INHERITED::flatten(buffer);
351 buffer.writeScalar(fRadius);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000352 buffer.writeInt(fBlurStyle);
353 buffer.writeUInt(fBlurFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354}
355
robertphillips@google.com49149312013-07-03 15:34:35 +0000356#if SK_SUPPORT_GPU
reed@google.com2b75f422011-07-07 13:43:38 +0000357
robertphillips@google.com49149312013-07-03 15:34:35 +0000358bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
359 const SkIRect& clipBounds,
360 const SkMatrix& ctm,
361 SkRect* maskRect) const {
362 SkScalar xformedRadius = this->computeXformedRadius(ctm);
363 if (xformedRadius <= 0) {
364 return false;
reed@google.com2b75f422011-07-07 13:43:38 +0000365 }
robertphillips@google.com49149312013-07-03 15:34:35 +0000366
367 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
368 static const SkScalar kMIN_GPU_BLUR_RADIUS = SkIntToScalar(32);
369
370 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
371 srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
372 xformedRadius <= kMIN_GPU_BLUR_RADIUS) {
373 // We prefer to blur small rect with small radius via CPU.
374 return false;
375 }
376
377 if (NULL == maskRect) {
378 // don't need to compute maskRect
379 return true;
380 }
381
382 float sigma3 = 3 * SkScalarToFloat(xformedRadius) * kBLUR_SIGMA_SCALE;
383
384 SkRect clipRect = SkRect::MakeFromIRect(clipBounds);
385 SkRect srcRect(srcBounds);
386
387 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
388 srcRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
389 clipRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
390 srcRect.intersect(clipRect);
391 *maskRect = srcRect;
392 return true;
reed@google.com2b75f422011-07-07 13:43:38 +0000393}
394
robertphillips@google.com49149312013-07-03 15:34:35 +0000395bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
396 const SkRect& maskRect,
397 GrTexture** result,
398 bool canOverwriteSrc) const {
399 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
400
401 GrContext* context = src->getContext();
402
403 GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
404
405 SkScalar xformedRadius = this->computeXformedRadius(context->getMatrix());
406 SkASSERT(xformedRadius > 0);
407
408 float sigma = SkScalarToFloat(xformedRadius) * kBLUR_SIGMA_SCALE;
409
410 // If we're doing a normal blur, we can clobber the pathTexture in the
411 // gaussianBlur. Otherwise, we need to save it for later compositing.
412 bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
413 *result = context->gaussianBlur(src, isNormalBlur && canOverwriteSrc,
414 clipRect, sigma, sigma);
415 if (NULL == result) {
416 return false;
417 }
418
419 if (!isNormalBlur) {
420 context->setIdentityMatrix();
421 GrPaint paint;
422 SkMatrix matrix;
423 matrix.setIDiv(src->width(), src->height());
424 // Blend pathTexture over blurTexture.
425 GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget());
426 paint.colorStage(0)->setEffect(
427 GrSimpleTextureEffect::Create(src, matrix))->unref();
428 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
429 // inner: dst = dst * src
430 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
431 } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) {
432 // solid: dst = src + dst - src * dst
433 // = (1 - dst) * src + 1 * dst
434 paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
435 } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
436 // outer: dst = dst * (1 - src)
437 // = 0 * src + (1 - src) * dst
438 paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
439 }
440 context->drawRect(paint, clipRect);
441 }
442
443 return true;
444}
445
446#endif // SK_SUPPORT_GPU
447
448
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000449#ifdef SK_DEVELOPER
450void SkBlurMaskFilterImpl::toString(SkString* str) const {
451 str->append("SkBlurMaskFilterImpl: (");
452
453 str->append("radius: ");
454 str->appendScalar(fRadius);
455 str->append(" ");
456
457 static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = {
458 "normal", "solid", "outer", "inner"
459 };
460
461 str->appendf("style: %s ", gStyleName[fBlurStyle]);
462 str->append("flags: (");
463 if (fBlurFlags) {
464 bool needSeparator = false;
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +0000465 SkAddFlagToString(str,
466 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000467 "IgnoreXform", &needSeparator);
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +0000468 SkAddFlagToString(str,
469 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000470 "HighQuality", &needSeparator);
471 } else {
472 str->append("None");
473 }
474 str->append("))");
475}
476#endif
477
caryclark@google.comd26147a2011-12-15 14:16:43 +0000478SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
479 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
480SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END