blob: a5cdc4f703990b2e7f3ab2cbbb5eeb494e5494ef [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"
robertphillips@google.com736dd032013-07-15 15:06:54 +000011#include "SkGpuBlurUtils.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000012#include "SkReadBuffer.h"
13#include "SkWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkMaskFilter.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000015#include "SkRRect.h"
humper@google.com7c5d7b72013-03-11 20:16:28 +000016#include "SkRTConf.h"
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +000017#include "SkStringUtils.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000018#include "SkStrokeRec.h"
19
20#if SK_SUPPORT_GPU
21#include "GrContext.h"
22#include "GrTexture.h"
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +000023#include "GrEffect.h"
24#include "gl/GrGLEffect.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000025#include "effects/GrSimpleTextureEffect.h"
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +000026#include "GrTBackendEffectFactory.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000027#include "SkGrPixelRef.h"
28#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
30class SkBlurMaskFilterImpl : public SkMaskFilter {
31public:
robertphillips@google.com7ce661d2013-08-27 16:14:03 +000032 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurMaskFilter::BlurStyle, uint32_t flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000033
34 // overrides from SkMaskFilter
reed@google.com30711b72012-12-18 19:18:39 +000035 virtual SkMask::Format getFormat() const SK_OVERRIDE;
reed@google.com03016a32011-08-12 14:59:59 +000036 virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
reed@google.com30711b72012-12-18 19:18:39 +000037 SkIPoint* margin) const SK_OVERRIDE;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000038
robertphillips@google.com49149312013-07-03 15:34:35 +000039#if SK_SUPPORT_GPU
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +000040 virtual bool canFilterMaskGPU(const SkRect& devBounds,
robertphillips@google.com49149312013-07-03 15:34:35 +000041 const SkIRect& clipBounds,
42 const SkMatrix& ctm,
43 SkRect* maskRect) const SK_OVERRIDE;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +000044 virtual bool directFilterMaskGPU(GrContext* context,
45 GrPaint* grp,
46 const SkStrokeRec& strokeRec,
47 const SkPath& path) const SK_OVERRIDE;
commit-bot@chromium.org82139702014-03-10 22:53:20 +000048 virtual bool directFilterRRectMaskGPU(GrContext* context,
49 GrPaint* grp,
50 const SkStrokeRec& strokeRec,
51 const SkRRect& rrect) const SK_OVERRIDE;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +000052
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +000053 virtual bool filterMaskGPU(GrTexture* src,
commit-bot@chromium.org41bf9302014-01-08 22:25:53 +000054 const SkMatrix& ctm,
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +000055 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +000056 GrTexture** result,
commit-bot@chromium.org41bf9302014-01-08 22:25:53 +000057 bool canOverwriteSrc) const SK_OVERRIDE;
robertphillips@google.com49149312013-07-03 15:34:35 +000058#endif
59
reed@google.com30711b72012-12-18 19:18:39 +000060 virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +000061
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +000062 SK_TO_STRING_OVERRIDE()
djsollen@google.comba28d032012-03-26 17:57:35 +000063 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
reed@android.com8a1c16f2008-12-17 15:59:43 +000064
reed@google.comd729b3e2012-11-09 14:30:48 +000065protected:
reed@google.comdab9b4f2012-11-19 16:45:14 +000066 virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
67 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +000068 NinePatch*) const SK_OVERRIDE;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000069
scroggo@google.coma8e33a92013-11-08 18:02:53 +000070 virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
71 const SkIRect& clipBounds,
72 NinePatch*) const SK_OVERRIDE;
73
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000074 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
humper@google.com7c5d7b72013-03-11 20:16:28 +000075 SkIPoint* margin, SkMask::CreateMode createMode) const;
commit-bot@chromium.orga4771542014-03-10 21:42:06 +000076 bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
77 SkIPoint* margin, SkMask::CreateMode createMode) const;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000078
reed@android.com8a1c16f2008-12-17 15:59:43 +000079private:
robertphillips@google.com49149312013-07-03 15:34:35 +000080 // To avoid unseemly allocation requests (esp. for finite platforms like
81 // handset) we limit the radius so something manageable. (as opposed to
82 // a request like 10,000)
robertphillips@google.com7ce661d2013-08-27 16:14:03 +000083 static const SkScalar kMAX_BLUR_SIGMA;
robertphillips@google.com49149312013-07-03 15:34:35 +000084
robertphillips@google.com7ce661d2013-08-27 16:14:03 +000085 SkScalar fSigma;
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 SkBlurMaskFilter::BlurStyle fBlurStyle;
senorblanco@chromium.org038aff62010-12-06 23:45:58 +000087 uint32_t fBlurFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000089 SkBlurMaskFilterImpl(SkReadBuffer&);
90 virtual void flatten(SkWriteBuffer&) const SK_OVERRIDE;
robertphillips@google.com7ce661d2013-08-27 16:14:03 +000091
92 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
robertphillips@google.com49149312013-07-03 15:34:35 +000093 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
94
robertphillips@google.com7ce661d2013-08-27 16:14:03 +000095 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma);
96 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
robertphillips@google.com49149312013-07-03 15:34:35 +000097 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000098
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 typedef SkMaskFilter INHERITED;
100};
101
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000102const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
robertphillips@google.com49149312013-07-03 15:34:35 +0000103
reed@google.com03016a32011-08-12 14:59:59 +0000104SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
105 SkBlurMaskFilter::BlurStyle style,
106 uint32_t flags) {
reed@google.come5828692012-01-11 14:07:27 +0000107 // use !(radius > 0) instead of radius <= 0 to reject NaN values
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000108 if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
reed@google.com03016a32011-08-12 14:59:59 +0000109 || flags > SkBlurMaskFilter::kAll_BlurFlag) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 return NULL;
reed@google.com03016a32011-08-12 14:59:59 +0000111 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000113 SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
114
115 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
116}
117
118SkMaskFilter* SkBlurMaskFilter::Create(SkBlurMaskFilter::BlurStyle style,
119 SkScalar sigma,
120 uint32_t flags) {
121 // use !(sigma > 0) instead of sigma <= 0 to reject NaN values
122 if (!(sigma > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
123 || flags > SkBlurMaskFilter::kAll_BlurFlag) {
124 return NULL;
125 }
126
127 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128}
129
reed@google.com03016a32011-08-12 14:59:59 +0000130///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000132SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma,
reed@google.com03016a32011-08-12 14:59:59 +0000133 SkBlurMaskFilter::BlurStyle style,
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000134 uint32_t flags)
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000135 : fSigma(sigma), fBlurStyle(style), fBlurFlags(flags) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136#if 0
137 fGamma = NULL;
reed@google.com03016a32011-08-12 14:59:59 +0000138 if (gammaScale) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 fGamma = new U8[256];
140 if (gammaScale > 0)
141 SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
142 else
143 SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
144 }
145#endif
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000146 SkASSERT(fSigma >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147 SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000148 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149}
150
reed@google.com30711b72012-12-18 19:18:39 +0000151SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152 return SkMask::kA8_Format;
153}
154
reed@google.com03016a32011-08-12 14:59:59 +0000155bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
reed@google.com30711b72012-12-18 19:18:39 +0000156 const SkMatrix& matrix,
157 SkIPoint* margin) const{
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000158 SkScalar sigma = this->computeXformedSigma(matrix);
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000159
reed@google.com03016a32011-08-12 14:59:59 +0000160 SkBlurMask::Quality blurQuality =
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000161 (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
reed@google.com03016a32011-08-12 14:59:59 +0000162 SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000164 return SkBlurMask::BoxBlur(dst, src, sigma, (SkBlurMask::Style)fBlurStyle,
165 blurQuality, margin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166}
167
humper@google.com7c5d7b72013-03-11 20:16:28 +0000168bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
169 const SkMatrix& matrix,
170 SkIPoint* margin, SkMask::CreateMode createMode) const{
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000171 SkScalar sigma = computeXformedSigma(matrix);
humper@google.com7c5d7b72013-03-11 20:16:28 +0000172
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000173 return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000174 margin, createMode);
175}
176
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000177bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
178 const SkMatrix& matrix,
179 SkIPoint* margin, SkMask::CreateMode createMode) const{
180 SkScalar sigma = computeXformedSigma(matrix);
181
182 return SkBlurMask::BlurRRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle,
183 margin, createMode);
184}
185
reed@google.comd729b3e2012-11-09 14:30:48 +0000186#include "SkCanvas.h"
187
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000188static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
189 SkASSERT(mask != NULL);
190
191 bounds.roundOut(&mask->fBounds);
reed@google.comd729b3e2012-11-09 14:30:48 +0000192 mask->fRowBytes = SkAlign4(mask->fBounds.width());
193 mask->fFormat = SkMask::kA8_Format;
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000194 const size_t size = mask->computeImageSize();
reed@google.comd729b3e2012-11-09 14:30:48 +0000195 mask->fImage = SkMask::AllocImage(size);
196 if (NULL == mask->fImage) {
197 return false;
198 }
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000199
200 // FIXME: use sk_calloc in AllocImage?
reed@google.comd729b3e2012-11-09 14:30:48 +0000201 sk_bzero(mask->fImage, size);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000202 return true;
203}
204
205static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
206 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
207 return false;
208 }
209
210 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
211 // clean way to share more code?
212 SkBitmap bitmap;
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000213 bitmap.installMaskPixels(*mask);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000214
215 SkCanvas canvas(bitmap);
216 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
217 -SkIntToScalar(mask->fBounds.top()));
218
219 SkPaint paint;
220 paint.setAntiAlias(true);
221 canvas.drawRRect(rrect, paint);
222 return true;
223}
224
225static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
226 if (!prepare_to_draw_into_mask(rects[0], mask)) {
227 return false;
228 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000229
230 SkBitmap bitmap;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +0000231 bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
232 mask->fBounds.height(),
233 kAlpha_8_SkColorType,
234 kPremul_SkAlphaType),
235 mask->fImage, mask->fRowBytes, NULL, NULL);
reed@google.comd729b3e2012-11-09 14:30:48 +0000236
237 SkCanvas canvas(bitmap);
reed@google.comdab9b4f2012-11-19 16:45:14 +0000238 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
239 -SkIntToScalar(mask->fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000240
241 SkPaint paint;
242 paint.setAntiAlias(true);
243
reed@google.comdab9b4f2012-11-19 16:45:14 +0000244 if (1 == count) {
245 canvas.drawRect(rects[0], paint);
246 } else {
247 // todo: do I need a fast way to do this?
248 SkPath path;
249 path.addRect(rects[0]);
250 path.addRect(rects[1]);
251 path.setFillType(SkPath::kEvenOdd_FillType);
252 canvas.drawPath(path, paint);
253 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000254 return true;
255}
256
reed@google.comf276fa72012-11-21 14:14:10 +0000257static bool rect_exceeds(const SkRect& r, SkScalar v) {
258 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
259 r.width() > v || r.height() > v;
reed@google.com07784a02012-11-19 21:09:14 +0000260}
261
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000262#ifdef SK_IGNORE_FAST_RRECT_BLUR
263SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticRRect", false, "Use the faster analytic blur approach for ninepatch rects" );
264#else
265SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticRRect", true, "Use the faster analytic blur approach for ninepatch round rects" );
266#endif
267
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000268SkMaskFilter::FilterReturn
269SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
270 const SkIRect& clipBounds,
271 NinePatch* patch) const {
272 SkASSERT(patch != NULL);
273 switch (rrect.getType()) {
274 case SkRRect::kUnknown_Type:
275 // Unknown should never be returned.
276 SkASSERT(false);
277 // Fall through.
278 case SkRRect::kEmpty_Type:
279 // Nothing to draw.
280 return kFalse_FilterReturn;
281
282 case SkRRect::kRect_Type:
283 // We should have caught this earlier.
284 SkASSERT(false);
285 // Fall through.
286 case SkRRect::kOval_Type:
287 // The nine patch special case does not handle ovals, and we
288 // already have code for rectangles.
289 return kUnimplemented_FilterReturn;
290
291 case SkRRect::kSimple_Type:
commit-bot@chromium.orgbab3fc42014-03-17 19:47:40 +0000292 // Fall through.
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000293 case SkRRect::kComplex_Type:
commit-bot@chromium.orgbab3fc42014-03-17 19:47:40 +0000294 // These can take advantage of this fast path.
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000295 break;
296 }
297
298 // TODO: report correct metrics for innerstyle, where we do not grow the
299 // total bounds, but we do need an inset the size of our blur-radius
300 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
301 return kUnimplemented_FilterReturn;
302 }
303
304 // TODO: take clipBounds into account to limit our coordinates up front
305 // for now, just skip too-large src rects (to take the old code path).
306 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
307 return kUnimplemented_FilterReturn;
308 }
309
310 SkIPoint margin;
311 SkMask srcM, dstM;
312 rrect.rect().roundOut(&srcM.fBounds);
313 srcM.fImage = NULL;
314 srcM.fFormat = SkMask::kA8_Format;
315 srcM.fRowBytes = 0;
316
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000317 bool filterResult = false;
318 if (c_analyticBlurRRect) {
319 // special case for fast round rect blur
320 // don't actually do the blur the first time, just compute the correct size
321 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
322 SkMask::kJustComputeBounds_CreateMode);
323 }
324
325 if (!filterResult) {
326 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
327 }
328
329 if (!filterResult) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000330 return kFalse_FilterReturn;
331 }
332
333 // Now figure out the appropriate width and height of the smaller round rectangle
334 // to stretch. It will take into account the larger radius per side as well as double
335 // the margin, to account for inner and outer blur.
336 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
337 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
338 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
339 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
340
341 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
342 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
343
344 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
345 // any fractional space on either side plus 1 for the part to stretch.
346 const SkScalar stretchSize = SkIntToScalar(3);
347
348 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
349 if (totalSmallWidth >= rrect.rect().width()) {
350 // There is no valid piece to stretch.
351 return kUnimplemented_FilterReturn;
352 }
353
354 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
355 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
356
357 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
358 if (totalSmallHeight >= rrect.rect().height()) {
359 // There is no valid piece to stretch.
360 return kUnimplemented_FilterReturn;
361 }
362
363 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
364
365 SkRRect smallRR;
366 SkVector radii[4];
367 radii[SkRRect::kUpperLeft_Corner] = UL;
368 radii[SkRRect::kUpperRight_Corner] = UR;
369 radii[SkRRect::kLowerRight_Corner] = LR;
370 radii[SkRRect::kLowerLeft_Corner] = LL;
371 smallRR.setRectRadii(smallR, radii);
372
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000373 bool analyticBlurWorked = false;
374 if (c_analyticBlurRRect) {
375 analyticBlurWorked =
376 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
377 SkMask::kComputeBoundsAndRenderImage_CreateMode);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000378 }
379
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000380 if (!analyticBlurWorked) {
381 if (!draw_rrect_into_mask(smallRR, &srcM)) {
382 return kFalse_FilterReturn;
383 }
robertphillips@google.comad993582013-11-11 18:45:18 +0000384
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000385 SkAutoMaskFreeImage amf(srcM.fImage);
386
387 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
388 return kFalse_FilterReturn;
389 }
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000390 }
391
392 patch->fMask.fBounds.offsetTo(0, 0);
393 patch->fOuterRect = dstM.fBounds;
394 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
395 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
396 return kTrue_FilterReturn;
397}
398
humper@google.come86af1f2013-03-11 21:23:36 +0000399SK_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 +0000400
reed@google.comd729b3e2012-11-09 14:30:48 +0000401SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000402SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
403 const SkMatrix& matrix,
404 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +0000405 NinePatch* patch) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000406 if (count < 1 || count > 2) {
407 return kUnimplemented_FilterReturn;
408 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000409
reed@google.com57850b92012-12-17 21:20:53 +0000410 // TODO: report correct metrics for innerstyle, where we do not grow the
411 // total bounds, but we do need an inset the size of our blur-radius
robertphillips@google.com96ac2f62013-11-07 22:25:21 +0000412 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle ||
413 SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
reed@google.com57850b92012-12-17 21:20:53 +0000414 return kUnimplemented_FilterReturn;
415 }
416
reed@google.com07784a02012-11-19 21:09:14 +0000417 // TODO: take clipBounds into account to limit our coordinates up front
418 // for now, just skip too-large src rects (to take the old code path).
reed@google.comf276fa72012-11-21 14:14:10 +0000419 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
reed@google.com07784a02012-11-19 21:09:14 +0000420 return kUnimplemented_FilterReturn;
421 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000422
reed@google.comd729b3e2012-11-09 14:30:48 +0000423 SkIPoint margin;
424 SkMask srcM, dstM;
reed@google.comdab9b4f2012-11-19 16:45:14 +0000425 rects[0].roundOut(&srcM.fBounds);
reed@google.comd729b3e2012-11-09 14:30:48 +0000426 srcM.fImage = NULL;
427 srcM.fFormat = SkMask::kA8_Format;
428 srcM.fRowBytes = 0;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000429
humper@google.com7c5d7b72013-03-11 20:16:28 +0000430 bool filterResult = false;
431 if (count == 1 && c_analyticBlurNinepatch) {
432 // special case for fast rect blur
433 // don't actually do the blur the first time, just compute the correct size
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000434 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000435 SkMask::kJustComputeBounds_CreateMode);
436 } else {
437 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
438 }
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000439
humper@google.com7c5d7b72013-03-11 20:16:28 +0000440 if (!filterResult) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000441 return kFalse_FilterReturn;
442 }
443
444 /*
445 * smallR is the smallest version of 'rect' that will still guarantee that
446 * we get the same blur results on all edges, plus 1 center row/col that is
447 * representative of the extendible/stretchable edges of the ninepatch.
448 * Since our actual edge may be fractional we inset 1 more to be sure we
449 * don't miss any interior blur.
450 * x is an added pixel of blur, and { and } are the (fractional) edge
451 * pixels from the original rect.
452 *
453 * x x { x x .... x x } x x
454 *
455 * Thus, in this case, we inset by a total of 5 (on each side) beginning
456 * with our outer-rect (dstM.fBounds)
457 */
reed@google.comdab9b4f2012-11-19 16:45:14 +0000458 SkRect smallR[2];
459 SkIPoint center;
460
461 // +2 is from +1 for each edge (to account for possible fractional edges
462 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
463 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
464 SkIRect innerIR;
465
466 if (1 == count) {
467 innerIR = srcM.fBounds;
468 center.set(smallW, smallH);
469 } else {
470 SkASSERT(2 == count);
471 rects[1].roundIn(&innerIR);
472 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
473 smallH + (innerIR.top() - srcM.fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000474 }
475
reed@google.comdab9b4f2012-11-19 16:45:14 +0000476 // +1 so we get a clean, stretchable, center row/col
477 smallW += 1;
478 smallH += 1;
479
480 // we want the inset amounts to be integral, so we don't change any
481 // fractional phase on the fRight or fBottom of our smallR.
482 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
483 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
484 if (dx < 0 || dy < 0) {
485 // we're too small, relative to our blur, to break into nine-patch,
486 // so we ask to have our normal filterMask() be called.
487 return kUnimplemented_FilterReturn;
488 }
489
490 smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
robertphillips@google.com112a23e2013-09-03 17:17:43 +0000491 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
492 return kUnimplemented_FilterReturn;
493 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000494 if (2 == count) {
495 smallR[1].set(rects[1].left(), rects[1].top(),
496 rects[1].right() - dx, rects[1].bottom() - dy);
497 SkASSERT(!smallR[1].isEmpty());
498 }
499
humper@google.com7c5d7b72013-03-11 20:16:28 +0000500 if (count > 1 || !c_analyticBlurNinepatch) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000501 if (!draw_rects_into_mask(smallR, count, &srcM)) {
humper@google.com7c5d7b72013-03-11 20:16:28 +0000502 return kFalse_FilterReturn;
503 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000504
humper@google.com7c5d7b72013-03-11 20:16:28 +0000505 SkAutoMaskFreeImage amf(srcM.fImage);
reed@google.comd198a5b2012-11-27 15:18:04 +0000506
humper@google.com7c5d7b72013-03-11 20:16:28 +0000507 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
508 return kFalse_FilterReturn;
509 }
510 } else {
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000511 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000512 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
513 return kFalse_FilterReturn;
514 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000515 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000516 patch->fMask.fBounds.offsetTo(0, 0);
517 patch->fOuterRect = dstM.fBounds;
518 patch->fCenter = center;
reed@google.comd729b3e2012-11-09 14:30:48 +0000519 return kTrue_FilterReturn;
520}
521
reed@google.com30711b72012-12-18 19:18:39 +0000522void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
523 SkRect* dst) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000524 SkScalar pad = 3.0f * fSigma;
robertphillips@google.com17ad2bd2013-07-30 12:15:19 +0000525
526 dst->set(src.fLeft - pad, src.fTop - pad,
527 src.fRight + pad, src.fBottom + pad);
reed@google.com9efd9a02012-01-30 15:41:43 +0000528}
529
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000530SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkReadBuffer& buffer)
reed@google.com03016a32011-08-12 14:59:59 +0000531 : SkMaskFilter(buffer) {
commit-bot@chromium.orgfed2ab62014-01-23 15:16:05 +0000532 fSigma = buffer.readScalar();
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000533 fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
534 fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000535 SkASSERT(fSigma >= 0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
537}
538
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000539void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 this->INHERITED::flatten(buffer);
robertphillips@google.com11e05552013-12-03 19:46:58 +0000541 buffer.writeScalar(fSigma);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000542 buffer.writeInt(fBlurStyle);
543 buffer.writeUInt(fBlurFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544}
545
robertphillips@google.com49149312013-07-03 15:34:35 +0000546#if SK_SUPPORT_GPU
reed@google.com2b75f422011-07-07 13:43:38 +0000547
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000548class GrGLRectBlurEffect;
549
550class GrRectBlurEffect : public GrEffect {
551public:
552 virtual ~GrRectBlurEffect();
553
554 static const char* Name() { return "RectBlur"; }
555
556 typedef GrGLRectBlurEffect GLEffect;
557
558 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
559 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
560
561 /**
562 * Create a simple filter effect with custom bicubic coefficients.
563 */
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000564 static GrEffectRef* Create(GrContext *context, const SkRect& rect,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000565 float sigma) {
commit-bot@chromium.orga29105a2014-01-31 14:00:58 +0000566 GrTexture *horizontalScanline = NULL, *verticalScanline = NULL;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000567 bool createdScanlines = CreateScanlineTextures(context, sigma,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000568 SkScalarCeilToInt(rect.width()),
569 SkScalarCeilToInt(rect.height()),
570 &horizontalScanline, &verticalScanline);
commit-bot@chromium.orga29105a2014-01-31 14:00:58 +0000571 SkAutoTUnref<GrTexture> hunref(horizontalScanline), vunref(verticalScanline);
572 if (!createdScanlines) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000573 return NULL;
574 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000575 AutoEffectUnref effect(SkNEW_ARGS(GrRectBlurEffect, (rect, sigma,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000576 horizontalScanline, verticalScanline)));
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000577 return CreateEffectRef(effect);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000578 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000579
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000580 unsigned int getWidth() const { return fWidth; }
581 unsigned int getHeight() const { return fHeight; }
582 float getSigma() const { return fSigma; }
583 const GrCoordTransform& getTransform() const { return fTransform; }
584
585private:
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000586 GrRectBlurEffect(const SkRect& rect, float sigma,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000587 GrTexture *horizontal_scanline, GrTexture *vertical_scanline);
588 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000589
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000590 static bool CreateScanlineTextures(GrContext *context, float sigma,
591 unsigned int width, unsigned int height,
592 GrTexture **horizontalScanline,
593 GrTexture **verticalScanline);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000594
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000595 unsigned int fWidth, fHeight;
596 float fSigma;
597 GrTextureAccess fHorizontalScanlineAccess;
598 GrTextureAccess fVerticalScanlineAccess;
599 GrCoordTransform fTransform;
600
601 GR_DECLARE_EFFECT_TEST;
602
603 typedef GrEffect INHERITED;
604};
605
606class GrGLRectBlurEffect : public GrGLEffect {
607public:
608 GrGLRectBlurEffect(const GrBackendEffectFactory& factory,
609 const GrDrawEffect&);
610 virtual void emitCode(GrGLShaderBuilder*,
611 const GrDrawEffect&,
612 EffectKey,
613 const char* outputColor,
614 const char* inputColor,
615 const TransformedCoordsArray&,
616 const TextureSamplerArray&) SK_OVERRIDE;
617
618 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
619
620private:
621 typedef GrGLUniformManager::UniformHandle UniformHandle;
622
623 UniformHandle fWidthUni;
624 UniformHandle fHeightUni;
625
626 typedef GrGLEffect INHERITED;
627};
628
629GrGLRectBlurEffect::GrGLRectBlurEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
630 : INHERITED(factory) {
631}
632
633void GrGLRectBlurEffect::emitCode(GrGLShaderBuilder* builder,
634 const GrDrawEffect&,
635 EffectKey key,
636 const char* outputColor,
637 const char* inputColor,
638 const TransformedCoordsArray& coords,
639 const TextureSamplerArray& samplers) {
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000640
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000641 SkString texture_coords = builder->ensureFSCoords2D(coords, 0);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000642
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000643 if (inputColor) {
644 builder->fsCodeAppendf("\tvec4 src=%s;\n", inputColor);
645 } else {
646 builder->fsCodeAppendf("\tvec4 src=vec4(1)\n;");
647 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000648
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000649 builder->fsCodeAppendf("\tvec4 horiz = ");
650 builder->fsAppendTextureLookup( samplers[0], texture_coords.c_str() );
651 builder->fsCodeAppendf(";\n");
652 builder->fsCodeAppendf("\tvec4 vert = ");
653 builder->fsAppendTextureLookup( samplers[1], texture_coords.c_str() );
654 builder->fsCodeAppendf(";\n");
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000655
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000656 builder->fsCodeAppendf("\tfloat final = (horiz*vert).r;\n");
657 builder->fsCodeAppendf("\t%s = final*src;\n", outputColor);
658}
659
660void GrGLRectBlurEffect::setData(const GrGLUniformManager& uman,
661 const GrDrawEffect& drawEffect) {
662}
663
664bool GrRectBlurEffect::CreateScanlineTextures(GrContext *context, float sigma,
665 unsigned int width, unsigned int height,
666 GrTexture **horizontalScanline,
667 GrTexture **verticalScanline) {
668 GrTextureParams params;
669 GrTextureDesc texDesc;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000670
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000671 unsigned int profile_size = SkScalarFloorToInt(6*sigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000672
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000673 texDesc.fWidth = width;
674 texDesc.fHeight = 1;
675 texDesc.fConfig = kAlpha_8_GrPixelConfig;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000676
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000677 static const GrCacheID::Domain gBlurProfileDomain = GrCacheID::GenerateDomain();
678 GrCacheID::Key key;
679 memset(&key, 0, sizeof(key));
680 key.fData32[0] = profile_size;
681 key.fData32[1] = width;
682 key.fData32[2] = 1;
683 GrCacheID horizontalCacheID(gBlurProfileDomain, key);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000684
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000685 uint8_t *profile = NULL;
commit-bot@chromium.orgcc787fa2014-01-31 01:00:49 +0000686 SkAutoTDeleteArray<uint8_t> ada(NULL);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000687
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000688 *horizontalScanline = context->findAndRefTexture(texDesc, horizontalCacheID, &params);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000689
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000690 if (NULL == *horizontalScanline) {
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000691
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000692 SkBlurMask::ComputeBlurProfile(sigma, &profile);
commit-bot@chromium.orgcc787fa2014-01-31 01:00:49 +0000693 ada.reset(profile);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000694
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000695 SkAutoTMalloc<uint8_t> horizontalPixels(width);
696 SkBlurMask::ComputeBlurredScanline(horizontalPixels, profile, width, sigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000697
698 *horizontalScanline = context->createTexture(&params, texDesc, horizontalCacheID,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000699 horizontalPixels, 0);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000700
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000701 if (NULL == *horizontalScanline) {
702 return false;
703 }
704 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000705
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000706 texDesc.fWidth = 1;
707 texDesc.fHeight = height;
708 key.fData32[1] = 1;
709 key.fData32[2] = height;
710 GrCacheID verticalCacheID(gBlurProfileDomain, key);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000711
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000712 *verticalScanline = context->findAndRefTexture(texDesc, verticalCacheID, &params);
713 if (NULL == *verticalScanline) {
714 if (NULL == profile) {
715 SkBlurMask::ComputeBlurProfile(sigma, &profile);
commit-bot@chromium.orgcc787fa2014-01-31 01:00:49 +0000716 ada.reset(profile);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000717 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000718
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000719 SkAutoTMalloc<uint8_t> verticalPixels(height);
720 SkBlurMask::ComputeBlurredScanline(verticalPixels, profile, height, sigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000721
722 *verticalScanline = context->createTexture(&params, texDesc, verticalCacheID,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000723 verticalPixels, 0);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000724
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000725 if (NULL == *verticalScanline) {
commit-bot@chromium.org85b45af2014-01-31 14:47:57 +0000726 SkSafeSetNull(*horizontalScanline);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000727 return false;
728 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000729
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000730 }
731 return true;
732}
733
734GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
735 GrTexture *horizontal_scanline, GrTexture *vertical_scanline)
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000736 : INHERITED(),
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000737 fWidth(horizontal_scanline->width()),
738 fHeight(vertical_scanline->width()),
739 fSigma(sigma),
740 fHorizontalScanlineAccess(horizontal_scanline),
741 fVerticalScanlineAccess(vertical_scanline) {
742 SkMatrix mat;
743 mat.setRectToRect(rect, SkRect::MakeWH(1,1), SkMatrix::kFill_ScaleToFit);
744 fTransform.reset(kLocal_GrCoordSet, mat);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000745 this->addTextureAccess(&fHorizontalScanlineAccess);
746 this->addTextureAccess(&fVerticalScanlineAccess);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000747 this->addCoordTransform(&fTransform);
748}
749
750GrRectBlurEffect::~GrRectBlurEffect() {
751}
752
753const GrBackendEffectFactory& GrRectBlurEffect::getFactory() const {
754 return GrTBackendEffectFactory<GrRectBlurEffect>::getInstance();
755}
756
757bool GrRectBlurEffect::onIsEqual(const GrEffect& sBase) const {
758 const GrRectBlurEffect& s = CastEffect<GrRectBlurEffect>(sBase);
759 return this->getWidth() == s.getWidth() &&
760 this->getHeight() == s.getHeight() &&
761 this->getSigma() == s.getSigma() &&
762 this->getTransform() == s.getTransform();
763}
764
765void GrRectBlurEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
766 *validFlags = 0;
767 return;
768}
769
770GR_DEFINE_EFFECT_TEST(GrRectBlurEffect);
771
772GrEffectRef* GrRectBlurEffect::TestCreate(SkRandom* random,
773 GrContext* context,
774 const GrDrawTargetCaps&,
775 GrTexture**) {
776 float sigma = random->nextRangeF(3,8);
777 float width = random->nextRangeF(200,300);
778 float height = random->nextRangeF(200,300);
779 return GrRectBlurEffect::Create(context, SkRect::MakeWH(width, height), sigma);
780}
781
782
783bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrContext* context,
784 GrPaint* grp,
785 const SkStrokeRec& strokeRec,
786 const SkPath& path) const {
787 if (fBlurStyle != SkBlurMaskFilter::kNormal_BlurStyle) {
788 return false;
789 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000790
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000791 SkRect rect;
792 if (!path.isRect(&rect)) {
793 return false;
794 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000795
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000796 if (!strokeRec.isFillStyle()) {
797 return false;
798 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000799
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000800 SkMatrix ctm = context->getMatrix();
801 SkScalar xformedSigma = this->computeXformedSigma(ctm);
802 rect.outset(3*xformedSigma, 3*xformedSigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000803
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000804 SkAutoTUnref<GrEffectRef> effect(GrRectBlurEffect::Create(
805 context, rect, xformedSigma));
806 if (!effect) {
807 return false;
808 }
809
810 GrContext::AutoMatrix am;
811 if (!am.setIdentity(context, grp)) {
812 return false;
813 }
814
815
816 grp->addCoverageEffect(effect);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000817
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000818 context->drawRect(*grp, rect);
819 return true;
820}
821
commit-bot@chromium.org82139702014-03-10 22:53:20 +0000822bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
823 GrPaint* grp,
824 const SkStrokeRec& strokeRec,
825 const SkRRect& rrect) const {
826 return false;
827}
828
robertphillips@google.com49149312013-07-03 15:34:35 +0000829bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
830 const SkIRect& clipBounds,
831 const SkMatrix& ctm,
832 SkRect* maskRect) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000833 SkScalar xformedSigma = this->computeXformedSigma(ctm);
834 if (xformedSigma <= 0) {
robertphillips@google.com49149312013-07-03 15:34:35 +0000835 return false;
reed@google.com2b75f422011-07-07 13:43:38 +0000836 }
robertphillips@google.com49149312013-07-03 15:34:35 +0000837
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000838 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
839 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
robertphillips@google.com49149312013-07-03 15:34:35 +0000840
841 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
842 srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000843 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
robertphillips@google.com49149312013-07-03 15:34:35 +0000844 // We prefer to blur small rect with small radius via CPU.
845 return false;
846 }
847
848 if (NULL == maskRect) {
849 // don't need to compute maskRect
850 return true;
851 }
852
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000853 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
robertphillips@google.com49149312013-07-03 15:34:35 +0000854
reed@google.com44699382013-10-31 17:28:30 +0000855 SkRect clipRect = SkRect::Make(clipBounds);
robertphillips@google.com49149312013-07-03 15:34:35 +0000856 SkRect srcRect(srcBounds);
857
858 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000859 srcRect.outset(sigma3, sigma3);
860 clipRect.outset(sigma3, sigma3);
robertphillips@google.com49149312013-07-03 15:34:35 +0000861 srcRect.intersect(clipRect);
862 *maskRect = srcRect;
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000863 return true;
reed@google.com2b75f422011-07-07 13:43:38 +0000864}
865
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000866bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +0000867 const SkMatrix& ctm,
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +0000868 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +0000869 GrTexture** result,
870 bool canOverwriteSrc) const {
871 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
872
873 GrContext* context = src->getContext();
874
875 GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
876
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +0000877 SkScalar xformedSigma = this->computeXformedSigma(ctm);
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000878 SkASSERT(xformedSigma > 0);
robertphillips@google.com49149312013-07-03 15:34:35 +0000879
880 // If we're doing a normal blur, we can clobber the pathTexture in the
881 // gaussianBlur. Otherwise, we need to save it for later compositing.
882 bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
robertphillips@google.com736dd032013-07-15 15:06:54 +0000883 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000884 clipRect, false, xformedSigma, xformedSigma);
robertphillips@google.com3cc820c2013-07-03 19:36:55 +0000885 if (NULL == *result) {
robertphillips@google.com49149312013-07-03 15:34:35 +0000886 return false;
887 }
888
889 if (!isNormalBlur) {
890 context->setIdentityMatrix();
891 GrPaint paint;
892 SkMatrix matrix;
893 matrix.setIDiv(src->width(), src->height());
894 // Blend pathTexture over blurTexture.
895 GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget());
commit-bot@chromium.org42dacab2013-07-13 17:24:24 +0000896 paint.addColorEffect(GrSimpleTextureEffect::Create(src, matrix))->unref();
robertphillips@google.com49149312013-07-03 15:34:35 +0000897 if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
898 // inner: dst = dst * src
899 paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
900 } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) {
901 // solid: dst = src + dst - src * dst
902 // = (1 - dst) * src + 1 * dst
903 paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
904 } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
905 // outer: dst = dst * (1 - src)
906 // = 0 * src + (1 - src) * dst
907 paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
908 }
909 context->drawRect(paint, clipRect);
910 }
911
912 return true;
913}
914
915#endif // SK_SUPPORT_GPU
916
917
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000918#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000919void SkBlurMaskFilterImpl::toString(SkString* str) const {
920 str->append("SkBlurMaskFilterImpl: (");
921
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000922 str->append("sigma: ");
923 str->appendScalar(fSigma);
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000924 str->append(" ");
925
926 static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = {
927 "normal", "solid", "outer", "inner"
928 };
929
930 str->appendf("style: %s ", gStyleName[fBlurStyle]);
931 str->append("flags: (");
932 if (fBlurFlags) {
933 bool needSeparator = false;
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +0000934 SkAddFlagToString(str,
935 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000936 "IgnoreXform", &needSeparator);
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +0000937 SkAddFlagToString(str,
938 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +0000939 "HighQuality", &needSeparator);
940 } else {
941 str->append("None");
942 }
943 str->append("))");
944}
945#endif
946
caryclark@google.comd26147a2011-12-15 14:16:43 +0000947SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
948 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
949SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END