blob: 96789c2d357e232f7a3e3a736df7fc2418163884 [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"
robertphillipsea461502015-05-26 11:38:03 -070022#include "GrDrawContext.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000023#include "GrTexture.h"
bsalomon6251d172014-10-15 10:50:36 -070024#include "GrFragmentProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080025#include "GrInvariantOutput.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000026#include "SkGrPixelRef.h"
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +000027#include "SkDraw.h"
joshualitteb2a6762014-12-04 11:35:33 -080028#include "effects/GrSimpleTextureEffect.h"
wangyix6af0c932015-07-22 10:21:17 -070029#include "gl/GrGLFragmentProcessor.h"
joshualitteb2a6762014-12-04 11:35:33 -080030#include "gl/builders/GrGLProgramBuilder.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000031#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000032
commit-bot@chromium.org508022c2014-05-01 15:24:55 +000033SkScalar SkBlurMaskFilter::ConvertRadiusToSigma(SkScalar radius) {
34 return SkBlurMask::ConvertRadiusToSigma(radius);
35}
36
reed@android.com8a1c16f2008-12-17 15:59:43 +000037class SkBlurMaskFilterImpl : public SkMaskFilter {
38public:
commit-bot@chromium.orge3964552014-04-28 16:25:35 +000039 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, uint32_t flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000040
41 // overrides from SkMaskFilter
mtklein36352bf2015-03-25 18:17:31 -070042 SkMask::Format getFormat() const override;
robertphillipsff0ca5e2015-07-22 11:54:44 -070043 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
44 SkIPoint* margin) const override;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000045
robertphillips@google.com49149312013-07-03 15:34:35 +000046#if SK_SUPPORT_GPU
robertphillipsff0ca5e2015-07-22 11:54:44 -070047 bool canFilterMaskGPU(const SkRect& devBounds,
48 const SkIRect& clipBounds,
49 const SkMatrix& ctm,
50 SkRect* maskRect) const override;
51 bool directFilterMaskGPU(GrTextureProvider* texProvider,
52 GrDrawContext* drawContext,
53 GrRenderTarget* rt,
54 GrPaint* grp,
55 const GrClip&,
56 const SkMatrix& viewMatrix,
57 const SkStrokeRec& strokeRec,
58 const SkPath& path) const override;
59 bool directFilterRRectMaskGPU(GrTextureProvider* texProvider,
60 GrDrawContext* drawContext,
61 GrRenderTarget* rt,
62 GrPaint* grp,
63 const GrClip&,
64 const SkMatrix& viewMatrix,
65 const SkStrokeRec& strokeRec,
66 const SkRRect& rrect) const override;
67 bool filterMaskGPU(GrTexture* src,
68 const SkMatrix& ctm,
69 const SkRect& maskRect,
70 GrTexture** result,
71 bool canOverwriteSrc) const override;
robertphillips@google.com49149312013-07-03 15:34:35 +000072#endif
73
mtklein36352bf2015-03-25 18:17:31 -070074 void computeFastBounds(const SkRect&, SkRect*) const override;
75 bool asABlur(BlurRec*) const override;
reed@android.com8a1c16f2008-12-17 15:59:43 +000076
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +000077 SK_TO_STRING_OVERRIDE()
djsollen@google.comba28d032012-03-26 17:57:35 +000078 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
reed@android.com8a1c16f2008-12-17 15:59:43 +000079
reed@google.comd729b3e2012-11-09 14:30:48 +000080protected:
robertphillipsff0ca5e2015-07-22 11:54:44 -070081 FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
82 const SkIRect& clipBounds,
83 NinePatch*) const override;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000084
robertphillipsff0ca5e2015-07-22 11:54:44 -070085 FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
86 const SkIRect& clipBounds,
87 NinePatch*) const override;
scroggo@google.coma8e33a92013-11-08 18:02:53 +000088
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000089 bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
humper@google.com7c5d7b72013-03-11 20:16:28 +000090 SkIPoint* margin, SkMask::CreateMode createMode) const;
commit-bot@chromium.orga4771542014-03-10 21:42:06 +000091 bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix,
92 SkIPoint* margin, SkMask::CreateMode createMode) const;
skia.committer@gmail.com453995e2012-11-10 02:01:26 +000093
reed@android.com8a1c16f2008-12-17 15:59:43 +000094private:
robertphillips@google.com49149312013-07-03 15:34:35 +000095 // To avoid unseemly allocation requests (esp. for finite platforms like
96 // handset) we limit the radius so something manageable. (as opposed to
97 // a request like 10,000)
robertphillips@google.com7ce661d2013-08-27 16:14:03 +000098 static const SkScalar kMAX_BLUR_SIGMA;
robertphillips@google.com49149312013-07-03 15:34:35 +000099
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000100 SkScalar fSigma;
101 SkBlurStyle fBlurStyle;
102 uint32_t fBlurFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103
reed@google.comdaaafa62014-04-29 15:20:16 +0000104 SkBlurQuality getQuality() const {
105 return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
106 kHigh_SkBlurQuality : kLow_SkBlurQuality;
107 }
108
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000109 SkBlurMaskFilterImpl(SkReadBuffer&);
mtklein36352bf2015-03-25 18:17:31 -0700110 void flatten(SkWriteBuffer&) const override;
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000111
112 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
robertphillips@google.com49149312013-07-03 15:34:35 +0000113 bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
114
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000115 SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma);
116 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
robertphillips@google.com49149312013-07-03 15:34:35 +0000117 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000118
reed9fa60da2014-08-21 07:59:51 -0700119 friend class SkBlurMaskFilter;
120
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 typedef SkMaskFilter INHERITED;
122};
123
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000124const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
robertphillips@google.com49149312013-07-03 15:34:35 +0000125
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000126SkMaskFilter* SkBlurMaskFilter::Create(SkBlurStyle style, SkScalar sigma, uint32_t flags) {
127 if (!SkScalarIsFinite(sigma) || sigma <= 0) {
128 return NULL;
129 }
130 if ((unsigned)style > (unsigned)kLastEnum_SkBlurStyle) {
131 return NULL;
132 }
133 if (flags > SkBlurMaskFilter::kAll_BlurFlag) {
134 return NULL;
135 }
136 return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
137}
138
reed@google.com03016a32011-08-12 14:59:59 +0000139///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000141SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, uint32_t flags)
142 : fSigma(sigma)
143 , fBlurStyle(style)
144 , fBlurFlags(flags) {
145 SkASSERT(fSigma > 0);
146 SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000147 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148}
149
reed@google.com30711b72012-12-18 19:18:39 +0000150SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 return SkMask::kA8_Format;
152}
153
reed@google.comdaaafa62014-04-29 15:20:16 +0000154bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
155 if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
156 return false;
157 }
158
159 if (rec) {
160 rec->fSigma = fSigma;
161 rec->fStyle = fBlurStyle;
162 rec->fQuality = this->getQuality();
163 }
164 return true;
165}
166
reed@google.com03016a32011-08-12 14:59:59 +0000167bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
reed@google.com30711b72012-12-18 19:18:39 +0000168 const SkMatrix& matrix,
169 SkIPoint* margin) const{
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000170 SkScalar sigma = this->computeXformedSigma(matrix);
reed@google.comdaaafa62014-04-29 15:20:16 +0000171 return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172}
173
humper@google.com7c5d7b72013-03-11 20:16:28 +0000174bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
175 const SkMatrix& matrix,
176 SkIPoint* margin, SkMask::CreateMode createMode) const{
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000177 SkScalar sigma = computeXformedSigma(matrix);
humper@google.com7c5d7b72013-03-11 20:16:28 +0000178
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000179 return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000180 margin, createMode);
181}
182
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000183bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
184 const SkMatrix& matrix,
185 SkIPoint* margin, SkMask::CreateMode createMode) const{
186 SkScalar sigma = computeXformedSigma(matrix);
187
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000188 return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle,
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000189 margin, createMode);
190}
191
reed@google.comd729b3e2012-11-09 14:30:48 +0000192#include "SkCanvas.h"
193
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000194static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
195 SkASSERT(mask != NULL);
196
reedb07a94f2014-11-19 05:03:18 -0800197 mask->fBounds = bounds.roundOut();
reed@google.comd729b3e2012-11-09 14:30:48 +0000198 mask->fRowBytes = SkAlign4(mask->fBounds.width());
199 mask->fFormat = SkMask::kA8_Format;
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000200 const size_t size = mask->computeImageSize();
reed@google.comd729b3e2012-11-09 14:30:48 +0000201 mask->fImage = SkMask::AllocImage(size);
202 if (NULL == mask->fImage) {
203 return false;
204 }
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000205
206 // FIXME: use sk_calloc in AllocImage?
reed@google.comd729b3e2012-11-09 14:30:48 +0000207 sk_bzero(mask->fImage, size);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000208 return true;
209}
210
211static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
212 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
213 return false;
214 }
215
216 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
217 // clean way to share more code?
218 SkBitmap bitmap;
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000219 bitmap.installMaskPixels(*mask);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000220
221 SkCanvas canvas(bitmap);
222 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
223 -SkIntToScalar(mask->fBounds.top()));
224
225 SkPaint paint;
226 paint.setAntiAlias(true);
227 canvas.drawRRect(rrect, paint);
228 return true;
229}
230
231static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
232 if (!prepare_to_draw_into_mask(rects[0], mask)) {
233 return false;
234 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000235
236 SkBitmap bitmap;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +0000237 bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
238 mask->fBounds.height(),
239 kAlpha_8_SkColorType,
240 kPremul_SkAlphaType),
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000241 mask->fImage, mask->fRowBytes);
reed@google.comd729b3e2012-11-09 14:30:48 +0000242
243 SkCanvas canvas(bitmap);
reed@google.comdab9b4f2012-11-19 16:45:14 +0000244 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
245 -SkIntToScalar(mask->fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000246
247 SkPaint paint;
248 paint.setAntiAlias(true);
249
reed@google.comdab9b4f2012-11-19 16:45:14 +0000250 if (1 == count) {
251 canvas.drawRect(rects[0], paint);
252 } else {
253 // todo: do I need a fast way to do this?
254 SkPath path;
255 path.addRect(rects[0]);
256 path.addRect(rects[1]);
257 path.setFillType(SkPath::kEvenOdd_FillType);
258 canvas.drawPath(path, paint);
259 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000260 return true;
261}
262
reed@google.comf276fa72012-11-21 14:14:10 +0000263static bool rect_exceeds(const SkRect& r, SkScalar v) {
264 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
265 r.width() > v || r.height() > v;
reed@google.com07784a02012-11-19 21:09:14 +0000266}
267
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800268#include "SkMaskCache.h"
269
reedb0df8be2015-02-04 09:07:17 -0800270static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) {
271 const size_t size = mask->computeTotalImageSize();
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800272 SkCachedData* data = SkResourceCache::NewCachedData(size);
273 if (data) {
reedb0df8be2015-02-04 09:07:17 -0800274 memcpy(data->writable_data(), mask->fImage, size);
275 SkMask::FreeImage(mask->fImage);
276 mask->fImage = (uint8_t*)data->data();
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800277 }
reedb0df8be2015-02-04 09:07:17 -0800278 return data;
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800279}
280
reedb0df8be2015-02-04 09:07:17 -0800281static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
282 SkBlurQuality quality, const SkRRect& rrect) {
283 return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800284}
285
reedb0df8be2015-02-04 09:07:17 -0800286static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
287 SkBlurQuality quality, const SkRRect& rrect) {
288 SkCachedData* cache = copy_mask_to_cacheddata(mask);
289 if (cache) {
290 SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800291 }
reedb0df8be2015-02-04 09:07:17 -0800292 return cache;
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800293}
294
reedb0df8be2015-02-04 09:07:17 -0800295static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
296 SkBlurQuality quality, const SkRect rects[], int count) {
297 return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800298}
299
reedb0df8be2015-02-04 09:07:17 -0800300static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
301 SkBlurQuality quality, const SkRect rects[], int count) {
302 SkCachedData* cache = copy_mask_to_cacheddata(mask);
303 if (cache) {
304 SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800305 }
reedb0df8be2015-02-04 09:07:17 -0800306 return cache;
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800307}
308
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000309#ifdef SK_IGNORE_FAST_RRECT_BLUR
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000310SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticblurrrect", false, "Use the faster analytic blur approach for ninepatch rects" );
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000311#else
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000312SK_CONF_DECLARE( bool, c_analyticBlurRRect, "mask.filter.blur.analyticblurrrect", true, "Use the faster analytic blur approach for ninepatch round rects" );
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000313#endif
314
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000315SkMaskFilter::FilterReturn
316SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
317 const SkIRect& clipBounds,
318 NinePatch* patch) const {
319 SkASSERT(patch != NULL);
320 switch (rrect.getType()) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000321 case SkRRect::kEmpty_Type:
322 // Nothing to draw.
323 return kFalse_FilterReturn;
324
325 case SkRRect::kRect_Type:
326 // We should have caught this earlier.
327 SkASSERT(false);
328 // Fall through.
329 case SkRRect::kOval_Type:
330 // The nine patch special case does not handle ovals, and we
331 // already have code for rectangles.
332 return kUnimplemented_FilterReturn;
333
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000334 // These three can take advantage of this fast path.
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000335 case SkRRect::kSimple_Type:
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000336 case SkRRect::kNinePatch_Type:
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000337 case SkRRect::kComplex_Type:
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000338 break;
339 }
340
341 // TODO: report correct metrics for innerstyle, where we do not grow the
342 // total bounds, but we do need an inset the size of our blur-radius
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000343 if (kInner_SkBlurStyle == fBlurStyle) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000344 return kUnimplemented_FilterReturn;
345 }
346
347 // TODO: take clipBounds into account to limit our coordinates up front
348 // for now, just skip too-large src rects (to take the old code path).
349 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
350 return kUnimplemented_FilterReturn;
351 }
352
353 SkIPoint margin;
354 SkMask srcM, dstM;
reedb07a94f2014-11-19 05:03:18 -0800355 srcM.fBounds = rrect.rect().roundOut();
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000356 srcM.fImage = NULL;
357 srcM.fFormat = SkMask::kA8_Format;
358 srcM.fRowBytes = 0;
359
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000360 bool filterResult = false;
361 if (c_analyticBlurRRect) {
362 // special case for fast round rect blur
363 // don't actually do the blur the first time, just compute the correct size
364 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
365 SkMask::kJustComputeBounds_CreateMode);
366 }
367
368 if (!filterResult) {
369 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
370 }
371
372 if (!filterResult) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000373 return kFalse_FilterReturn;
374 }
375
376 // Now figure out the appropriate width and height of the smaller round rectangle
377 // to stretch. It will take into account the larger radius per side as well as double
378 // the margin, to account for inner and outer blur.
379 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
380 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
381 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
382 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
383
384 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
385 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
386
387 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
388 // any fractional space on either side plus 1 for the part to stretch.
389 const SkScalar stretchSize = SkIntToScalar(3);
390
391 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
392 if (totalSmallWidth >= rrect.rect().width()) {
393 // There is no valid piece to stretch.
394 return kUnimplemented_FilterReturn;
395 }
396
397 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
398 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
399
400 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
401 if (totalSmallHeight >= rrect.rect().height()) {
402 // There is no valid piece to stretch.
403 return kUnimplemented_FilterReturn;
404 }
405
406 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
407
408 SkRRect smallRR;
409 SkVector radii[4];
410 radii[SkRRect::kUpperLeft_Corner] = UL;
411 radii[SkRRect::kUpperRight_Corner] = UR;
412 radii[SkRRect::kLowerRight_Corner] = LR;
413 radii[SkRRect::kLowerLeft_Corner] = LL;
414 smallRR.setRectRadii(smallR, radii);
415
reed4dca7a82014-10-23 12:42:46 -0700416 const SkScalar sigma = this->computeXformedSigma(matrix);
reedb0df8be2015-02-04 09:07:17 -0800417 SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle,
418 this->getQuality(), smallRR);
419 if (!cache) {
reed4dca7a82014-10-23 12:42:46 -0700420 bool analyticBlurWorked = false;
421 if (c_analyticBlurRRect) {
422 analyticBlurWorked =
423 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
424 SkMask::kComputeBoundsAndRenderImage_CreateMode);
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000425 }
robertphillips@google.comad993582013-11-11 18:45:18 +0000426
reed4dca7a82014-10-23 12:42:46 -0700427 if (!analyticBlurWorked) {
428 if (!draw_rrect_into_mask(smallRR, &srcM)) {
429 return kFalse_FilterReturn;
430 }
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000431
reed4dca7a82014-10-23 12:42:46 -0700432 SkAutoMaskFreeImage amf(srcM.fImage);
433
434 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
435 return kFalse_FilterReturn;
436 }
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000437 }
reedb0df8be2015-02-04 09:07:17 -0800438 cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000439 }
440
441 patch->fMask.fBounds.offsetTo(0, 0);
442 patch->fOuterRect = dstM.fBounds;
443 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
444 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
reedb0df8be2015-02-04 09:07:17 -0800445 SkASSERT(NULL == patch->fCache);
446 patch->fCache = cache; // transfer ownership to patch
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000447 return kTrue_FilterReturn;
448}
449
humper@google.come86af1f2013-03-11 21:23:36 +0000450SK_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 +0000451
reed@google.comd729b3e2012-11-09 14:30:48 +0000452SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000453SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
454 const SkMatrix& matrix,
455 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +0000456 NinePatch* patch) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000457 if (count < 1 || count > 2) {
458 return kUnimplemented_FilterReturn;
459 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000460
reed@google.com57850b92012-12-17 21:20:53 +0000461 // TODO: report correct metrics for innerstyle, where we do not grow the
462 // total bounds, but we do need an inset the size of our blur-radius
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000463 if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
reed@google.com57850b92012-12-17 21:20:53 +0000464 return kUnimplemented_FilterReturn;
465 }
466
reed@google.com07784a02012-11-19 21:09:14 +0000467 // TODO: take clipBounds into account to limit our coordinates up front
468 // for now, just skip too-large src rects (to take the old code path).
reed@google.comf276fa72012-11-21 14:14:10 +0000469 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
reed@google.com07784a02012-11-19 21:09:14 +0000470 return kUnimplemented_FilterReturn;
471 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000472
reed@google.comd729b3e2012-11-09 14:30:48 +0000473 SkIPoint margin;
474 SkMask srcM, dstM;
reedb07a94f2014-11-19 05:03:18 -0800475 srcM.fBounds = rects[0].roundOut();
reed@google.comd729b3e2012-11-09 14:30:48 +0000476 srcM.fImage = NULL;
477 srcM.fFormat = SkMask::kA8_Format;
478 srcM.fRowBytes = 0;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000479
humper@google.com7c5d7b72013-03-11 20:16:28 +0000480 bool filterResult = false;
481 if (count == 1 && c_analyticBlurNinepatch) {
482 // special case for fast rect blur
483 // don't actually do the blur the first time, just compute the correct size
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000484 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000485 SkMask::kJustComputeBounds_CreateMode);
486 } else {
487 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
488 }
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000489
humper@google.com7c5d7b72013-03-11 20:16:28 +0000490 if (!filterResult) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000491 return kFalse_FilterReturn;
492 }
493
494 /*
495 * smallR is the smallest version of 'rect' that will still guarantee that
496 * we get the same blur results on all edges, plus 1 center row/col that is
497 * representative of the extendible/stretchable edges of the ninepatch.
498 * Since our actual edge may be fractional we inset 1 more to be sure we
499 * don't miss any interior blur.
500 * x is an added pixel of blur, and { and } are the (fractional) edge
501 * pixels from the original rect.
502 *
503 * x x { x x .... x x } x x
504 *
505 * Thus, in this case, we inset by a total of 5 (on each side) beginning
506 * with our outer-rect (dstM.fBounds)
507 */
reed@google.comdab9b4f2012-11-19 16:45:14 +0000508 SkRect smallR[2];
509 SkIPoint center;
510
511 // +2 is from +1 for each edge (to account for possible fractional edges
512 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
513 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
514 SkIRect innerIR;
515
516 if (1 == count) {
517 innerIR = srcM.fBounds;
518 center.set(smallW, smallH);
519 } else {
520 SkASSERT(2 == count);
521 rects[1].roundIn(&innerIR);
522 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
523 smallH + (innerIR.top() - srcM.fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000524 }
525
reed@google.comdab9b4f2012-11-19 16:45:14 +0000526 // +1 so we get a clean, stretchable, center row/col
527 smallW += 1;
528 smallH += 1;
529
530 // we want the inset amounts to be integral, so we don't change any
531 // fractional phase on the fRight or fBottom of our smallR.
532 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
533 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
534 if (dx < 0 || dy < 0) {
535 // we're too small, relative to our blur, to break into nine-patch,
536 // so we ask to have our normal filterMask() be called.
537 return kUnimplemented_FilterReturn;
538 }
539
540 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 +0000541 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
542 return kUnimplemented_FilterReturn;
543 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000544 if (2 == count) {
545 smallR[1].set(rects[1].left(), rects[1].top(),
546 rects[1].right() - dx, rects[1].bottom() - dy);
547 SkASSERT(!smallR[1].isEmpty());
548 }
549
reed4dca7a82014-10-23 12:42:46 -0700550 const SkScalar sigma = this->computeXformedSigma(matrix);
reedb0df8be2015-02-04 09:07:17 -0800551 SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle,
552 this->getQuality(), smallR, count);
553 if (!cache) {
reed4dca7a82014-10-23 12:42:46 -0700554 if (count > 1 || !c_analyticBlurNinepatch) {
555 if (!draw_rects_into_mask(smallR, count, &srcM)) {
556 return kFalse_FilterReturn;
557 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000558
reed4dca7a82014-10-23 12:42:46 -0700559 SkAutoMaskFreeImage amf(srcM.fImage);
reed@google.comd198a5b2012-11-27 15:18:04 +0000560
reed4dca7a82014-10-23 12:42:46 -0700561 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
562 return kFalse_FilterReturn;
563 }
564 } else {
565 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
566 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
567 return kFalse_FilterReturn;
568 }
humper@google.com7c5d7b72013-03-11 20:16:28 +0000569 }
reedb0df8be2015-02-04 09:07:17 -0800570 cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
reed@google.comd729b3e2012-11-09 14:30:48 +0000571 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000572 patch->fMask.fBounds.offsetTo(0, 0);
573 patch->fOuterRect = dstM.fBounds;
574 patch->fCenter = center;
reedb0df8be2015-02-04 09:07:17 -0800575 SkASSERT(NULL == patch->fCache);
576 patch->fCache = cache; // transfer ownership to patch
reed@google.comd729b3e2012-11-09 14:30:48 +0000577 return kTrue_FilterReturn;
578}
579
reed@google.com30711b72012-12-18 19:18:39 +0000580void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
581 SkRect* dst) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000582 SkScalar pad = 3.0f * fSigma;
robertphillips@google.com17ad2bd2013-07-30 12:15:19 +0000583
584 dst->set(src.fLeft - pad, src.fTop - pad,
585 src.fRight + pad, src.fBottom + pad);
reed@google.com9efd9a02012-01-30 15:41:43 +0000586}
587
reed9fa60da2014-08-21 07:59:51 -0700588SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
589 const SkScalar sigma = buffer.readScalar();
590 const unsigned style = buffer.readUInt();
591 const unsigned flags = buffer.readUInt();
592 if (style <= kLastEnum_SkBlurStyle) {
593 return SkBlurMaskFilter::Create((SkBlurStyle)style, sigma, flags);
594 }
595 return NULL;
596}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000598void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
robertphillips@google.com11e05552013-12-03 19:46:58 +0000599 buffer.writeScalar(fSigma);
reed9fa60da2014-08-21 07:59:51 -0700600 buffer.writeUInt(fBlurStyle);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000601 buffer.writeUInt(fBlurFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602}
603
robertphillips@google.com49149312013-07-03 15:34:35 +0000604#if SK_SUPPORT_GPU
reed@google.com2b75f422011-07-07 13:43:38 +0000605
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000606class GrGLRectBlurEffect;
607
joshualittb0a8a372014-09-23 09:50:21 -0700608class GrRectBlurEffect : public GrFragmentProcessor {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000609public:
610 virtual ~GrRectBlurEffect();
611
mtklein36352bf2015-03-25 18:17:31 -0700612 const char* name() const override { return "RectBlur"; }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000613
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000614 /**
615 * Create a simple filter effect with custom bicubic coefficients.
616 */
bsalomond309e7a2015-04-30 14:18:54 -0700617 static GrFragmentProcessor* Create(GrTextureProvider *textureProvider, const SkRect& rect,
618 float sigma) {
humper4a24cd82014-06-17 13:39:29 -0700619 GrTexture *blurProfileTexture = NULL;
620 int doubleProfileSize = SkScalarCeilToInt(12*sigma);
621
622 if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
623 // if the blur sigma is too large so the gaussian overlaps the whole
624 // rect in either direction, fall back to CPU path for now.
625
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000626 return NULL;
627 }
humper4a24cd82014-06-17 13:39:29 -0700628
bsalomond309e7a2015-04-30 14:18:54 -0700629 bool createdBlurProfileTexture = CreateBlurProfileTexture(
630 textureProvider, sigma, &blurProfileTexture);
humper4a24cd82014-06-17 13:39:29 -0700631 SkAutoTUnref<GrTexture> hunref(blurProfileTexture);
632 if (!createdBlurProfileTexture) {
633 return NULL;
634 }
bsalomon55fad7a2014-07-08 07:34:20 -0700635 return SkNEW_ARGS(GrRectBlurEffect, (rect, sigma, blurProfileTexture));
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000636 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000637
humper4a24cd82014-06-17 13:39:29 -0700638 const SkRect& getRect() const { return fRect; }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000639 float getSigma() const { return fSigma; }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000640
641private:
wangyixb1daa862015-08-18 11:29:31 -0700642 GrGLFragmentProcessor* onCreateGLInstance() const override;
643
humper4a24cd82014-06-17 13:39:29 -0700644 GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blur_profile);
wangyix4b3050b2015-08-04 07:59:37 -0700645
646 virtual void onGetGLProcessorKey(const GrGLSLCaps& caps,
647 GrProcessorKeyBuilder* b) const override;
648
mtklein36352bf2015-03-25 18:17:31 -0700649 bool onIsEqual(const GrFragmentProcessor&) const override;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000650
mtklein36352bf2015-03-25 18:17:31 -0700651 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
egdaniel1a8ecdf2014-10-03 06:24:12 -0700652
bsalomond309e7a2015-04-30 14:18:54 -0700653 static bool CreateBlurProfileTexture(GrTextureProvider*, float sigma,
joshualitt16b27892014-12-18 07:47:16 -0800654 GrTexture **blurProfileTexture);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000655
humper4a24cd82014-06-17 13:39:29 -0700656 SkRect fRect;
657 float fSigma;
658 GrTextureAccess fBlurProfileAccess;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000659
joshualittb0a8a372014-09-23 09:50:21 -0700660 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000661
joshualittb0a8a372014-09-23 09:50:21 -0700662 typedef GrFragmentProcessor INHERITED;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000663};
664
joshualittb0a8a372014-09-23 09:50:21 -0700665class GrGLRectBlurEffect : public GrGLFragmentProcessor {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000666public:
joshualitteb2a6762014-12-04 11:35:33 -0800667 GrGLRectBlurEffect(const GrProcessor&) {}
wangyix7c157a92015-07-22 15:08:53 -0700668 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000669
wangyixb1daa862015-08-18 11:29:31 -0700670protected:
671 void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000672
673private:
kkinnunen7510b222014-07-30 00:04:16 -0700674 typedef GrGLProgramDataManager::UniformHandle UniformHandle;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000675
humper4a24cd82014-06-17 13:39:29 -0700676 UniformHandle fProxyRectUniform;
677 UniformHandle fProfileSizeUniform;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000678
joshualittb0a8a372014-09-23 09:50:21 -0700679 typedef GrGLFragmentProcessor INHERITED;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000680};
681
egdaniel29bee0f2015-04-29 11:54:42 -0700682void OutputRectBlurProfileLookup(GrGLFragmentBuilder* fsBuilder,
humper4a24cd82014-06-17 13:39:29 -0700683 const GrGLShaderBuilder::TextureSampler& sampler,
684 const char *output,
685 const char *profileSize, const char *loc,
686 const char *blurred_width,
687 const char *sharp_width) {
joshualitt30ba4362014-08-21 20:18:45 -0700688 fsBuilder->codeAppendf("\tfloat %s;\n", output);
689 fsBuilder->codeAppendf("\t\t{\n");
690 fsBuilder->codeAppendf("\t\t\tfloat coord = (0.5 * (abs(2.0*%s - %s) - %s))/%s;\n",
humper4a24cd82014-06-17 13:39:29 -0700691 loc, blurred_width, sharp_width, profileSize);
joshualitt30ba4362014-08-21 20:18:45 -0700692 fsBuilder->codeAppendf("\t\t\t%s = ", output);
693 fsBuilder->appendTextureLookup(sampler, "vec2(coord,0.5)");
694 fsBuilder->codeAppend(".a;\n");
695 fsBuilder->codeAppendf("\t\t}\n");
humper4a24cd82014-06-17 13:39:29 -0700696}
697
wangyix7c157a92015-07-22 15:08:53 -0700698void GrGLRectBlurEffect::emitCode(EmitArgs& args) {
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000699
humper4a24cd82014-06-17 13:39:29 -0700700 const char *rectName;
701 const char *profileSizeName;
702
wangyix7c157a92015-07-22 15:08:53 -0700703 fProxyRectUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
humper4a24cd82014-06-17 13:39:29 -0700704 kVec4f_GrSLType,
bsalomon422f56f2014-12-09 10:18:12 -0800705 kDefault_GrSLPrecision,
humper4a24cd82014-06-17 13:39:29 -0700706 "proxyRect",
707 &rectName);
wangyix7c157a92015-07-22 15:08:53 -0700708 fProfileSizeUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
humper4a24cd82014-06-17 13:39:29 -0700709 kFloat_GrSLType,
bsalomon422f56f2014-12-09 10:18:12 -0800710 kDefault_GrSLPrecision,
humper4a24cd82014-06-17 13:39:29 -0700711 "profileSize",
712 &profileSizeName);
713
wangyix7c157a92015-07-22 15:08:53 -0700714 GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700715 const char *fragmentPos = fsBuilder->fragmentPosition();
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000716
wangyix7c157a92015-07-22 15:08:53 -0700717 if (args.fInputColor) {
718 fsBuilder->codeAppendf("\tvec4 src=%s;\n", args.fInputColor);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000719 } else {
joshualitt30ba4362014-08-21 20:18:45 -0700720 fsBuilder->codeAppendf("\tvec4 src=vec4(1)\n;");
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000721 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000722
joshualitt30ba4362014-08-21 20:18:45 -0700723 fsBuilder->codeAppendf("\tvec2 translatedPos = %s.xy - %s.xy;\n", fragmentPos, rectName );
724 fsBuilder->codeAppendf("\tfloat width = %s.z - %s.x;\n", rectName, rectName);
725 fsBuilder->codeAppendf("\tfloat height = %s.w - %s.y;\n", rectName, rectName);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000726
joshualitt30ba4362014-08-21 20:18:45 -0700727 fsBuilder->codeAppendf("\tvec2 smallDims = vec2(width - %s, height-%s);\n", profileSizeName, profileSizeName);
728 fsBuilder->codeAppendf("\tfloat center = 2.0 * floor(%s/2.0 + .25) - 1.0;\n", profileSizeName);
729 fsBuilder->codeAppendf("\tvec2 wh = smallDims - vec2(center,center);\n");
humper4a24cd82014-06-17 13:39:29 -0700730
wangyix7c157a92015-07-22 15:08:53 -0700731 OutputRectBlurProfileLookup(fsBuilder, args.fSamplers[0], "horiz_lookup", profileSizeName,
732 "translatedPos.x", "width", "wh.x");
733 OutputRectBlurProfileLookup(fsBuilder, args.fSamplers[0], "vert_lookup", profileSizeName,
734 "translatedPos.y", "height", "wh.y");
humper4a24cd82014-06-17 13:39:29 -0700735
joshualitt30ba4362014-08-21 20:18:45 -0700736 fsBuilder->codeAppendf("\tfloat final = horiz_lookup * vert_lookup;\n");
wangyix7c157a92015-07-22 15:08:53 -0700737 fsBuilder->codeAppendf("\t%s = src * final;\n", args.fOutputColor );
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000738}
739
wangyixb1daa862015-08-18 11:29:31 -0700740void GrGLRectBlurEffect::onSetData(const GrGLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -0700741 const GrProcessor& proc) {
742 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
humper4a24cd82014-06-17 13:39:29 -0700743 SkRect rect = rbe.getRect();
744
kkinnunen7510b222014-07-30 00:04:16 -0700745 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
746 pdman.set1f(fProfileSizeUniform, SkScalarCeilToScalar(6*rbe.getSigma()));
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000747}
748
bsalomond309e7a2015-04-30 14:18:54 -0700749bool GrRectBlurEffect::CreateBlurProfileTexture(GrTextureProvider* textureProvider, float sigma,
joshualitt16b27892014-12-18 07:47:16 -0800750 GrTexture **blurProfileTexture) {
bsalomonf2703d82014-10-28 14:33:06 -0700751 GrSurfaceDesc texDesc;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000752
bsalomon24db3b12015-01-23 04:24:04 -0800753 unsigned int profileSize = SkScalarCeilToInt(6*sigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000754
bsalomon24db3b12015-01-23 04:24:04 -0800755 texDesc.fWidth = profileSize;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000756 texDesc.fHeight = 1;
757 texDesc.fConfig = kAlpha_8_GrPixelConfig;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000758
bsalomon8718aaf2015-02-19 07:24:21 -0800759 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
760 GrUniqueKey key;
761 GrUniqueKey::Builder builder(&key, kDomain, 1);
bsalomon24db3b12015-01-23 04:24:04 -0800762 builder[0] = profileSize;
763 builder.finish();
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000764
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000765 uint8_t *profile = NULL;
commit-bot@chromium.orgcc787fa2014-01-31 01:00:49 +0000766 SkAutoTDeleteArray<uint8_t> ada(NULL);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000767
bsalomond309e7a2015-04-30 14:18:54 -0700768 *blurProfileTexture = textureProvider->findAndRefTextureByUniqueKey(key);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000769
humper4a24cd82014-06-17 13:39:29 -0700770 if (NULL == *blurProfileTexture) {
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000771
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000772 SkBlurMask::ComputeBlurProfile(sigma, &profile);
commit-bot@chromium.orgcc787fa2014-01-31 01:00:49 +0000773 ada.reset(profile);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000774
bsalomond309e7a2015-04-30 14:18:54 -0700775 *blurProfileTexture = textureProvider->createTexture(texDesc, true, profile, 0);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000776
humper4a24cd82014-06-17 13:39:29 -0700777 if (NULL == *blurProfileTexture) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000778 return false;
779 }
bsalomond309e7a2015-04-30 14:18:54 -0700780 textureProvider->assignUniqueKeyToTexture(key, *blurProfileTexture);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000781 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000782
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000783 return true;
784}
785
786GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma,
humper4a24cd82014-06-17 13:39:29 -0700787 GrTexture *blur_profile)
joshualitteb2a6762014-12-04 11:35:33 -0800788 : fRect(rect),
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000789 fSigma(sigma),
humper4a24cd82014-06-17 13:39:29 -0700790 fBlurProfileAccess(blur_profile) {
joshualitteb2a6762014-12-04 11:35:33 -0800791 this->initClassID<GrRectBlurEffect>();
humper4a24cd82014-06-17 13:39:29 -0700792 this->addTextureAccess(&fBlurProfileAccess);
793 this->setWillReadFragmentPosition();
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000794}
795
796GrRectBlurEffect::~GrRectBlurEffect() {
797}
798
wangyix4b3050b2015-08-04 07:59:37 -0700799void GrRectBlurEffect::onGetGLProcessorKey(const GrGLSLCaps& caps,
joshualitteb2a6762014-12-04 11:35:33 -0800800 GrProcessorKeyBuilder* b) const {
801 GrGLRectBlurEffect::GenKey(*this, caps, b);
802}
803
wangyixb1daa862015-08-18 11:29:31 -0700804GrGLFragmentProcessor* GrRectBlurEffect::onCreateGLInstance() const {
joshualitteb2a6762014-12-04 11:35:33 -0800805 return SkNEW_ARGS(GrGLRectBlurEffect, (*this));
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000806}
807
bsalomon0e08fc12014-10-15 08:19:04 -0700808bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
joshualitt49586be2014-09-16 08:21:41 -0700809 const GrRectBlurEffect& s = sBase.cast<GrRectBlurEffect>();
humper4a24cd82014-06-17 13:39:29 -0700810 return this->getSigma() == s.getSigma() && this->getRect() == s.getRect();
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000811}
812
egdaniel605dd0f2014-11-12 08:35:25 -0800813void GrRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
joshualitt56995b52014-12-11 15:44:02 -0800814 inout->mulByUnknownSingleComponent();
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000815}
816
joshualittb0a8a372014-09-23 09:50:21 -0700817GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000818
joshualitt0067ff52015-07-08 14:26:19 -0700819GrFragmentProcessor* GrRectBlurEffect::TestCreate(GrProcessorTestData* d) {
820 float sigma = d->fRandom->nextRangeF(3,8);
821 float width = d->fRandom->nextRangeF(200,300);
822 float height = d->fRandom->nextRangeF(200,300);
823 return GrRectBlurEffect::Create(d->fContext->textureProvider(), SkRect::MakeWH(width, height),
bsalomond309e7a2015-04-30 14:18:54 -0700824 sigma);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000825}
826
827
robertphillipsff0ca5e2015-07-22 11:54:44 -0700828bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
829 GrDrawContext* drawContext,
joshualitt25d9c152015-02-18 12:29:52 -0800830 GrRenderTarget* rt,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000831 GrPaint* grp,
joshualitt570d2f82015-02-25 13:19:48 -0800832 const GrClip& clip,
joshualitt5531d512014-12-17 15:50:11 -0800833 const SkMatrix& viewMatrix,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000834 const SkStrokeRec& strokeRec,
835 const SkPath& path) const {
robertphillipsff0ca5e2015-07-22 11:54:44 -0700836 SkASSERT(drawContext);
837
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000838 if (fBlurStyle != kNormal_SkBlurStyle) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000839 return false;
840 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000841
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000842 SkRect rect;
843 if (!path.isRect(&rect)) {
844 return false;
845 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000846
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000847 if (!strokeRec.isFillStyle()) {
848 return false;
849 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000850
robertphillipsff0ca5e2015-07-22 11:54:44 -0700851 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
humper4a24cd82014-06-17 13:39:29 -0700852
robertphillipsff0ca5e2015-07-22 11:54:44 -0700853 int pad = SkScalarCeilToInt(6*xformedSigma)/2;
humper4a24cd82014-06-17 13:39:29 -0700854 rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000855
bsalomond309e7a2015-04-30 14:18:54 -0700856 SkAutoTUnref<GrFragmentProcessor> fp(GrRectBlurEffect::Create(
robertphillipsff0ca5e2015-07-22 11:54:44 -0700857 texProvider, rect, xformedSigma));
joshualittb0a8a372014-09-23 09:50:21 -0700858 if (!fp) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000859 return false;
860 }
861
joshualittb0a8a372014-09-23 09:50:21 -0700862 grp->addCoverageProcessor(fp);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000863
joshualittc2625822014-12-18 16:40:54 -0800864 SkMatrix inverse;
865 if (!viewMatrix.invert(&inverse)) {
866 return false;
867 }
robertphillipsea461502015-05-26 11:38:03 -0700868
robertphillipsff0ca5e2015-07-22 11:54:44 -0700869 drawContext->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), rect, inverse);
870 return true;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000871}
872
joshualittb0a8a372014-09-23 09:50:21 -0700873class GrRRectBlurEffect : public GrFragmentProcessor {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000874public:
875
robertphillipsff0ca5e2015-07-22 11:54:44 -0700876 static GrFragmentProcessor* Create(GrTextureProvider*, float sigma, const SkRRect&);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000877
878 virtual ~GrRRectBlurEffect() {};
mtklein36352bf2015-03-25 18:17:31 -0700879 const char* name() const override { return "GrRRectBlur"; }
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000880
881 const SkRRect& getRRect() const { return fRRect; }
882 float getSigma() const { return fSigma; }
883
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000884private:
wangyixb1daa862015-08-18 11:29:31 -0700885 GrGLFragmentProcessor* onCreateGLInstance() const override;
886
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000887 GrRRectBlurEffect(float sigma, const SkRRect&, GrTexture* profileTexture);
888
wangyix4b3050b2015-08-04 07:59:37 -0700889 virtual void onGetGLProcessorKey(const GrGLSLCaps& caps,
890 GrProcessorKeyBuilder* b) const override;
891
mtklein36352bf2015-03-25 18:17:31 -0700892 bool onIsEqual(const GrFragmentProcessor& other) const override;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000893
mtklein36352bf2015-03-25 18:17:31 -0700894 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
egdaniel1a8ecdf2014-10-03 06:24:12 -0700895
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000896 SkRRect fRRect;
897 float fSigma;
898 GrTextureAccess fNinePatchAccess;
899
joshualittb0a8a372014-09-23 09:50:21 -0700900 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000901
joshualittb0a8a372014-09-23 09:50:21 -0700902 typedef GrFragmentProcessor INHERITED;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000903};
904
905
robertphillipsff0ca5e2015-07-22 11:54:44 -0700906GrFragmentProcessor* GrRRectBlurEffect::Create(GrTextureProvider* texProvider, float sigma,
joshualittb0a8a372014-09-23 09:50:21 -0700907 const SkRRect& rrect) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000908 if (!rrect.isSimpleCircular()) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000909 return NULL;
910 }
911
912 // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
913 // sufficiently small relative to both the size of the corner radius and the
914 // width (and height) of the rrect.
915
916 unsigned int blurRadius = 3*SkScalarCeilToInt(sigma-1/6.0f);
917 unsigned int cornerRadius = SkScalarCeilToInt(rrect.getSimpleRadii().x());
918 if (cornerRadius + blurRadius > rrect.width()/2 ||
919 cornerRadius + blurRadius > rrect.height()/2) {
920 return NULL;
921 }
922
bsalomon8718aaf2015-02-19 07:24:21 -0800923 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
924 GrUniqueKey key;
925 GrUniqueKey::Builder builder(&key, kDomain, 2);
bsalomon24db3b12015-01-23 04:24:04 -0800926 builder[0] = blurRadius;
927 builder[1] = cornerRadius;
928 builder.finish();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000929
robertphillipsff0ca5e2015-07-22 11:54:44 -0700930 SkAutoTUnref<GrTexture> blurNinePatchTexture(texProvider->findAndRefTextureByUniqueKey(key));
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000931
joshualittd5b98402015-03-11 09:11:19 -0700932 if (!blurNinePatchTexture) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000933 SkMask mask;
934
bsalomon37f9a262015-02-02 13:00:10 -0800935 unsigned int smallRectSide = 2*(blurRadius + cornerRadius) + 1;
936
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000937 mask.fBounds = SkIRect::MakeWH(smallRectSide, smallRectSide);
938 mask.fFormat = SkMask::kA8_Format;
939 mask.fRowBytes = mask.fBounds.width();
940 mask.fImage = SkMask::AllocImage(mask.computeTotalImageSize());
941 SkAutoMaskFreeImage amfi(mask.fImage);
942
943 memset(mask.fImage, 0, mask.computeTotalImageSize());
944
945 SkRect smallRect;
946 smallRect.setWH(SkIntToScalar(smallRectSide), SkIntToScalar(smallRectSide));
947
948 SkRRect smallRRect;
949 smallRRect.setRectXY(smallRect, SkIntToScalar(cornerRadius), SkIntToScalar(cornerRadius));
950
951 SkPath path;
952 path.addRRect( smallRRect );
953
bsalomon37f9a262015-02-02 13:00:10 -0800954 SkDraw::DrawToMask(path, &mask.fBounds, NULL, NULL, &mask,
955 SkMask::kJustRenderImage_CreateMode, SkPaint::kFill_Style);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000956
joshualittd5b98402015-03-11 09:11:19 -0700957 SkMask blurredMask;
958 SkBlurMask::BoxBlur(&blurredMask, mask, sigma, kNormal_SkBlurStyle, kHigh_SkBlurQuality,
bsalomon37f9a262015-02-02 13:00:10 -0800959 NULL, true );
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000960
bsalomon37f9a262015-02-02 13:00:10 -0800961 unsigned int texSide = smallRectSide + 2*blurRadius;
962 GrSurfaceDesc texDesc;
963 texDesc.fWidth = texSide;
964 texDesc.fHeight = texSide;
965 texDesc.fConfig = kAlpha_8_GrPixelConfig;
966
bsalomond309e7a2015-04-30 14:18:54 -0700967 blurNinePatchTexture.reset(
robertphillipsff0ca5e2015-07-22 11:54:44 -0700968 texProvider->createTexture(texDesc, true, blurredMask.fImage, 0));
joshualittd5b98402015-03-11 09:11:19 -0700969 SkMask::FreeImage(blurredMask.fImage);
joshualitt5f5a8d72015-02-25 14:09:45 -0800970 if (!blurNinePatchTexture) {
971 return NULL;
972 }
robertphillipsff0ca5e2015-07-22 11:54:44 -0700973 texProvider->assignUniqueKeyToTexture(key, blurNinePatchTexture);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000974 }
bsalomon55fad7a2014-07-08 07:34:20 -0700975 return SkNEW_ARGS(GrRRectBlurEffect, (sigma, rrect, blurNinePatchTexture));
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000976}
977
egdaniel605dd0f2014-11-12 08:35:25 -0800978void GrRRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
joshualitt56995b52014-12-11 15:44:02 -0800979 inout->mulByUnknownSingleComponent();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000980}
981
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000982GrRRectBlurEffect::GrRRectBlurEffect(float sigma, const SkRRect& rrect, GrTexture *ninePatchTexture)
983 : fRRect(rrect),
984 fSigma(sigma),
985 fNinePatchAccess(ninePatchTexture) {
joshualitteb2a6762014-12-04 11:35:33 -0800986 this->initClassID<GrRRectBlurEffect>();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000987 this->addTextureAccess(&fNinePatchAccess);
988 this->setWillReadFragmentPosition();
989}
990
bsalomon0e08fc12014-10-15 08:19:04 -0700991bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
joshualitt49586be2014-09-16 08:21:41 -0700992 const GrRRectBlurEffect& rrbe = other.cast<GrRRectBlurEffect>();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000993 return fRRect.getSimpleRadii().fX == rrbe.fRRect.getSimpleRadii().fX && fSigma == rrbe.fSigma;
994}
995
996//////////////////////////////////////////////////////////////////////////////
997
joshualittb0a8a372014-09-23 09:50:21 -0700998GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +0000999
joshualitt0067ff52015-07-08 14:26:19 -07001000GrFragmentProcessor* GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) {
1001 SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
1002 SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
1003 SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
1004 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001005 SkRRect rrect;
1006 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
robertphillipsff0ca5e2015-07-22 11:54:44 -07001007 return GrRRectBlurEffect::Create(d->fContext->textureProvider(), sigma, rrect);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001008}
1009
1010//////////////////////////////////////////////////////////////////////////////
1011
joshualittb0a8a372014-09-23 09:50:21 -07001012class GrGLRRectBlurEffect : public GrGLFragmentProcessor {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001013public:
joshualitteb2a6762014-12-04 11:35:33 -08001014 GrGLRRectBlurEffect(const GrProcessor&) {}
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001015
wangyix7c157a92015-07-22 15:08:53 -07001016 virtual void emitCode(EmitArgs&) override;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001017
wangyixb1daa862015-08-18 11:29:31 -07001018protected:
1019 void onSetData(const GrGLProgramDataManager&, const GrProcessor&) override;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001020
1021private:
kkinnunen7510b222014-07-30 00:04:16 -07001022 GrGLProgramDataManager::UniformHandle fProxyRectUniform;
1023 GrGLProgramDataManager::UniformHandle fCornerRadiusUniform;
1024 GrGLProgramDataManager::UniformHandle fBlurRadiusUniform;
joshualittb0a8a372014-09-23 09:50:21 -07001025 typedef GrGLFragmentProcessor INHERITED;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001026};
1027
wangyix7c157a92015-07-22 15:08:53 -07001028void GrGLRRectBlurEffect::emitCode(EmitArgs& args) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001029 const char *rectName;
1030 const char *cornerRadiusName;
1031 const char *blurRadiusName;
1032
1033 // The proxy rect has left, top, right, and bottom edges correspond to
1034 // components x, y, z, and w, respectively.
1035
wangyix7c157a92015-07-22 15:08:53 -07001036 fProxyRectUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001037 kVec4f_GrSLType,
bsalomon422f56f2014-12-09 10:18:12 -08001038 kDefault_GrSLPrecision,
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001039 "proxyRect",
1040 &rectName);
wangyix7c157a92015-07-22 15:08:53 -07001041 fCornerRadiusUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -08001042 kFloat_GrSLType,
1043 kDefault_GrSLPrecision,
1044 "cornerRadius",
1045 &cornerRadiusName);
wangyix7c157a92015-07-22 15:08:53 -07001046 fBlurRadiusUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
bsalomon422f56f2014-12-09 10:18:12 -08001047 kFloat_GrSLType,
1048 kDefault_GrSLPrecision,
1049 "blurRadius",
1050 &blurRadiusName);
joshualitt30ba4362014-08-21 20:18:45 -07001051
wangyix7c157a92015-07-22 15:08:53 -07001052 GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -07001053 const char* fragmentPos = fsBuilder->fragmentPosition();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001054
1055 // warp the fragment position to the appropriate part of the 9patch blur texture
1056
joshualitt30ba4362014-08-21 20:18:45 -07001057 fsBuilder->codeAppendf("\t\tvec2 rectCenter = (%s.xy + %s.zw)/2.0;\n", rectName, rectName);
1058 fsBuilder->codeAppendf("\t\tvec2 translatedFragPos = %s.xy - %s.xy;\n", fragmentPos, rectName);
1059 fsBuilder->codeAppendf("\t\tfloat threshold = %s + 2.0*%s;\n", cornerRadiusName, blurRadiusName );
1060 fsBuilder->codeAppendf("\t\tvec2 middle = %s.zw - %s.xy - 2.0*threshold;\n", rectName, rectName );
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001061
joshualitt30ba4362014-08-21 20:18:45 -07001062 fsBuilder->codeAppendf("\t\tif (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x+threshold)) {\n" );
1063 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.x = threshold;\n");
1064 fsBuilder->codeAppendf("\t\t} else if (translatedFragPos.x >= (middle.x + threshold)) {\n");
1065 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.x -= middle.x - 1.0;\n");
1066 fsBuilder->codeAppendf("\t\t}\n");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001067
joshualitt30ba4362014-08-21 20:18:45 -07001068 fsBuilder->codeAppendf("\t\tif (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {\n" );
1069 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.y = threshold;\n");
1070 fsBuilder->codeAppendf("\t\t} else if (translatedFragPos.y >= (middle.y + threshold)) {\n");
1071 fsBuilder->codeAppendf("\t\t\ttranslatedFragPos.y -= middle.y - 1.0;\n");
1072 fsBuilder->codeAppendf("\t\t}\n");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001073
joshualitt30ba4362014-08-21 20:18:45 -07001074 fsBuilder->codeAppendf("\t\tvec2 proxyDims = vec2(2.0*threshold+1.0);\n");
1075 fsBuilder->codeAppendf("\t\tvec2 texCoord = translatedFragPos / proxyDims;\n");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001076
wangyix7c157a92015-07-22 15:08:53 -07001077 fsBuilder->codeAppendf("\t%s = ", args.fOutputColor);
1078 fsBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], "texCoord");
joshualitt30ba4362014-08-21 20:18:45 -07001079 fsBuilder->codeAppend(";\n");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001080}
1081
wangyixb1daa862015-08-18 11:29:31 -07001082void GrGLRRectBlurEffect::onSetData(const GrGLProgramDataManager& pdman,
joshualittb0a8a372014-09-23 09:50:21 -07001083 const GrProcessor& proc) {
1084 const GrRRectBlurEffect& brre = proc.cast<GrRRectBlurEffect>();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001085 SkRRect rrect = brre.getRRect();
1086
1087 float blurRadius = 3.f*SkScalarCeilToScalar(brre.getSigma()-1/6.0f);
kkinnunen7510b222014-07-30 00:04:16 -07001088 pdman.set1f(fBlurRadiusUniform, blurRadius);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001089
1090 SkRect rect = rrect.getBounds();
1091 rect.outset(blurRadius, blurRadius);
kkinnunen7510b222014-07-30 00:04:16 -07001092 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001093
1094 SkScalar radius = 0;
1095 SkASSERT(rrect.isSimpleCircular() || rrect.isRect());
1096 radius = rrect.getSimpleRadii().fX;
kkinnunen7510b222014-07-30 00:04:16 -07001097 pdman.set1f(fCornerRadiusUniform, radius);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001098}
1099
wangyix4b3050b2015-08-04 07:59:37 -07001100void GrRRectBlurEffect::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001101 GrGLRRectBlurEffect::GenKey(*this, caps, b);
1102}
1103
wangyixb1daa862015-08-18 11:29:31 -07001104GrGLFragmentProcessor* GrRRectBlurEffect::onCreateGLInstance() const {
joshualitteb2a6762014-12-04 11:35:33 -08001105 return SkNEW_ARGS(GrGLRRectBlurEffect, (*this));
1106}
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001107
robertphillipsff0ca5e2015-07-22 11:54:44 -07001108bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrTextureProvider* texProvider,
1109 GrDrawContext* drawContext,
joshualitt25d9c152015-02-18 12:29:52 -08001110 GrRenderTarget* rt,
commit-bot@chromium.org82139702014-03-10 22:53:20 +00001111 GrPaint* grp,
joshualitt570d2f82015-02-25 13:19:48 -08001112 const GrClip& clip,
joshualitt5531d512014-12-17 15:50:11 -08001113 const SkMatrix& viewMatrix,
commit-bot@chromium.org82139702014-03-10 22:53:20 +00001114 const SkStrokeRec& strokeRec,
1115 const SkRRect& rrect) const {
robertphillipsff0ca5e2015-07-22 11:54:44 -07001116 SkASSERT(drawContext);
1117
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001118 if (fBlurStyle != kNormal_SkBlurStyle) {
1119 return false;
1120 }
1121
1122 if (!strokeRec.isFillStyle()) {
1123 return false;
1124 }
1125
robertphillipsff0ca5e2015-07-22 11:54:44 -07001126 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001127 float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001128
robertphillipsff0ca5e2015-07-22 11:54:44 -07001129 SkRect proxyRect = rrect.rect();
1130 proxyRect.outset(extra, extra);
1131
1132 SkAutoTUnref<GrFragmentProcessor> fp(GrRRectBlurEffect::Create(texProvider,
1133 xformedSigma, rrect));
joshualittb0a8a372014-09-23 09:50:21 -07001134 if (!fp) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001135 return false;
1136 }
1137
joshualittb0a8a372014-09-23 09:50:21 -07001138 grp->addCoverageProcessor(fp);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001139
joshualittc2625822014-12-18 16:40:54 -08001140 SkMatrix inverse;
1141 if (!viewMatrix.invert(&inverse)) {
1142 return false;
1143 }
robertphillipsea461502015-05-26 11:38:03 -07001144
robertphillipsff0ca5e2015-07-22 11:54:44 -07001145 drawContext->drawNonAARectWithLocalMatrix(rt, clip, *grp, SkMatrix::I(), proxyRect, inverse);
1146 return true;
commit-bot@chromium.org82139702014-03-10 22:53:20 +00001147}
1148
robertphillips@google.com49149312013-07-03 15:34:35 +00001149bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
1150 const SkIRect& clipBounds,
1151 const SkMatrix& ctm,
1152 SkRect* maskRect) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001153 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1154 if (xformedSigma <= 0) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001155 return false;
reed@google.com2b75f422011-07-07 13:43:38 +00001156 }
robertphillips@google.com49149312013-07-03 15:34:35 +00001157
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001158 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
1159 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
robertphillips@google.com49149312013-07-03 15:34:35 +00001160
1161 if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
1162 srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001163 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001164 // We prefer to blur small rect with small radius via CPU.
1165 return false;
1166 }
1167
1168 if (NULL == maskRect) {
1169 // don't need to compute maskRect
1170 return true;
1171 }
1172
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001173 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
robertphillips@google.com49149312013-07-03 15:34:35 +00001174
reed@google.com44699382013-10-31 17:28:30 +00001175 SkRect clipRect = SkRect::Make(clipBounds);
robertphillips@google.com49149312013-07-03 15:34:35 +00001176 SkRect srcRect(srcBounds);
1177
1178 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001179 srcRect.outset(sigma3, sigma3);
1180 clipRect.outset(sigma3, sigma3);
robertphillipsf4e59952015-01-07 12:16:10 -08001181 if (!srcRect.intersect(clipRect)) {
1182 srcRect.setEmpty();
1183 }
robertphillips@google.com49149312013-07-03 15:34:35 +00001184 *maskRect = srcRect;
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +00001185 return true;
reed@google.com2b75f422011-07-07 13:43:38 +00001186}
1187
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +00001188bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +00001189 const SkMatrix& ctm,
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +00001190 const SkRect& maskRect,
robertphillips@google.com49149312013-07-03 15:34:35 +00001191 GrTexture** result,
1192 bool canOverwriteSrc) const {
1193 SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
1194
1195 GrContext* context = src->getContext();
1196
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +00001197 SkScalar xformedSigma = this->computeXformedSigma(ctm);
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001198 SkASSERT(xformedSigma > 0);
robertphillips@google.com49149312013-07-03 15:34:35 +00001199
1200 // If we're doing a normal blur, we can clobber the pathTexture in the
1201 // gaussianBlur. Otherwise, we need to save it for later compositing.
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001202 bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
robertphillips@google.com736dd032013-07-15 15:06:54 +00001203 *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001204 clipRect, false, xformedSigma, xformedSigma);
robertphillips@google.com3cc820c2013-07-03 19:36:55 +00001205 if (NULL == *result) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001206 return false;
1207 }
1208
1209 if (!isNormalBlur) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001210 GrPaint paint;
1211 SkMatrix matrix;
1212 matrix.setIDiv(src->width(), src->height());
1213 // Blend pathTexture over blurTexture.
joshualitt5f10b5c2015-07-09 10:24:35 -07001214 paint.addCoverageProcessor(GrSimpleTextureEffect::Create(paint.getProcessorDataManager(),
1215 src, matrix))->unref();
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001216 if (kInner_SkBlurStyle == fBlurStyle) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001217 // inner: dst = dst * src
egdanielb197b8f2015-02-17 07:34:43 -08001218 paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001219 } else if (kSolid_SkBlurStyle == fBlurStyle) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001220 // solid: dst = src + dst - src * dst
egdanielb197b8f2015-02-17 07:34:43 -08001221 // = src + (1 - src) * dst
1222 paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001223 } else if (kOuter_SkBlurStyle == fBlurStyle) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001224 // outer: dst = dst * (1 - src)
1225 // = 0 * src + (1 - src) * dst
egdanielb197b8f2015-02-17 07:34:43 -08001226 paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
robertphillips@google.com49149312013-07-03 15:34:35 +00001227 }
robertphillipsea461502015-05-26 11:38:03 -07001228
1229 GrDrawContext* drawContext = context->drawContext();
1230 if (!drawContext) {
1231 return false;
1232 }
1233
1234 drawContext->drawRect((*result)->asRenderTarget(), GrClip::WideOpen(),
1235 paint, SkMatrix::I(), clipRect);
robertphillips@google.com49149312013-07-03 15:34:35 +00001236 }
1237
1238 return true;
1239}
1240
1241#endif // SK_SUPPORT_GPU
1242
1243
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001244#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001245void SkBlurMaskFilterImpl::toString(SkString* str) const {
1246 str->append("SkBlurMaskFilterImpl: (");
1247
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001248 str->append("sigma: ");
1249 str->appendScalar(fSigma);
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001250 str->append(" ");
1251
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001252 static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = {
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001253 "normal", "solid", "outer", "inner"
1254 };
1255
1256 str->appendf("style: %s ", gStyleName[fBlurStyle]);
1257 str->append("flags: (");
1258 if (fBlurFlags) {
1259 bool needSeparator = false;
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +00001260 SkAddFlagToString(str,
1261 SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001262 "IgnoreXform", &needSeparator);
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +00001263 SkAddFlagToString(str,
1264 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001265 "HighQuality", &needSeparator);
1266 } else {
1267 str->append("None");
1268 }
1269 str->append("))");
1270}
1271#endif
1272
caryclark@google.comd26147a2011-12-15 14:16:43 +00001273SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
1274 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
1275SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END