blob: b53fc41dead1015bfb96f700e6bfdabac15cbe21 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkBlurMaskFilter.h"
9#include "SkBlurMask.h"
robertphillips@google.com736dd032013-07-15 15:06:54 +000010#include "SkGpuBlurUtils.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000011#include "SkReadBuffer.h"
12#include "SkWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkMaskFilter.h"
scroggo@google.coma8e33a92013-11-08 18:02:53 +000014#include "SkRRect.h"
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +000015#include "SkStringUtils.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000016#include "SkStrokeRec.h"
17
18#if SK_SUPPORT_GPU
robertphillips30c4cae2015-09-15 10:20:55 -070019#include "GrCircleBlurFragmentProcessor.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000020#include "GrContext.h"
Brian Osman11052242016-10-27 14:47:55 -040021#include "GrRenderTargetContext.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000022#include "GrTexture.h"
bsalomon6251d172014-10-15 10:50:36 -070023#include "GrFragmentProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080024#include "GrInvariantOutput.h"
Brian Salomon94efbf52016-11-29 13:43:05 -050025#include "GrShaderCaps.h"
robertphillipsf5a83e82016-08-10 12:00:09 -070026#include "GrStyle.h"
Robert Phillips4a24da52016-12-14 09:00:07 -050027#include "GrTextureProxy.h"
joshualitteb2a6762014-12-04 11:35:33 -080028#include "effects/GrSimpleTextureEffect.h"
egdaniel64c47282015-11-13 06:54:19 -080029#include "glsl/GrGLSLFragmentProcessor.h"
egdaniel2d721d32015-11-11 13:06:05 -080030#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070031#include "glsl/GrGLSLProgramDataManager.h"
egdaniel7ea439b2015-12-03 09:20:44 -080032#include "glsl/GrGLSLUniformHandler.h"
robertphillips@google.com49149312013-07-03 15:34:35 +000033#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +000034
commit-bot@chromium.org508022c2014-05-01 15:24:55 +000035SkScalar SkBlurMaskFilter::ConvertRadiusToSigma(SkScalar radius) {
36 return SkBlurMask::ConvertRadiusToSigma(radius);
37}
38
reed@android.com8a1c16f2008-12-17 15:59:43 +000039class SkBlurMaskFilterImpl : public SkMaskFilter {
40public:
robertphillips27cdd942016-08-10 16:25:25 -070041 SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, const SkRect& occluder, uint32_t flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
43 // overrides from SkMaskFilter
mtklein36352bf2015-03-25 18:17:31 -070044 SkMask::Format getFormat() const override;
robertphillipsff0ca5e2015-07-22 11:54:44 -070045 bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
46 SkIPoint* margin) const override;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +000047
robertphillips@google.com49149312013-07-03 15:34:35 +000048#if SK_SUPPORT_GPU
robertphillips30c4cae2015-09-15 10:20:55 -070049 bool canFilterMaskGPU(const SkRRect& devRRect,
robertphillipsff0ca5e2015-07-22 11:54:44 -070050 const SkIRect& clipBounds,
51 const SkMatrix& ctm,
52 SkRect* maskRect) const override;
53 bool directFilterMaskGPU(GrTextureProvider* texProvider,
Brian Osman11052242016-10-27 14:47:55 -040054 GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -050055 GrPaint&&,
robertphillipsff0ca5e2015-07-22 11:54:44 -070056 const GrClip&,
57 const SkMatrix& viewMatrix,
58 const SkStrokeRec& strokeRec,
59 const SkPath& path) const override;
robertphillipsf5a83e82016-08-10 12:00:09 -070060 bool directFilterRRectMaskGPU(GrContext*,
Brian Osman11052242016-10-27 14:47:55 -040061 GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -050062 GrPaint&&,
robertphillipsff0ca5e2015-07-22 11:54:44 -070063 const GrClip&,
64 const SkMatrix& viewMatrix,
65 const SkStrokeRec& strokeRec,
robertphillips27cdd942016-08-10 16:25:25 -070066 const SkRRect& rrect,
67 const SkRRect& devRRect) const override;
Robert Phillips4a24da52016-12-14 09:00:07 -050068 sk_sp<GrTextureProxy> filterMaskGPU(GrContext*,
69 sk_sp<GrTextureProxy> srcProxy,
70 const SkMatrix& ctm,
71 const SkIRect& maskRect) 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
robertphillips9aff85a2016-08-05 07:51:29 -070094 bool ignoreXform() const {
95 return SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
96 }
97
reed@android.com8a1c16f2008-12-17 15:59:43 +000098private:
robertphillips@google.com49149312013-07-03 15:34:35 +000099 // To avoid unseemly allocation requests (esp. for finite platforms like
100 // handset) we limit the radius so something manageable. (as opposed to
101 // a request like 10,000)
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000102 static const SkScalar kMAX_BLUR_SIGMA;
robertphillips@google.com49149312013-07-03 15:34:35 +0000103
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000104 SkScalar fSigma;
105 SkBlurStyle fBlurStyle;
robertphillips27cdd942016-08-10 16:25:25 -0700106 SkRect fOccluder;
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000107 uint32_t fBlurFlags;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
reed@google.comdaaafa62014-04-29 15:20:16 +0000109 SkBlurQuality getQuality() const {
110 return (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
111 kHigh_SkBlurQuality : kLow_SkBlurQuality;
112 }
113
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000114 SkBlurMaskFilterImpl(SkReadBuffer&);
mtklein36352bf2015-03-25 18:17:31 -0700115 void flatten(SkWriteBuffer&) const override;
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000116
117 SkScalar computeXformedSigma(const SkMatrix& ctm) const {
robertphillips9aff85a2016-08-05 07:51:29 -0700118 SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma);
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000119 return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
robertphillips@google.com49149312013-07-03 15:34:35 +0000120 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000121
reed9fa60da2014-08-21 07:59:51 -0700122 friend class SkBlurMaskFilter;
123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 typedef SkMaskFilter INHERITED;
125};
126
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000127const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
robertphillips@google.com49149312013-07-03 15:34:35 +0000128
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400129sk_sp<SkMaskFilter> SkBlurMaskFilter::Make(SkBlurStyle style, SkScalar sigma,
robertphillips27cdd942016-08-10 16:25:25 -0700130 const SkRect& occluder, uint32_t flags) {
Robert Phillips98624d22016-12-19 11:37:37 -0500131 SkASSERT(!(flags & ~SkBlurMaskFilter::kAll_BlurFlag));
132 SkASSERT(style <= kLastEnum_SkBlurStyle);
133
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000134 if (!SkScalarIsFinite(sigma) || sigma <= 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700135 return nullptr;
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000136 }
robertphillips9aff85a2016-08-05 07:51:29 -0700137
robertphillips27cdd942016-08-10 16:25:25 -0700138 return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, occluder, flags));
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000139}
140
robertphillipsc4d2f902016-08-16 09:30:03 -0700141// linearly interpolate between y1 & y3 to match x2's position between x1 & x3
142static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) {
143 SkASSERT(x1 <= x2 && x2 <= x3);
144 SkASSERT(y1 <= y3);
145
146 SkScalar t = (x2 - x1) / (x3 - x1);
147 return y1 + t * (y3 - y1);
148}
149
150// Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion
151// point in 'array2' that linearly interpolates between the existing values.
152// Return a bit mask which contains a copy of 'inputMask' for all the cells between the two
153// insertion points.
154static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2,
155 SkScalar lower, SkScalar higher,
156 int* num, uint32_t inputMask, int maskSize) {
157 SkASSERT(lower < higher);
158 SkASSERT(lower >= array1[0] && higher <= array1[*num-1]);
159
160 int32_t skipMask = 0x0;
161 int i;
162 for (i = 0; i < *num; ++i) {
163 if (lower >= array1[i] && lower < array1[i+1]) {
164 if (!SkScalarNearlyEqual(lower, array1[i])) {
165 memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
166 array1[i+1] = lower;
167 memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
168 array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]);
169 i++;
170 (*num)++;
171 }
172 break;
173 }
174 }
175 for ( ; i < *num; ++i) {
176 skipMask |= inputMask << (i*maskSize);
177 if (higher > array1[i] && higher <= array1[i+1]) {
178 if (!SkScalarNearlyEqual(higher, array1[i+1])) {
179 memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar));
180 array1[i+1] = higher;
181 memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar));
182 array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]);
183 (*num)++;
184 }
185 break;
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400186 }
robertphillipsc4d2f902016-08-16 09:30:03 -0700187 }
188
189 return skipMask;
190}
191
192bool SkBlurMaskFilter::ComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect,
193 const SkRect& occluder,
194 SkScalar sigma, SkScalar xformedSigma,
robertphillipsf5a83e82016-08-10 12:00:09 -0700195 SkRRect* rrectToDraw,
196 SkISize* widthHeight,
robertphillipsc4d2f902016-08-16 09:30:03 -0700197 SkScalar rectXs[kMaxDivisions],
198 SkScalar rectYs[kMaxDivisions],
199 SkScalar texXs[kMaxDivisions],
200 SkScalar texYs[kMaxDivisions],
201 int* numXs, int* numYs, uint32_t* skipMask) {
202 unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f);
203 SkScalar srcBlurRadius = 3.0f * sigma;
robertphillipsf5a83e82016-08-10 12:00:09 -0700204
robertphillipsc4d2f902016-08-16 09:30:03 -0700205 const SkRect& devOrig = devRRect.getBounds();
206 const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner);
207 const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner);
208 const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner);
209 const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner);
robertphillipsf5a83e82016-08-10 12:00:09 -0700210
robertphillipsc4d2f902016-08-16 09:30:03 -0700211 const int devLeft = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fX, devRadiiLL.fX));
212 const int devTop = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUL.fY, devRadiiUR.fY));
213 const int devRight = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiUR.fX, devRadiiLR.fX));
214 const int devBot = SkScalarCeilToInt(SkTMax<SkScalar>(devRadiiLL.fY, devRadiiLR.fY));
robertphillipsf5a83e82016-08-10 12:00:09 -0700215
216 // This is a conservative check for nine-patchability
robertphillipsc4d2f902016-08-16 09:30:03 -0700217 if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius ||
218 devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) {
robertphillipsf5a83e82016-08-10 12:00:09 -0700219 return false;
220 }
221
robertphillipsc4d2f902016-08-16 09:30:03 -0700222 const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner);
223 const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner);
224 const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner);
225 const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner);
robertphillipsf5a83e82016-08-10 12:00:09 -0700226
robertphillipsc4d2f902016-08-16 09:30:03 -0700227 const SkScalar srcLeft = SkTMax<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX);
228 const SkScalar srcTop = SkTMax<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY);
229 const SkScalar srcRight = SkTMax<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX);
230 const SkScalar srcBot = SkTMax<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY);
231
232 int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1;
233 int newRRHeight = 2*devBlurRadius + devTop + devBot + 1;
234 widthHeight->fWidth = newRRWidth + 2 * devBlurRadius;
235 widthHeight->fHeight = newRRHeight + 2 * devBlurRadius;
236
237 const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius);
238
239 rectXs[0] = srcProxyRect.fLeft;
240 rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft;
241 rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight;
242 rectXs[3] = srcProxyRect.fRight;
243
244 rectYs[0] = srcProxyRect.fTop;
245 rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop;
246 rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot;
247 rectYs[3] = srcProxyRect.fBottom;
248
249 texXs[0] = 0.0f;
250 texXs[1] = 2.0f*devBlurRadius + devLeft;
251 texXs[2] = 2.0f*devBlurRadius + devLeft + 1;
252 texXs[3] = SkIntToScalar(widthHeight->fWidth);
253
254 texYs[0] = 0.0f;
255 texYs[1] = 2.0f*devBlurRadius + devTop;
256 texYs[2] = 2.0f*devBlurRadius + devTop + 1;
257 texYs[3] = SkIntToScalar(widthHeight->fHeight);
258
259 SkRect temp = occluder;
260
robertphillipsf5a83e82016-08-10 12:00:09 -0700261 *numXs = 4;
robertphillipsf5a83e82016-08-10 12:00:09 -0700262 *numYs = 4;
robertphillipsc4d2f902016-08-16 09:30:03 -0700263 *skipMask = 0;
264 if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
265 *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1);
266 *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom,
267 numYs, *skipMask, *numXs-1);
268 }
robertphillipsf5a83e82016-08-10 12:00:09 -0700269
robertphillipsc4d2f902016-08-16 09:30:03 -0700270 const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius),
271 SkIntToScalar(devBlurRadius),
272 SkIntToScalar(newRRWidth),
273 SkIntToScalar(newRRHeight));
robertphillipsf5a83e82016-08-10 12:00:09 -0700274 SkVector newRadii[4];
robertphillipsc4d2f902016-08-16 09:30:03 -0700275 newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) };
276 newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) };
277 newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) };
278 newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) };
robertphillipsf5a83e82016-08-10 12:00:09 -0700279
280 rrectToDraw->setRectRadii(newRect, newRadii);
281 return true;
282}
283
reed@google.com03016a32011-08-12 14:59:59 +0000284///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
robertphillips27cdd942016-08-10 16:25:25 -0700286SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style,
287 const SkRect& occluder, uint32_t flags)
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000288 : fSigma(sigma)
289 , fBlurStyle(style)
robertphillips27cdd942016-08-10 16:25:25 -0700290 , fOccluder(occluder)
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000291 , fBlurFlags(flags) {
292 SkASSERT(fSigma > 0);
293 SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
senorblanco@chromium.org038aff62010-12-06 23:45:58 +0000294 SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295}
296
reed@google.com30711b72012-12-18 19:18:39 +0000297SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 return SkMask::kA8_Format;
299}
300
reed@google.comdaaafa62014-04-29 15:20:16 +0000301bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
robertphillips9aff85a2016-08-05 07:51:29 -0700302 if (this->ignoreXform()) {
reed@google.comdaaafa62014-04-29 15:20:16 +0000303 return false;
304 }
305
306 if (rec) {
307 rec->fSigma = fSigma;
308 rec->fStyle = fBlurStyle;
309 rec->fQuality = this->getQuality();
310 }
311 return true;
312}
313
reed@google.com03016a32011-08-12 14:59:59 +0000314bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
reed@google.com30711b72012-12-18 19:18:39 +0000315 const SkMatrix& matrix,
robertphillips30c4cae2015-09-15 10:20:55 -0700316 SkIPoint* margin) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000317 SkScalar sigma = this->computeXformedSigma(matrix);
reed@google.comdaaafa62014-04-29 15:20:16 +0000318 return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, this->getQuality(), margin);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319}
320
humper@google.com7c5d7b72013-03-11 20:16:28 +0000321bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
322 const SkMatrix& matrix,
robertphillips30c4cae2015-09-15 10:20:55 -0700323 SkIPoint* margin, SkMask::CreateMode createMode) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000324 SkScalar sigma = computeXformedSigma(matrix);
humper@google.com7c5d7b72013-03-11 20:16:28 +0000325
robertphillips30c4cae2015-09-15 10:20:55 -0700326 return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
humper@google.com7c5d7b72013-03-11 20:16:28 +0000327}
328
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000329bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r,
330 const SkMatrix& matrix,
robertphillips30c4cae2015-09-15 10:20:55 -0700331 SkIPoint* margin, SkMask::CreateMode createMode) const {
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000332 SkScalar sigma = computeXformedSigma(matrix);
333
robertphillips30c4cae2015-09-15 10:20:55 -0700334 return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode);
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000335}
336
reed@google.comd729b3e2012-11-09 14:30:48 +0000337#include "SkCanvas.h"
338
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000339static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
halcanary96fcdcc2015-08-27 07:41:13 -0700340 SkASSERT(mask != nullptr);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000341
reedb07a94f2014-11-19 05:03:18 -0800342 mask->fBounds = bounds.roundOut();
reed@google.comd729b3e2012-11-09 14:30:48 +0000343 mask->fRowBytes = SkAlign4(mask->fBounds.width());
344 mask->fFormat = SkMask::kA8_Format;
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000345 const size_t size = mask->computeImageSize();
reed@google.comd729b3e2012-11-09 14:30:48 +0000346 mask->fImage = SkMask::AllocImage(size);
halcanary96fcdcc2015-08-27 07:41:13 -0700347 if (nullptr == mask->fImage) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000348 return false;
349 }
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000350
351 // FIXME: use sk_calloc in AllocImage?
reed@google.comd729b3e2012-11-09 14:30:48 +0000352 sk_bzero(mask->fImage, size);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000353 return true;
354}
355
356static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
357 if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
358 return false;
359 }
360
361 // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a
362 // clean way to share more code?
363 SkBitmap bitmap;
commit-bot@chromium.orgdac52252014-02-17 21:21:46 +0000364 bitmap.installMaskPixels(*mask);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000365
366 SkCanvas canvas(bitmap);
367 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
368 -SkIntToScalar(mask->fBounds.top()));
369
370 SkPaint paint;
371 paint.setAntiAlias(true);
372 canvas.drawRRect(rrect, paint);
373 return true;
374}
375
376static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) {
377 if (!prepare_to_draw_into_mask(rects[0], mask)) {
378 return false;
379 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000380
381 SkBitmap bitmap;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +0000382 bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(),
383 mask->fBounds.height(),
384 kAlpha_8_SkColorType,
385 kPremul_SkAlphaType),
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +0000386 mask->fImage, mask->fRowBytes);
reed@google.comd729b3e2012-11-09 14:30:48 +0000387
388 SkCanvas canvas(bitmap);
reed@google.comdab9b4f2012-11-19 16:45:14 +0000389 canvas.translate(-SkIntToScalar(mask->fBounds.left()),
390 -SkIntToScalar(mask->fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000391
392 SkPaint paint;
393 paint.setAntiAlias(true);
394
reed@google.comdab9b4f2012-11-19 16:45:14 +0000395 if (1 == count) {
396 canvas.drawRect(rects[0], paint);
397 } else {
398 // todo: do I need a fast way to do this?
399 SkPath path;
400 path.addRect(rects[0]);
401 path.addRect(rects[1]);
402 path.setFillType(SkPath::kEvenOdd_FillType);
403 canvas.drawPath(path, paint);
404 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000405 return true;
406}
407
reed@google.comf276fa72012-11-21 14:14:10 +0000408static bool rect_exceeds(const SkRect& r, SkScalar v) {
409 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
410 r.width() > v || r.height() > v;
reed@google.com07784a02012-11-19 21:09:14 +0000411}
412
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800413#include "SkMaskCache.h"
414
reedb0df8be2015-02-04 09:07:17 -0800415static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) {
416 const size_t size = mask->computeTotalImageSize();
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800417 SkCachedData* data = SkResourceCache::NewCachedData(size);
418 if (data) {
reedb0df8be2015-02-04 09:07:17 -0800419 memcpy(data->writable_data(), mask->fImage, size);
420 SkMask::FreeImage(mask->fImage);
421 mask->fImage = (uint8_t*)data->data();
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800422 }
reedb0df8be2015-02-04 09:07:17 -0800423 return data;
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800424}
425
reedb0df8be2015-02-04 09:07:17 -0800426static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
427 SkBlurQuality quality, const SkRRect& rrect) {
428 return SkMaskCache::FindAndRef(sigma, style, quality, rrect, mask);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800429}
430
reedb0df8be2015-02-04 09:07:17 -0800431static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style,
432 SkBlurQuality quality, const SkRRect& rrect) {
433 SkCachedData* cache = copy_mask_to_cacheddata(mask);
434 if (cache) {
435 SkMaskCache::Add(sigma, style, quality, rrect, *mask, cache);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800436 }
reedb0df8be2015-02-04 09:07:17 -0800437 return cache;
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800438}
439
reedb0df8be2015-02-04 09:07:17 -0800440static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
441 SkBlurQuality quality, const SkRect rects[], int count) {
442 return SkMaskCache::FindAndRef(sigma, style, quality, rects, count, mask);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800443}
444
reedb0df8be2015-02-04 09:07:17 -0800445static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style,
446 SkBlurQuality quality, const SkRect rects[], int count) {
447 SkCachedData* cache = copy_mask_to_cacheddata(mask);
448 if (cache) {
449 SkMaskCache::Add(sigma, style, quality, rects, count, *mask, cache);
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800450 }
reedb0df8be2015-02-04 09:07:17 -0800451 return cache;
qiankun.miaoeabd0d72015-01-07 19:20:49 -0800452}
453
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000454#ifdef SK_IGNORE_FAST_RRECT_BLUR
halcanary4e44efe2016-08-04 10:47:16 -0700455 // Use the faster analytic blur approach for ninepatch round rects
456 static const bool c_analyticBlurRRect{false};
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000457#else
halcanary4e44efe2016-08-04 10:47:16 -0700458 static const bool c_analyticBlurRRect{true};
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000459#endif
460
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000461SkMaskFilter::FilterReturn
462SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
463 const SkIRect& clipBounds,
464 NinePatch* patch) const {
halcanary96fcdcc2015-08-27 07:41:13 -0700465 SkASSERT(patch != nullptr);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000466 switch (rrect.getType()) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000467 case SkRRect::kEmpty_Type:
468 // Nothing to draw.
469 return kFalse_FilterReturn;
470
471 case SkRRect::kRect_Type:
472 // We should have caught this earlier.
473 SkASSERT(false);
474 // Fall through.
475 case SkRRect::kOval_Type:
476 // The nine patch special case does not handle ovals, and we
477 // already have code for rectangles.
478 return kUnimplemented_FilterReturn;
479
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000480 // These three can take advantage of this fast path.
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000481 case SkRRect::kSimple_Type:
commit-bot@chromium.orgf338d7c2014-03-17 21:17:30 +0000482 case SkRRect::kNinePatch_Type:
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000483 case SkRRect::kComplex_Type:
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000484 break;
485 }
486
487 // TODO: report correct metrics for innerstyle, where we do not grow the
488 // total bounds, but we do need an inset the size of our blur-radius
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000489 if (kInner_SkBlurStyle == fBlurStyle) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000490 return kUnimplemented_FilterReturn;
491 }
492
493 // TODO: take clipBounds into account to limit our coordinates up front
494 // for now, just skip too-large src rects (to take the old code path).
495 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
496 return kUnimplemented_FilterReturn;
497 }
498
499 SkIPoint margin;
500 SkMask srcM, dstM;
reedb07a94f2014-11-19 05:03:18 -0800501 srcM.fBounds = rrect.rect().roundOut();
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000502 srcM.fFormat = SkMask::kA8_Format;
503 srcM.fRowBytes = 0;
504
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000505 bool filterResult = false;
506 if (c_analyticBlurRRect) {
507 // special case for fast round rect blur
508 // don't actually do the blur the first time, just compute the correct size
509 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
510 SkMask::kJustComputeBounds_CreateMode);
511 }
512
513 if (!filterResult) {
514 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
515 }
516
517 if (!filterResult) {
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000518 return kFalse_FilterReturn;
519 }
520
521 // Now figure out the appropriate width and height of the smaller round rectangle
522 // to stretch. It will take into account the larger radius per side as well as double
523 // the margin, to account for inner and outer blur.
524 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
525 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
526 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
527 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
528
529 const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
530 const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
531
532 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
533 // any fractional space on either side plus 1 for the part to stretch.
534 const SkScalar stretchSize = SkIntToScalar(3);
535
536 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
537 if (totalSmallWidth >= rrect.rect().width()) {
538 // There is no valid piece to stretch.
539 return kUnimplemented_FilterReturn;
540 }
541
542 const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
543 const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
544
545 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
546 if (totalSmallHeight >= rrect.rect().height()) {
547 // There is no valid piece to stretch.
548 return kUnimplemented_FilterReturn;
549 }
550
551 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
552
553 SkRRect smallRR;
554 SkVector radii[4];
555 radii[SkRRect::kUpperLeft_Corner] = UL;
556 radii[SkRRect::kUpperRight_Corner] = UR;
557 radii[SkRRect::kLowerRight_Corner] = LR;
558 radii[SkRRect::kLowerLeft_Corner] = LL;
559 smallRR.setRectRadii(smallR, radii);
560
reed4dca7a82014-10-23 12:42:46 -0700561 const SkScalar sigma = this->computeXformedSigma(matrix);
reedb0df8be2015-02-04 09:07:17 -0800562 SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle,
563 this->getQuality(), smallRR);
564 if (!cache) {
reed4dca7a82014-10-23 12:42:46 -0700565 bool analyticBlurWorked = false;
566 if (c_analyticBlurRRect) {
567 analyticBlurWorked =
568 this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin,
569 SkMask::kComputeBoundsAndRenderImage_CreateMode);
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000570 }
robertphillips@google.comad993582013-11-11 18:45:18 +0000571
reed4dca7a82014-10-23 12:42:46 -0700572 if (!analyticBlurWorked) {
573 if (!draw_rrect_into_mask(smallRR, &srcM)) {
574 return kFalse_FilterReturn;
575 }
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000576
reed4dca7a82014-10-23 12:42:46 -0700577 SkAutoMaskFreeImage amf(srcM.fImage);
578
579 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
580 return kFalse_FilterReturn;
581 }
commit-bot@chromium.orga4771542014-03-10 21:42:06 +0000582 }
reedb0df8be2015-02-04 09:07:17 -0800583 cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallRR);
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000584 }
585
586 patch->fMask.fBounds.offsetTo(0, 0);
587 patch->fOuterRect = dstM.fBounds;
588 patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
589 patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700590 SkASSERT(nullptr == patch->fCache);
reedb0df8be2015-02-04 09:07:17 -0800591 patch->fCache = cache; // transfer ownership to patch
scroggo@google.coma8e33a92013-11-08 18:02:53 +0000592 return kTrue_FilterReturn;
593}
594
halcanary4e44efe2016-08-04 10:47:16 -0700595// Use the faster analytic blur approach for ninepatch rects
596static const bool c_analyticBlurNinepatch{true};
humper@google.com7c5d7b72013-03-11 20:16:28 +0000597
reed@google.comd729b3e2012-11-09 14:30:48 +0000598SkMaskFilter::FilterReturn
reed@google.comdab9b4f2012-11-19 16:45:14 +0000599SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
600 const SkMatrix& matrix,
601 const SkIRect& clipBounds,
reed@google.com30711b72012-12-18 19:18:39 +0000602 NinePatch* patch) const {
reed@google.comdab9b4f2012-11-19 16:45:14 +0000603 if (count < 1 || count > 2) {
604 return kUnimplemented_FilterReturn;
605 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000606
reed@google.com57850b92012-12-17 21:20:53 +0000607 // TODO: report correct metrics for innerstyle, where we do not grow the
608 // total bounds, but we do need an inset the size of our blur-radius
commit-bot@chromium.orge3964552014-04-28 16:25:35 +0000609 if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
reed@google.com57850b92012-12-17 21:20:53 +0000610 return kUnimplemented_FilterReturn;
611 }
612
reed@google.com07784a02012-11-19 21:09:14 +0000613 // TODO: take clipBounds into account to limit our coordinates up front
614 // for now, just skip too-large src rects (to take the old code path).
reed@google.comf276fa72012-11-21 14:14:10 +0000615 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
reed@google.com07784a02012-11-19 21:09:14 +0000616 return kUnimplemented_FilterReturn;
617 }
skia.committer@gmail.com34587162012-11-20 02:01:23 +0000618
reed@google.comd729b3e2012-11-09 14:30:48 +0000619 SkIPoint margin;
620 SkMask srcM, dstM;
reedb07a94f2014-11-19 05:03:18 -0800621 srcM.fBounds = rects[0].roundOut();
reed@google.comd729b3e2012-11-09 14:30:48 +0000622 srcM.fFormat = SkMask::kA8_Format;
623 srcM.fRowBytes = 0;
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000624
humper@google.com7c5d7b72013-03-11 20:16:28 +0000625 bool filterResult = false;
626 if (count == 1 && c_analyticBlurNinepatch) {
627 // special case for fast rect blur
628 // don't actually do the blur the first time, just compute the correct size
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000629 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
humper@google.com7c5d7b72013-03-11 20:16:28 +0000630 SkMask::kJustComputeBounds_CreateMode);
631 } else {
632 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
633 }
skia.committer@gmail.com2e71f162013-03-12 07:12:32 +0000634
humper@google.com7c5d7b72013-03-11 20:16:28 +0000635 if (!filterResult) {
reed@google.comd729b3e2012-11-09 14:30:48 +0000636 return kFalse_FilterReturn;
637 }
638
639 /*
640 * smallR is the smallest version of 'rect' that will still guarantee that
641 * we get the same blur results on all edges, plus 1 center row/col that is
642 * representative of the extendible/stretchable edges of the ninepatch.
643 * Since our actual edge may be fractional we inset 1 more to be sure we
644 * don't miss any interior blur.
645 * x is an added pixel of blur, and { and } are the (fractional) edge
646 * pixels from the original rect.
647 *
648 * x x { x x .... x x } x x
649 *
650 * Thus, in this case, we inset by a total of 5 (on each side) beginning
651 * with our outer-rect (dstM.fBounds)
652 */
reed@google.comdab9b4f2012-11-19 16:45:14 +0000653 SkRect smallR[2];
654 SkIPoint center;
655
656 // +2 is from +1 for each edge (to account for possible fractional edges
657 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
658 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
659 SkIRect innerIR;
660
661 if (1 == count) {
662 innerIR = srcM.fBounds;
663 center.set(smallW, smallH);
664 } else {
665 SkASSERT(2 == count);
666 rects[1].roundIn(&innerIR);
667 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
668 smallH + (innerIR.top() - srcM.fBounds.top()));
reed@google.comd729b3e2012-11-09 14:30:48 +0000669 }
670
reed@google.comdab9b4f2012-11-19 16:45:14 +0000671 // +1 so we get a clean, stretchable, center row/col
672 smallW += 1;
673 smallH += 1;
674
675 // we want the inset amounts to be integral, so we don't change any
676 // fractional phase on the fRight or fBottom of our smallR.
677 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
678 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
679 if (dx < 0 || dy < 0) {
680 // we're too small, relative to our blur, to break into nine-patch,
681 // so we ask to have our normal filterMask() be called.
682 return kUnimplemented_FilterReturn;
683 }
684
685 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 +0000686 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
687 return kUnimplemented_FilterReturn;
688 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000689 if (2 == count) {
690 smallR[1].set(rects[1].left(), rects[1].top(),
691 rects[1].right() - dx, rects[1].bottom() - dy);
692 SkASSERT(!smallR[1].isEmpty());
693 }
694
reed4dca7a82014-10-23 12:42:46 -0700695 const SkScalar sigma = this->computeXformedSigma(matrix);
reedb0df8be2015-02-04 09:07:17 -0800696 SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle,
697 this->getQuality(), smallR, count);
698 if (!cache) {
reed4dca7a82014-10-23 12:42:46 -0700699 if (count > 1 || !c_analyticBlurNinepatch) {
700 if (!draw_rects_into_mask(smallR, count, &srcM)) {
701 return kFalse_FilterReturn;
702 }
reed@google.comd729b3e2012-11-09 14:30:48 +0000703
reed4dca7a82014-10-23 12:42:46 -0700704 SkAutoMaskFreeImage amf(srcM.fImage);
reed@google.comd198a5b2012-11-27 15:18:04 +0000705
reed4dca7a82014-10-23 12:42:46 -0700706 if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
707 return kFalse_FilterReturn;
708 }
709 } else {
710 if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
711 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
712 return kFalse_FilterReturn;
713 }
humper@google.com7c5d7b72013-03-11 20:16:28 +0000714 }
reedb0df8be2015-02-04 09:07:17 -0800715 cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, this->getQuality(), smallR, count);
reed@google.comd729b3e2012-11-09 14:30:48 +0000716 }
reed@google.comdab9b4f2012-11-19 16:45:14 +0000717 patch->fMask.fBounds.offsetTo(0, 0);
718 patch->fOuterRect = dstM.fBounds;
719 patch->fCenter = center;
halcanary96fcdcc2015-08-27 07:41:13 -0700720 SkASSERT(nullptr == patch->fCache);
reedb0df8be2015-02-04 09:07:17 -0800721 patch->fCache = cache; // transfer ownership to patch
reed@google.comd729b3e2012-11-09 14:30:48 +0000722 return kTrue_FilterReturn;
723}
724
reed@google.com30711b72012-12-18 19:18:39 +0000725void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
726 SkRect* dst) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +0000727 SkScalar pad = 3.0f * fSigma;
robertphillips@google.com17ad2bd2013-07-30 12:15:19 +0000728
729 dst->set(src.fLeft - pad, src.fTop - pad,
730 src.fRight + pad, src.fBottom + pad);
reed@google.com9efd9a02012-01-30 15:41:43 +0000731}
732
reed60c9b582016-04-03 09:11:13 -0700733sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -0700734 const SkScalar sigma = buffer.readScalar();
735 const unsigned style = buffer.readUInt();
Robert Phillips98624d22016-12-19 11:37:37 -0500736 unsigned flags = buffer.readUInt();
737
738 buffer.validate(style <= kLastEnum_SkBlurStyle);
739 buffer.validate(!(flags & ~SkBlurMaskFilter::kAll_BlurFlag));
740
741 flags &= SkBlurMaskFilter::kAll_BlurFlag;
robertphillips27cdd942016-08-10 16:25:25 -0700742
743 SkRect occluder;
744 if (buffer.isVersionLT(SkReadBuffer::kBlurMaskFilterWritesOccluder)) {
745 occluder.setEmpty();
746 } else {
747 buffer.readRect(&occluder);
748 }
749
reed9fa60da2014-08-21 07:59:51 -0700750 if (style <= kLastEnum_SkBlurStyle) {
robertphillips27cdd942016-08-10 16:25:25 -0700751 return SkBlurMaskFilter::Make((SkBlurStyle)style, sigma, occluder, flags);
reed9fa60da2014-08-21 07:59:51 -0700752 }
halcanary96fcdcc2015-08-27 07:41:13 -0700753 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700754}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000756void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
robertphillips@google.com11e05552013-12-03 19:46:58 +0000757 buffer.writeScalar(fSigma);
reed9fa60da2014-08-21 07:59:51 -0700758 buffer.writeUInt(fBlurStyle);
djsollen@google.comc73dd5c2012-08-07 15:54:32 +0000759 buffer.writeUInt(fBlurFlags);
robertphillips27cdd942016-08-10 16:25:25 -0700760 buffer.writeRect(fOccluder);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761}
762
robertphillipsf5a83e82016-08-10 12:00:09 -0700763
robertphillips@google.com49149312013-07-03 15:34:35 +0000764#if SK_SUPPORT_GPU
reed@google.com2b75f422011-07-07 13:43:38 +0000765
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000766class GrGLRectBlurEffect;
767
joshualittb0a8a372014-09-23 09:50:21 -0700768class GrRectBlurEffect : public GrFragmentProcessor {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000769public:
robertphillips30c4cae2015-09-15 10:20:55 -0700770 ~GrRectBlurEffect() override { }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000771
mtklein36352bf2015-03-25 18:17:31 -0700772 const char* name() const override { return "RectBlur"; }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000773
bungeman06ca8ec2016-06-09 08:01:03 -0700774 static sk_sp<GrFragmentProcessor> Make(GrTextureProvider *textureProvider,
775 const SkRect& rect, float sigma) {
humper4a24cd82014-06-17 13:39:29 -0700776 int doubleProfileSize = SkScalarCeilToInt(12*sigma);
777
778 if (doubleProfileSize >= rect.width() || doubleProfileSize >= rect.height()) {
779 // if the blur sigma is too large so the gaussian overlaps the whole
780 // rect in either direction, fall back to CPU path for now.
halcanary96fcdcc2015-08-27 07:41:13 -0700781 return nullptr;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000782 }
humper4a24cd82014-06-17 13:39:29 -0700783
Hal Canary67b39de2016-11-07 11:47:44 -0500784 sk_sp<GrTexture> blurProfile(CreateBlurProfileTexture(textureProvider, sigma));
robertphillips30c4cae2015-09-15 10:20:55 -0700785 if (!blurProfile) {
halcanary96fcdcc2015-08-27 07:41:13 -0700786 return nullptr;
humper4a24cd82014-06-17 13:39:29 -0700787 }
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700788 // in OpenGL ES, mediump floats have a minimum range of 2^14. If we have coordinates bigger
789 // than that, the shader math will end up with infinities and result in the blur effect not
790 // working correctly. To avoid this, we switch into highp when the coordinates are too big.
halcanary9d524f22016-03-29 09:03:52 -0700791 // As 2^14 is the minimum range but the actual range can be bigger, we might end up
792 // switching to highp sooner than strictly necessary, but most devices that have a bigger
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700793 // range for mediump also have mediump being exactly the same as highp (e.g. all non-OpenGL
794 // ES devices), and thus incur no additional penalty for the switch.
795 static const SkScalar kMAX_BLUR_COORD = SkIntToScalar(16000);
796 GrSLPrecision precision;
797 if (SkScalarAbs(rect.top()) > kMAX_BLUR_COORD ||
798 SkScalarAbs(rect.left()) > kMAX_BLUR_COORD ||
799 SkScalarAbs(rect.bottom()) > kMAX_BLUR_COORD ||
800 SkScalarAbs(rect.right()) > kMAX_BLUR_COORD ||
801 SkScalarAbs(rect.width()) > kMAX_BLUR_COORD ||
802 SkScalarAbs(rect.height()) > kMAX_BLUR_COORD) {
803 precision = kHigh_GrSLPrecision;
bungeman06ca8ec2016-06-09 08:01:03 -0700804 } else {
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700805 precision = kDefault_GrSLPrecision;
806 }
Hal Canary67b39de2016-11-07 11:47:44 -0500807
bungeman06ca8ec2016-06-09 08:01:03 -0700808 return sk_sp<GrFragmentProcessor>(
Hal Canary67b39de2016-11-07 11:47:44 -0500809 new GrRectBlurEffect(rect, sigma, blurProfile.get(), precision));
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000810 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000811
humper4a24cd82014-06-17 13:39:29 -0700812 const SkRect& getRect() const { return fRect; }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000813 float getSigma() const { return fSigma; }
robertphillipsbf536af2016-02-04 06:11:53 -0800814 GrSLPrecision precision() const { return fPrecision; }
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000815
816private:
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700817 GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blurProfile,
818 GrSLPrecision fPrecision);
robertphillips30c4cae2015-09-15 10:20:55 -0700819
egdaniel57d3b032015-11-13 11:57:27 -0800820 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -0700821
Brian Salomon94efbf52016-11-29 13:43:05 -0500822 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -0700823
mtklein36352bf2015-03-25 18:17:31 -0700824 bool onIsEqual(const GrFragmentProcessor&) const override;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000825
mtklein36352bf2015-03-25 18:17:31 -0700826 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
egdaniel1a8ecdf2014-10-03 06:24:12 -0700827
robertphillips30c4cae2015-09-15 10:20:55 -0700828 static GrTexture* CreateBlurProfileTexture(GrTextureProvider*, float sigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000829
humper4a24cd82014-06-17 13:39:29 -0700830 SkRect fRect;
831 float fSigma;
Brian Salomon0bbecb22016-11-17 11:38:22 -0500832 TextureSampler fBlurProfileSampler;
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700833 GrSLPrecision fPrecision;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000834
joshualittb0a8a372014-09-23 09:50:21 -0700835 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000836
joshualittb0a8a372014-09-23 09:50:21 -0700837 typedef GrFragmentProcessor INHERITED;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000838};
839
egdaniel64c47282015-11-13 06:54:19 -0800840class GrGLRectBlurEffect : public GrGLSLFragmentProcessor {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000841public:
robertphillips30c4cae2015-09-15 10:20:55 -0700842 void emitCode(EmitArgs&) override;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000843
Brian Salomon94efbf52016-11-29 13:43:05 -0500844 static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b);
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700845
wangyixb1daa862015-08-18 11:29:31 -0700846protected:
egdaniel018fb622015-10-28 07:26:40 -0700847 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000848
849private:
egdaniel018fb622015-10-28 07:26:40 -0700850 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000851
humper4a24cd82014-06-17 13:39:29 -0700852 UniformHandle fProxyRectUniform;
853 UniformHandle fProfileSizeUniform;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000854
egdaniel64c47282015-11-13 06:54:19 -0800855 typedef GrGLSLFragmentProcessor INHERITED;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000856};
857
cdalton85285412016-02-18 12:37:07 -0800858void OutputRectBlurProfileLookup(GrGLSLFPFragmentBuilder* fragBuilder,
egdaniel09aa1fc2016-04-20 07:09:46 -0700859 GrGLSLFragmentProcessor::SamplerHandle sampler,
humper4a24cd82014-06-17 13:39:29 -0700860 const char *output,
861 const char *profileSize, const char *loc,
862 const char *blurred_width,
863 const char *sharp_width) {
egdaniel4ca2e602015-11-18 08:01:26 -0800864 fragBuilder->codeAppendf("float %s;", output);
865 fragBuilder->codeAppendf("{");
866 fragBuilder->codeAppendf("float coord = ((abs(%s - 0.5 * %s) - 0.5 * %s)) / %s;",
humper4a24cd82014-06-17 13:39:29 -0700867 loc, blurred_width, sharp_width, profileSize);
egdaniel4ca2e602015-11-18 08:01:26 -0800868 fragBuilder->codeAppendf("%s = ", output);
869 fragBuilder->appendTextureLookup(sampler, "vec2(coord,0.5)");
870 fragBuilder->codeAppend(".a;");
871 fragBuilder->codeAppendf("}");
humper4a24cd82014-06-17 13:39:29 -0700872}
873
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700874
Brian Salomon94efbf52016-11-29 13:43:05 -0500875void GrGLRectBlurEffect::GenKey(const GrProcessor& proc, const GrShaderCaps&,
robertphillipsbf536af2016-02-04 06:11:53 -0800876 GrProcessorKeyBuilder* b) {
877 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
878
879 b->add32(rbe.precision());
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700880}
881
882
wangyix7c157a92015-07-22 15:08:53 -0700883void GrGLRectBlurEffect::emitCode(EmitArgs& args) {
robertphillipsbf536af2016-02-04 06:11:53 -0800884 const GrRectBlurEffect& rbe = args.fFp.cast<GrRectBlurEffect>();
885
egdaniel7ea439b2015-12-03 09:20:44 -0800886 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000887
humper4a24cd82014-06-17 13:39:29 -0700888 const char *rectName;
889 const char *profileSizeName;
890
cdalton1f50acf2016-04-11 11:30:50 -0700891 SkString precisionString;
Brian Salomon1edc5b92016-11-29 13:43:46 -0500892 if (args.fShaderCaps->usesPrecisionModifiers()) {
cdalton1f50acf2016-04-11 11:30:50 -0700893 precisionString.printf("%s ", GrGLSLPrecisionString(rbe.precision()));
894 }
cdalton5e58cee2016-02-11 12:49:47 -0800895 fProxyRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800896 kVec4f_GrSLType,
robertphillipsbf536af2016-02-04 06:11:53 -0800897 rbe.precision(),
egdaniel7ea439b2015-12-03 09:20:44 -0800898 "proxyRect",
899 &rectName);
cdalton5e58cee2016-02-11 12:49:47 -0800900 fProfileSizeUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -0800901 kFloat_GrSLType,
902 kDefault_GrSLPrecision,
903 "profileSize",
904 &profileSizeName);
humper4a24cd82014-06-17 13:39:29 -0700905
cdalton85285412016-02-18 12:37:07 -0800906 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000907
wangyix7c157a92015-07-22 15:08:53 -0700908 if (args.fInputColor) {
egdaniel4ca2e602015-11-18 08:01:26 -0800909 fragBuilder->codeAppendf("vec4 src=%s;", args.fInputColor);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000910 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800911 fragBuilder->codeAppendf("vec4 src=vec4(1);");
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000912 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000913
Ethan Nicholasde4d3012017-01-19 16:58:02 -0500914 fragBuilder->codeAppendf("%s vec2 translatedPos = sk_FragCoord.xy - %s.xy;",
915 precisionString.c_str(), rectName);
cdalton1f50acf2016-04-11 11:30:50 -0700916 fragBuilder->codeAppendf("%s float width = %s.z - %s.x;", precisionString.c_str(), rectName,
egdaniel4ca2e602015-11-18 08:01:26 -0800917 rectName);
cdalton1f50acf2016-04-11 11:30:50 -0700918 fragBuilder->codeAppendf("%s float height = %s.w - %s.y;", precisionString.c_str(), rectName,
919 rectName);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000920
cdalton1f50acf2016-04-11 11:30:50 -0700921 fragBuilder->codeAppendf("%s vec2 smallDims = vec2(width - %s, height - %s);",
922 precisionString.c_str(), profileSizeName, profileSizeName);
923 fragBuilder->codeAppendf("%s float center = 2.0 * floor(%s/2.0 + .25) - 1.0;",
924 precisionString.c_str(), profileSizeName);
925 fragBuilder->codeAppendf("%s vec2 wh = smallDims - vec2(center,center);",
926 precisionString.c_str());
humper4a24cd82014-06-17 13:39:29 -0700927
cdalton3f6f76f2016-04-11 12:18:09 -0700928 OutputRectBlurProfileLookup(fragBuilder, args.fTexSamplers[0], "horiz_lookup", profileSizeName,
wangyix7c157a92015-07-22 15:08:53 -0700929 "translatedPos.x", "width", "wh.x");
cdalton3f6f76f2016-04-11 12:18:09 -0700930 OutputRectBlurProfileLookup(fragBuilder, args.fTexSamplers[0], "vert_lookup", profileSizeName,
wangyix7c157a92015-07-22 15:08:53 -0700931 "translatedPos.y", "height", "wh.y");
humper4a24cd82014-06-17 13:39:29 -0700932
egdaniel4ca2e602015-11-18 08:01:26 -0800933 fragBuilder->codeAppendf("float final = horiz_lookup * vert_lookup;");
934 fragBuilder->codeAppendf("%s = src * final;", args.fOutputColor);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000935}
936
egdaniel018fb622015-10-28 07:26:40 -0700937void GrGLRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
robertphillips30c4cae2015-09-15 10:20:55 -0700938 const GrProcessor& proc) {
joshualittb0a8a372014-09-23 09:50:21 -0700939 const GrRectBlurEffect& rbe = proc.cast<GrRectBlurEffect>();
humper4a24cd82014-06-17 13:39:29 -0700940 SkRect rect = rbe.getRect();
941
kkinnunen7510b222014-07-30 00:04:16 -0700942 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
943 pdman.set1f(fProfileSizeUniform, SkScalarCeilToScalar(6*rbe.getSigma()));
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000944}
945
robertphillips30c4cae2015-09-15 10:20:55 -0700946GrTexture* GrRectBlurEffect::CreateBlurProfileTexture(GrTextureProvider* textureProvider,
947 float sigma) {
bsalomonf2703d82014-10-28 14:33:06 -0700948 GrSurfaceDesc texDesc;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000949
bsalomon24db3b12015-01-23 04:24:04 -0800950 unsigned int profileSize = SkScalarCeilToInt(6*sigma);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000951
bsalomon24db3b12015-01-23 04:24:04 -0800952 texDesc.fWidth = profileSize;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000953 texDesc.fHeight = 1;
954 texDesc.fConfig = kAlpha_8_GrPixelConfig;
cblume55f2d2d2016-02-26 13:20:48 -0800955 texDesc.fIsMipMapped = false;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000956
bsalomon8718aaf2015-02-19 07:24:21 -0800957 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
958 GrUniqueKey key;
959 GrUniqueKey::Builder builder(&key, kDomain, 1);
bsalomon24db3b12015-01-23 04:24:04 -0800960 builder[0] = profileSize;
961 builder.finish();
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000962
robertphillips30c4cae2015-09-15 10:20:55 -0700963 GrTexture *blurProfile = textureProvider->findAndRefTextureByUniqueKey(key);
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000964
robertphillips30c4cae2015-09-15 10:20:55 -0700965 if (!blurProfile) {
Ben Wagner7ecc5962016-11-02 17:07:33 -0400966 std::unique_ptr<uint8_t[]> profile(SkBlurMask::ComputeBlurProfile(sigma));
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000967
bsalomon5ec26ae2016-02-25 08:33:02 -0800968 blurProfile = textureProvider->createTexture(texDesc, SkBudgeted::kYes, profile.get(), 0);
robertphillips30c4cae2015-09-15 10:20:55 -0700969 if (blurProfile) {
970 textureProvider->assignUniqueKeyToTexture(key, blurProfile);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000971 }
972 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +0000973
robertphillips30c4cae2015-09-15 10:20:55 -0700974 return blurProfile;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000975}
976
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700977GrRectBlurEffect::GrRectBlurEffect(const SkRect& rect, float sigma, GrTexture *blurProfile,
978 GrSLPrecision precision)
robertphillips30c4cae2015-09-15 10:20:55 -0700979 : fRect(rect)
980 , fSigma(sigma)
Brian Salomon0bbecb22016-11-17 11:38:22 -0500981 , fBlurProfileSampler(blurProfile)
ethannicholasa8e5fbd2015-10-19 08:38:16 -0700982 , fPrecision(precision) {
joshualitteb2a6762014-12-04 11:35:33 -0800983 this->initClassID<GrRectBlurEffect>();
Brian Salomon0bbecb22016-11-17 11:38:22 -0500984 this->addTextureSampler(&fBlurProfileSampler);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000985}
986
Brian Salomon94efbf52016-11-29 13:43:05 -0500987void GrRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -0800988 GrProcessorKeyBuilder* b) const {
robertphillipsbf536af2016-02-04 06:11:53 -0800989 GrGLRectBlurEffect::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800990}
991
egdaniel57d3b032015-11-13 11:57:27 -0800992GrGLSLFragmentProcessor* GrRectBlurEffect::onCreateGLSLInstance() const {
robertphillipsbf536af2016-02-04 06:11:53 -0800993 return new GrGLRectBlurEffect;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000994}
995
bsalomon0e08fc12014-10-15 08:19:04 -0700996bool GrRectBlurEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
joshualitt49586be2014-09-16 08:21:41 -0700997 const GrRectBlurEffect& s = sBase.cast<GrRectBlurEffect>();
humper4a24cd82014-06-17 13:39:29 -0700998 return this->getSigma() == s.getSigma() && this->getRect() == s.getRect();
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +0000999}
1000
egdaniel605dd0f2014-11-12 08:35:25 -08001001void GrRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
joshualitt56995b52014-12-11 15:44:02 -08001002 inout->mulByUnknownSingleComponent();
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001003}
1004
joshualittb0a8a372014-09-23 09:50:21 -07001005GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001006
bungeman06ca8ec2016-06-09 08:01:03 -07001007sk_sp<GrFragmentProcessor> GrRectBlurEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001008 float sigma = d->fRandom->nextRangeF(3,8);
1009 float width = d->fRandom->nextRangeF(200,300);
1010 float height = d->fRandom->nextRangeF(200,300);
bungeman06ca8ec2016-06-09 08:01:03 -07001011 return GrRectBlurEffect::Make(d->fContext->textureProvider(), SkRect::MakeWH(width, height),
1012 sigma);
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001013}
1014
robertphillipsff0ca5e2015-07-22 11:54:44 -07001015bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrTextureProvider* texProvider,
Brian Osman11052242016-10-27 14:47:55 -04001016 GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -05001017 GrPaint&& paint,
joshualitt570d2f82015-02-25 13:19:48 -08001018 const GrClip& clip,
joshualitt5531d512014-12-17 15:50:11 -08001019 const SkMatrix& viewMatrix,
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001020 const SkStrokeRec& strokeRec,
1021 const SkPath& path) const {
Brian Osman11052242016-10-27 14:47:55 -04001022 SkASSERT(renderTargetContext);
robertphillipsff0ca5e2015-07-22 11:54:44 -07001023
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001024 if (fBlurStyle != kNormal_SkBlurStyle) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001025 return false;
1026 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001027
robertphillips30c4cae2015-09-15 10:20:55 -07001028 // TODO: we could handle blurred stroked circles
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001029 if (!strokeRec.isFillStyle()) {
1030 return false;
1031 }
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001032
robertphillipsff0ca5e2015-07-22 11:54:44 -07001033 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
humper4a24cd82014-06-17 13:39:29 -07001034
bungeman06ca8ec2016-06-09 08:01:03 -07001035 sk_sp<GrFragmentProcessor> fp;
skia.committer@gmail.com4c18e9f2014-01-31 03:01:59 +00001036
robertphillips30c4cae2015-09-15 10:20:55 -07001037 SkRect rect;
1038 if (path.isRect(&rect)) {
robertphillips3a0c3692016-08-22 11:48:44 -07001039 SkScalar pad = 3.0f * xformedSigma;
1040 rect.outset(pad, pad);
robertphillips30c4cae2015-09-15 10:20:55 -07001041
bungeman06ca8ec2016-06-09 08:01:03 -07001042 fp = GrRectBlurEffect::Make(texProvider, rect, xformedSigma);
robertphillips30c4cae2015-09-15 10:20:55 -07001043 } else if (path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height())) {
bungeman06ca8ec2016-06-09 08:01:03 -07001044 fp = GrCircleBlurFragmentProcessor::Make(texProvider, rect, xformedSigma);
robertphillips30c4cae2015-09-15 10:20:55 -07001045
1046 // expand the rect for the coverage geometry
1047 int pad = SkScalarCeilToInt(6*xformedSigma)/2;
1048 rect.outset(SkIntToScalar(pad), SkIntToScalar(pad));
1049 } else {
1050 return false;
1051 }
1052
joshualittb0a8a372014-09-23 09:50:21 -07001053 if (!fp) {
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001054 return false;
1055 }
1056
joshualittc2625822014-12-18 16:40:54 -08001057 SkMatrix inverse;
1058 if (!viewMatrix.invert(&inverse)) {
1059 return false;
1060 }
robertphillipsea461502015-05-26 11:38:03 -07001061
Brian Salomon82f44312017-01-11 13:42:54 -05001062 paint.addCoverageFragmentProcessor(std::move(fp));
1063 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
1064 rect, inverse);
robertphillipsff0ca5e2015-07-22 11:54:44 -07001065 return true;
commit-bot@chromium.orgcf34bc02014-01-30 15:34:43 +00001066}
1067
robertphillips30c4cae2015-09-15 10:20:55 -07001068//////////////////////////////////////////////////////////////////////////////
1069
joshualittb0a8a372014-09-23 09:50:21 -07001070class GrRRectBlurEffect : public GrFragmentProcessor {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001071public:
1072
robertphillipsc4d2f902016-08-16 09:30:03 -07001073 static sk_sp<GrFragmentProcessor> Make(GrContext*,
1074 float sigma, float xformedSigma,
1075 const SkRRect& srcRRect, const SkRRect& devRRect);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001076
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001077 virtual ~GrRRectBlurEffect() {}
mtklein36352bf2015-03-25 18:17:31 -07001078 const char* name() const override { return "GrRRectBlur"; }
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001079
1080 const SkRRect& getRRect() const { return fRRect; }
1081 float getSigma() const { return fSigma; }
1082
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001083private:
egdaniel57d3b032015-11-13 11:57:27 -08001084 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
wangyixb1daa862015-08-18 11:29:31 -07001085
robertphillips287e7cb2016-08-16 15:49:20 -07001086 GrRRectBlurEffect(float sigma, const SkRRect&, GrTexture* profileTexture);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001087
Brian Salomon94efbf52016-11-29 13:43:05 -05001088 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -08001089 GrProcessorKeyBuilder* b) const override;
wangyix4b3050b2015-08-04 07:59:37 -07001090
mtklein36352bf2015-03-25 18:17:31 -07001091 bool onIsEqual(const GrFragmentProcessor& other) const override;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001092
mtklein36352bf2015-03-25 18:17:31 -07001093 void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
egdaniel1a8ecdf2014-10-03 06:24:12 -07001094
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001095 SkRRect fRRect;
1096 float fSigma;
Brian Salomon0bbecb22016-11-17 11:38:22 -05001097 TextureSampler fNinePatchSampler;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001098
joshualittb0a8a372014-09-23 09:50:21 -07001099 GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001100
joshualittb0a8a372014-09-23 09:50:21 -07001101 typedef GrFragmentProcessor INHERITED;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001102};
1103
robertphillipsd39430d2016-08-15 12:37:00 -07001104static sk_sp<GrTexture> find_or_create_rrect_blur_mask(GrContext* context,
1105 const SkRRect& rrectToDraw,
1106 const SkISize& size,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -05001107 float xformedSigma) {
robertphillipsd39430d2016-08-15 12:37:00 -07001108 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
1109 GrUniqueKey key;
1110 GrUniqueKey::Builder builder(&key, kDomain, 9);
1111 builder[0] = SkScalarCeilToInt(xformedSigma-1/6.0f);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001112
robertphillipsd39430d2016-08-15 12:37:00 -07001113 int index = 1;
1114 for (auto c : { SkRRect::kUpperLeft_Corner, SkRRect::kUpperRight_Corner,
1115 SkRRect::kLowerRight_Corner, SkRRect::kLowerLeft_Corner }) {
1116 SkASSERT(SkScalarIsInt(rrectToDraw.radii(c).fX) && SkScalarIsInt(rrectToDraw.radii(c).fY));
1117 builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fX);
1118 builder[index++] = SkScalarCeilToInt(rrectToDraw.radii(c).fY);
robertphillipsf5a83e82016-08-10 12:00:09 -07001119 }
robertphillipsd39430d2016-08-15 12:37:00 -07001120 builder.finish();
1121
1122 sk_sp<GrTexture> mask(context->textureProvider()->findAndRefTextureByUniqueKey(key));
1123 if (!mask) {
1124 // TODO: this could be approx but the texture coords will need to be updated
Robert Phillips55360b12016-12-05 13:18:47 +00001125 sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContextWithFallback(
Brian Osman11052242016-10-27 14:47:55 -04001126 SkBackingFit::kExact, size.fWidth, size.fHeight, kAlpha_8_GrPixelConfig, nullptr));
1127 if (!rtc) {
robertphillipsd39430d2016-08-15 12:37:00 -07001128 return nullptr;
1129 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001130
Brian Salomon82f44312017-01-11 13:42:54 -05001131 GrPaint paint;
robertphillipsf5a83e82016-08-10 12:00:09 -07001132
Brian Osman11052242016-10-27 14:47:55 -04001133 rtc->clear(nullptr, 0x0, true);
Brian Salomon82f44312017-01-11 13:42:54 -05001134 rtc->drawRRect(GrNoClip(), std::move(paint), GrAA::kYes, SkMatrix::I(), rrectToDraw,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -05001135 GrStyle::SimpleFill());
robertphillipsf5a83e82016-08-10 12:00:09 -07001136
Brian Osman11052242016-10-27 14:47:55 -04001137 sk_sp<GrTexture> srcTexture(rtc->asTexture());
Robert Phillips833dcf42016-11-18 08:44:13 -05001138 if (!srcTexture) {
1139 return nullptr;
1140 }
Brian Osman11052242016-10-27 14:47:55 -04001141 sk_sp<GrRenderTargetContext> rtc2(SkGpuBlurUtils::GaussianBlur(context,
1142 srcTexture.get(),
1143 nullptr,
1144 SkIRect::MakeWH(
robertphillipsd728f0c2016-11-21 11:05:03 -08001145 size.fWidth,
1146 size.fHeight),
Brian Osman11052242016-10-27 14:47:55 -04001147 nullptr,
1148 xformedSigma, xformedSigma,
1149 SkBackingFit::kExact));
1150 if (!rtc2) {
robertphillipsd39430d2016-08-15 12:37:00 -07001151 return nullptr;
1152 }
1153
Brian Osman11052242016-10-27 14:47:55 -04001154 mask = rtc2->asTexture();
robertphillipsd728f0c2016-11-21 11:05:03 -08001155 if (!mask) {
1156 return nullptr;
1157 }
robertphillipsd39430d2016-08-15 12:37:00 -07001158 context->textureProvider()->assignUniqueKeyToTexture(key, mask.get());
robertphillips94b5c5a2016-08-10 07:14:55 -07001159 }
1160
robertphillipsd39430d2016-08-15 12:37:00 -07001161 return mask;
robertphillipsf5a83e82016-08-10 12:00:09 -07001162}
1163
robertphillips0dafbde2016-08-15 09:04:45 -07001164sk_sp<GrFragmentProcessor> GrRRectBlurEffect::Make(GrContext* context,
robertphillipsc4d2f902016-08-16 09:30:03 -07001165 float sigma, float xformedSigma,
1166 const SkRRect& srcRRect, const SkRRect& devRRect) {
robertphillips6cfb1062016-08-22 16:13:48 -07001167 SkASSERT(!devRRect.isCircle() && !devRRect.isRect()); // Should've been caught up-stream
robertphillipsf5a83e82016-08-10 12:00:09 -07001168
1169 // TODO: loosen this up
robertphillips0dafbde2016-08-15 09:04:45 -07001170 if (!devRRect.isSimpleCircular()) {
halcanary96fcdcc2015-08-27 07:41:13 -07001171 return nullptr;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001172 }
1173
1174 // Make sure we can successfully ninepatch this rrect -- the blur sigma has to be
1175 // sufficiently small relative to both the size of the corner radius and the
1176 // width (and height) of the rrect.
robertphillipsd39430d2016-08-15 12:37:00 -07001177 SkRRect rrectToDraw;
1178 SkISize size;
robertphillipsc4d2f902016-08-16 09:30:03 -07001179 SkScalar ignored[SkBlurMaskFilter::kMaxDivisions];
robertphillipsd39430d2016-08-15 12:37:00 -07001180 int ignoredSize;
robertphillipsc4d2f902016-08-16 09:30:03 -07001181 uint32_t ignored32;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001182
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001183 bool ninePatchable = SkBlurMaskFilter::ComputeBlurredRRectParams(srcRRect, devRRect,
robertphillipsc4d2f902016-08-16 09:30:03 -07001184 SkRect::MakeEmpty(),
1185 sigma, xformedSigma,
robertphillipsd39430d2016-08-15 12:37:00 -07001186 &rrectToDraw, &size,
robertphillipsc4d2f902016-08-16 09:30:03 -07001187 ignored, ignored,
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001188 ignored, ignored,
1189 &ignoredSize, &ignoredSize,
robertphillipsc4d2f902016-08-16 09:30:03 -07001190 &ignored32);
robertphillipsd39430d2016-08-15 12:37:00 -07001191 if (!ninePatchable) {
halcanary96fcdcc2015-08-27 07:41:13 -07001192 return nullptr;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001193 }
1194
Brian Salomon0e8fc8b2016-12-09 15:10:07 -05001195 sk_sp<GrTexture> mask(find_or_create_rrect_blur_mask(context, rrectToDraw, size, xformedSigma));
robertphillipsd39430d2016-08-15 12:37:00 -07001196 if (!mask) {
1197 return nullptr;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001198 }
robertphillipsd39430d2016-08-15 12:37:00 -07001199
robertphillips287e7cb2016-08-16 15:49:20 -07001200 return sk_sp<GrFragmentProcessor>(new GrRRectBlurEffect(xformedSigma,
1201 devRRect,
1202 mask.get()));
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001203}
1204
egdaniel605dd0f2014-11-12 08:35:25 -08001205void GrRRectBlurEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
joshualitt56995b52014-12-11 15:44:02 -08001206 inout->mulByUnknownSingleComponent();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001207}
1208
robertphillips287e7cb2016-08-16 15:49:20 -07001209GrRRectBlurEffect::GrRRectBlurEffect(float sigma, const SkRRect& rrect, GrTexture *ninePatchTexture)
1210 : fRRect(rrect),
1211 fSigma(sigma),
Brian Salomon0bbecb22016-11-17 11:38:22 -05001212 fNinePatchSampler(ninePatchTexture) {
joshualitteb2a6762014-12-04 11:35:33 -08001213 this->initClassID<GrRRectBlurEffect>();
Brian Salomon0bbecb22016-11-17 11:38:22 -05001214 this->addTextureSampler(&fNinePatchSampler);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001215}
1216
bsalomon0e08fc12014-10-15 08:19:04 -07001217bool GrRRectBlurEffect::onIsEqual(const GrFragmentProcessor& other) const {
joshualitt49586be2014-09-16 08:21:41 -07001218 const GrRRectBlurEffect& rrbe = other.cast<GrRRectBlurEffect>();
robertphillipse8517142016-02-26 08:51:25 -08001219 return fRRect.getSimpleRadii().fX == rrbe.fRRect.getSimpleRadii().fX &&
1220 fSigma == rrbe.fSigma &&
1221 fRRect.rect() == rrbe.fRRect.rect();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001222}
1223
1224//////////////////////////////////////////////////////////////////////////////
1225
joshualittb0a8a372014-09-23 09:50:21 -07001226GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001227
bungeman06ca8ec2016-06-09 08:01:03 -07001228sk_sp<GrFragmentProcessor> GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -07001229 SkScalar w = d->fRandom->nextRangeScalar(100.f, 1000.f);
1230 SkScalar h = d->fRandom->nextRangeScalar(100.f, 1000.f);
1231 SkScalar r = d->fRandom->nextRangeF(1.f, 9.f);
1232 SkScalar sigma = d->fRandom->nextRangeF(1.f,10.f);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001233 SkRRect rrect;
1234 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
robertphillipsc4d2f902016-08-16 09:30:03 -07001235 return GrRRectBlurEffect::Make(d->fContext, sigma, sigma, rrect, rrect);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001236}
1237
1238//////////////////////////////////////////////////////////////////////////////
1239
egdaniel64c47282015-11-13 06:54:19 -08001240class GrGLRRectBlurEffect : public GrGLSLFragmentProcessor {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001241public:
robertphillips9cdb9922016-02-03 12:25:40 -08001242 void emitCode(EmitArgs&) override;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001243
wangyixb1daa862015-08-18 11:29:31 -07001244protected:
egdaniel018fb622015-10-28 07:26:40 -07001245 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001246
1247private:
egdaniel018fb622015-10-28 07:26:40 -07001248 GrGLSLProgramDataManager::UniformHandle fProxyRectUniform;
1249 GrGLSLProgramDataManager::UniformHandle fCornerRadiusUniform;
1250 GrGLSLProgramDataManager::UniformHandle fBlurRadiusUniform;
egdaniel64c47282015-11-13 06:54:19 -08001251 typedef GrGLSLFragmentProcessor INHERITED;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001252};
1253
wangyix7c157a92015-07-22 15:08:53 -07001254void GrGLRRectBlurEffect::emitCode(EmitArgs& args) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001255 const char *rectName;
1256 const char *cornerRadiusName;
1257 const char *blurRadiusName;
1258
egdaniel7ea439b2015-12-03 09:20:44 -08001259 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001260 // The proxy rect has left, top, right, and bottom edges correspond to
1261 // components x, y, z, and w, respectively.
1262
cdalton5e58cee2016-02-11 12:49:47 -08001263 fProxyRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001264 kVec4f_GrSLType,
egdaniel2d721d32015-11-11 13:06:05 -08001265 kDefault_GrSLPrecision,
egdaniel7ea439b2015-12-03 09:20:44 -08001266 "proxyRect",
1267 &rectName);
cdalton5e58cee2016-02-11 12:49:47 -08001268 fCornerRadiusUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001269 kFloat_GrSLType,
1270 kDefault_GrSLPrecision,
1271 "cornerRadius",
1272 &cornerRadiusName);
cdalton5e58cee2016-02-11 12:49:47 -08001273 fBlurRadiusUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
egdaniel7ea439b2015-12-03 09:20:44 -08001274 kFloat_GrSLType,
1275 kDefault_GrSLPrecision,
1276 "blurRadius",
1277 &blurRadiusName);
joshualitt30ba4362014-08-21 20:18:45 -07001278
cdalton85285412016-02-18 12:37:07 -08001279 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001280
1281 // warp the fragment position to the appropriate part of the 9patch blur texture
1282
egdaniel4ca2e602015-11-18 08:01:26 -08001283 fragBuilder->codeAppendf("vec2 rectCenter = (%s.xy + %s.zw)/2.0;", rectName, rectName);
Ethan Nicholasde4d3012017-01-19 16:58:02 -05001284 fragBuilder->codeAppendf("vec2 translatedFragPos = sk_FragCoord.xy - %s.xy;", rectName);
egdaniel4ca2e602015-11-18 08:01:26 -08001285 fragBuilder->codeAppendf("float threshold = %s + 2.0*%s;", cornerRadiusName, blurRadiusName);
1286 fragBuilder->codeAppendf("vec2 middle = %s.zw - %s.xy - 2.0*threshold;", rectName, rectName);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001287
egdaniel4ca2e602015-11-18 08:01:26 -08001288 fragBuilder->codeAppendf(
1289 "if (translatedFragPos.x >= threshold && translatedFragPos.x < (middle.x+threshold)) {");
1290 fragBuilder->codeAppendf("translatedFragPos.x = threshold;\n");
1291 fragBuilder->codeAppendf("} else if (translatedFragPos.x >= (middle.x + threshold)) {");
1292 fragBuilder->codeAppendf("translatedFragPos.x -= middle.x - 1.0;");
1293 fragBuilder->codeAppendf("}");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001294
egdaniel4ca2e602015-11-18 08:01:26 -08001295 fragBuilder->codeAppendf(
1296 "if (translatedFragPos.y > threshold && translatedFragPos.y < (middle.y+threshold)) {");
1297 fragBuilder->codeAppendf("translatedFragPos.y = threshold;");
1298 fragBuilder->codeAppendf("} else if (translatedFragPos.y >= (middle.y + threshold)) {");
1299 fragBuilder->codeAppendf("translatedFragPos.y -= middle.y - 1.0;");
1300 fragBuilder->codeAppendf("}");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001301
egdaniel4ca2e602015-11-18 08:01:26 -08001302 fragBuilder->codeAppendf("vec2 proxyDims = vec2(2.0*threshold+1.0);");
1303 fragBuilder->codeAppendf("vec2 texCoord = translatedFragPos / proxyDims;");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001304
egdaniel4ca2e602015-11-18 08:01:26 -08001305 fragBuilder->codeAppendf("%s = ", args.fOutputColor);
cdalton3f6f76f2016-04-11 12:18:09 -07001306 fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fTexSamplers[0], "texCoord");
egdaniel4ca2e602015-11-18 08:01:26 -08001307 fragBuilder->codeAppend(";");
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001308}
1309
egdaniel018fb622015-10-28 07:26:40 -07001310void GrGLRRectBlurEffect::onSetData(const GrGLSLProgramDataManager& pdman,
1311 const GrProcessor& proc) {
joshualittb0a8a372014-09-23 09:50:21 -07001312 const GrRRectBlurEffect& brre = proc.cast<GrRRectBlurEffect>();
robertphillipse8517142016-02-26 08:51:25 -08001313 const SkRRect& rrect = brre.getRRect();
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001314
1315 float blurRadius = 3.f*SkScalarCeilToScalar(brre.getSigma()-1/6.0f);
kkinnunen7510b222014-07-30 00:04:16 -07001316 pdman.set1f(fBlurRadiusUniform, blurRadius);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001317
1318 SkRect rect = rrect.getBounds();
1319 rect.outset(blurRadius, blurRadius);
kkinnunen7510b222014-07-30 00:04:16 -07001320 pdman.set4f(fProxyRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001321
1322 SkScalar radius = 0;
1323 SkASSERT(rrect.isSimpleCircular() || rrect.isRect());
1324 radius = rrect.getSimpleRadii().fX;
kkinnunen7510b222014-07-30 00:04:16 -07001325 pdman.set1f(fCornerRadiusUniform, radius);
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001326}
1327
Brian Salomon94efbf52016-11-29 13:43:05 -05001328void GrRRectBlurEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -08001329 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -08001330 GrGLRRectBlurEffect::GenKey(*this, caps, b);
1331}
1332
egdaniel57d3b032015-11-13 11:57:27 -08001333GrGLSLFragmentProcessor* GrRRectBlurEffect::onCreateGLSLInstance() const {
robertphillips9cdb9922016-02-03 12:25:40 -08001334 return new GrGLRRectBlurEffect;
joshualitteb2a6762014-12-04 11:35:33 -08001335}
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001336
robertphillipsf5a83e82016-08-10 12:00:09 -07001337bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
Brian Osman11052242016-10-27 14:47:55 -04001338 GrRenderTargetContext* renderTargetContext,
Brian Salomon82f44312017-01-11 13:42:54 -05001339 GrPaint&& paint,
joshualitt570d2f82015-02-25 13:19:48 -08001340 const GrClip& clip,
joshualitt5531d512014-12-17 15:50:11 -08001341 const SkMatrix& viewMatrix,
commit-bot@chromium.org82139702014-03-10 22:53:20 +00001342 const SkStrokeRec& strokeRec,
robertphillips27cdd942016-08-10 16:25:25 -07001343 const SkRRect& srcRRect,
1344 const SkRRect& devRRect) const {
Brian Osman11052242016-10-27 14:47:55 -04001345 SkASSERT(renderTargetContext);
robertphillipsff0ca5e2015-07-22 11:54:44 -07001346
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001347 if (fBlurStyle != kNormal_SkBlurStyle) {
1348 return false;
1349 }
1350
1351 if (!strokeRec.isFillStyle()) {
1352 return false;
1353 }
1354
robertphillipsff0ca5e2015-07-22 11:54:44 -07001355 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
robertphillipsff0ca5e2015-07-22 11:54:44 -07001356
robertphillips6cfb1062016-08-22 16:13:48 -07001357 if (devRRect.isRect() || devRRect.isCircle()) {
1358 if (this->ignoreXform()) {
1359 return false;
1360 }
1361
1362 sk_sp<GrFragmentProcessor> fp;
1363 if (devRRect.isRect()) {
1364 SkScalar pad = 3.0f * xformedSigma;
1365 const SkRect dstCoverageRect = devRRect.rect().makeOutset(pad, pad);
1366
1367 fp = GrRectBlurEffect::Make(context->textureProvider(), dstCoverageRect, xformedSigma);
1368 } else {
1369 fp = GrCircleBlurFragmentProcessor::Make(context->textureProvider(),
1370 devRRect.rect(),
1371 xformedSigma);
1372 }
1373
robertphillips0dafbde2016-08-15 09:04:45 -07001374 if (!fp) {
1375 return false;
1376 }
1377
Brian Salomon82f44312017-01-11 13:42:54 -05001378 paint.addCoverageFragmentProcessor(std::move(fp));
robertphillips0dafbde2016-08-15 09:04:45 -07001379
1380 SkRect srcProxyRect = srcRRect.rect();
1381 srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
1382
Brian Salomon82f44312017-01-11 13:42:54 -05001383 renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
robertphillips0dafbde2016-08-15 09:04:45 -07001384 return true;
1385 }
1386
robertphillips287e7cb2016-08-16 15:49:20 -07001387 sk_sp<GrFragmentProcessor> fp(GrRRectBlurEffect::Make(context, fSigma, xformedSigma,
1388 srcRRect, devRRect));
1389 if (!fp) {
commit-bot@chromium.org3d8bf232014-04-28 19:49:24 +00001390 return false;
1391 }
1392
robertphillips287e7cb2016-08-16 15:49:20 -07001393 if (!this->ignoreXform()) {
1394 SkRect srcProxyRect = srcRRect.rect();
1395 srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
robertphillips27cdd942016-08-10 16:25:25 -07001396
robertphillips287e7cb2016-08-16 15:49:20 -07001397 SkPoint points[8];
1398 uint16_t indices[24];
1399 int numPoints, numIndices;
robertphillips27cdd942016-08-10 16:25:25 -07001400
robertphillips287e7cb2016-08-16 15:49:20 -07001401 SkRect temp = fOccluder;
robertphillips27cdd942016-08-10 16:25:25 -07001402
robertphillips287e7cb2016-08-16 15:49:20 -07001403 if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) {
1404 srcProxyRect.toQuad(points);
1405 temp.toQuad(&points[4]);
1406 numPoints = 8;
robertphillips27cdd942016-08-10 16:25:25 -07001407
robertphillips287e7cb2016-08-16 15:49:20 -07001408 static const uint16_t ringI[24] = { 0, 1, 5, 5, 4, 0,
1409 1, 2, 6, 6, 5, 1,
1410 2, 3, 7, 7, 6, 2,
1411 3, 0, 4, 4, 7, 3 };
1412 memcpy(indices, ringI, sizeof(ringI));
1413 numIndices = 24;
1414 } else {
1415 // full rect case
1416 srcProxyRect.toQuad(points);
1417 numPoints = 4;
1418
1419 static const uint16_t fullI[6] = { 0, 1, 2, 0, 2, 3 };
1420 memcpy(indices, fullI, sizeof(fullI));
1421 numIndices = 6;
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001422 }
robertphillips287e7cb2016-08-16 15:49:20 -07001423
Brian Salomon82f44312017-01-11 13:42:54 -05001424 paint.addCoverageFragmentProcessor(std::move(fp));
1425 renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix,
1426 kTriangles_GrPrimitiveType, numPoints, points, nullptr,
1427 nullptr, indices, numIndices);
robertphillips287e7cb2016-08-16 15:49:20 -07001428
robertphillips27cdd942016-08-10 16:25:25 -07001429 } else {
1430 SkMatrix inverse;
1431 if (!viewMatrix.invert(&inverse)) {
1432 return false;
1433 }
1434
1435 float extra=3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f);
1436 SkRect proxyRect = devRRect.rect();
1437 proxyRect.outset(extra, extra);
1438
Brian Salomon82f44312017-01-11 13:42:54 -05001439 paint.addCoverageFragmentProcessor(std::move(fp));
1440 renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo,
1441 SkMatrix::I(), proxyRect, inverse);
joshualittc2625822014-12-18 16:40:54 -08001442 }
robertphillipsea461502015-05-26 11:38:03 -07001443
robertphillipsff0ca5e2015-07-22 11:54:44 -07001444 return true;
commit-bot@chromium.org82139702014-03-10 22:53:20 +00001445}
1446
robertphillips30c4cae2015-09-15 10:20:55 -07001447bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRRect& devRRect,
robertphillips@google.com49149312013-07-03 15:34:35 +00001448 const SkIRect& clipBounds,
1449 const SkMatrix& ctm,
1450 SkRect* maskRect) const {
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001451 SkScalar xformedSigma = this->computeXformedSigma(ctm);
1452 if (xformedSigma <= 0) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001453 return false;
reed@google.com2b75f422011-07-07 13:43:38 +00001454 }
robertphillips@google.com49149312013-07-03 15:34:35 +00001455
robertphillips865606d2016-08-11 08:24:41 -07001456 // We always do circles and simple circular rrects on the GPU
1457 if (!devRRect.isCircle() && !devRRect.isSimpleCircular()) {
robertphillips30c4cae2015-09-15 10:20:55 -07001458 static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
1459 static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
robertphillips@google.com49149312013-07-03 15:34:35 +00001460
robertphillips30c4cae2015-09-15 10:20:55 -07001461 if (devRRect.width() <= kMIN_GPU_BLUR_SIZE &&
1462 devRRect.height() <= kMIN_GPU_BLUR_SIZE &&
1463 xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
1464 // We prefer to blur small rects with small radii on the CPU.
1465 return false;
1466 }
robertphillips@google.com49149312013-07-03 15:34:35 +00001467 }
1468
halcanary96fcdcc2015-08-27 07:41:13 -07001469 if (nullptr == maskRect) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001470 // don't need to compute maskRect
1471 return true;
1472 }
1473
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001474 float sigma3 = 3 * SkScalarToFloat(xformedSigma);
robertphillips@google.com49149312013-07-03 15:34:35 +00001475
reed@google.com44699382013-10-31 17:28:30 +00001476 SkRect clipRect = SkRect::Make(clipBounds);
robertphillips30c4cae2015-09-15 10:20:55 -07001477 SkRect srcRect(devRRect.rect());
robertphillips@google.com49149312013-07-03 15:34:35 +00001478
1479 // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001480 srcRect.outset(sigma3, sigma3);
1481 clipRect.outset(sigma3, sigma3);
robertphillipsf4e59952015-01-07 12:16:10 -08001482 if (!srcRect.intersect(clipRect)) {
1483 srcRect.setEmpty();
1484 }
robertphillips@google.com49149312013-07-03 15:34:35 +00001485 *maskRect = srcRect;
skia.committer@gmail.com1842adf2013-07-04 07:01:07 +00001486 return true;
reed@google.com2b75f422011-07-07 13:43:38 +00001487}
1488
Robert Phillips4a24da52016-12-14 09:00:07 -05001489sk_sp<GrTextureProxy> SkBlurMaskFilterImpl::filterMaskGPU(GrContext* context,
1490 sk_sp<GrTextureProxy> srcProxy,
1491 const SkMatrix& ctm,
1492 const SkIRect& maskRect) const {
robertphillipsf054b172016-05-13 05:06:19 -07001493 // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is.
1494 const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height());
robertphillips@google.com49149312013-07-03 15:34:35 +00001495
commit-bot@chromium.org439ff1b2014-01-13 16:39:39 +00001496 SkScalar xformedSigma = this->computeXformedSigma(ctm);
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001497 SkASSERT(xformedSigma > 0);
robertphillips@google.com49149312013-07-03 15:34:35 +00001498
Robert Phillips4a24da52016-12-14 09:00:07 -05001499 // TODO: defer this further (i.e., push the proxy into GaussianBlur)
1500 GrTexture* src = srcProxy->instantiate(context->textureProvider());
1501 if (!src) {
1502 return nullptr;
1503 }
1504
robertphillips@google.com49149312013-07-03 15:34:35 +00001505 // If we're doing a normal blur, we can clobber the pathTexture in the
1506 // gaussianBlur. Otherwise, we need to save it for later compositing.
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001507 bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle);
Brian Osman11052242016-10-27 14:47:55 -04001508 sk_sp<GrRenderTargetContext> renderTargetContext(SkGpuBlurUtils::GaussianBlur(context, src,
1509 nullptr, clipRect,
1510 nullptr,
1511 xformedSigma,
1512 xformedSigma));
1513 if (!renderTargetContext) {
Robert Phillips4a24da52016-12-14 09:00:07 -05001514 return nullptr;
robertphillips@google.com49149312013-07-03 15:34:35 +00001515 }
1516
1517 if (!isNormalBlur) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001518 GrPaint paint;
robertphillips@google.com49149312013-07-03 15:34:35 +00001519 // Blend pathTexture over blurTexture.
Robert Phillips67c18d62017-01-20 12:44:06 -05001520 paint.addCoverageFragmentProcessor(
1521 GrSimpleTextureEffect::Make(src, nullptr, SkMatrix::I()));
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001522 if (kInner_SkBlurStyle == fBlurStyle) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001523 // inner: dst = dst * src
egdanielb197b8f2015-02-17 07:34:43 -08001524 paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001525 } else if (kSolid_SkBlurStyle == fBlurStyle) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001526 // solid: dst = src + dst - src * dst
egdanielb197b8f2015-02-17 07:34:43 -08001527 // = src + (1 - src) * dst
1528 paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op);
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001529 } else if (kOuter_SkBlurStyle == fBlurStyle) {
robertphillips@google.com49149312013-07-03 15:34:35 +00001530 // outer: dst = dst * (1 - src)
1531 // = 0 * src + (1 - src) * dst
egdanielb197b8f2015-02-17 07:34:43 -08001532 paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op);
egdanielc4b72722015-11-23 13:20:41 -08001533 } else {
1534 paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);
robertphillips@google.com49149312013-07-03 15:34:35 +00001535 }
robertphillipsea461502015-05-26 11:38:03 -07001536
Brian Salomon82f44312017-01-11 13:42:54 -05001537 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(),
Brian Salomon0e8fc8b2016-12-09 15:10:07 -05001538 SkRect::Make(clipRect));
robertphillips@google.com49149312013-07-03 15:34:35 +00001539 }
1540
Robert Phillips4a24da52016-12-14 09:00:07 -05001541 return sk_ref_sp(renderTargetContext->asDeferredTexture());
robertphillips@google.com49149312013-07-03 15:34:35 +00001542}
1543
1544#endif // SK_SUPPORT_GPU
1545
1546
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +00001547#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001548void SkBlurMaskFilterImpl::toString(SkString* str) const {
1549 str->append("SkBlurMaskFilterImpl: (");
1550
robertphillips@google.com7ce661d2013-08-27 16:14:03 +00001551 str->append("sigma: ");
1552 str->appendScalar(fSigma);
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001553 str->append(" ");
1554
commit-bot@chromium.orge3964552014-04-28 16:25:35 +00001555 static const char* gStyleName[kLastEnum_SkBlurStyle + 1] = {
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001556 "normal", "solid", "outer", "inner"
1557 };
1558
1559 str->appendf("style: %s ", gStyleName[fBlurStyle]);
1560 str->append("flags: (");
1561 if (fBlurFlags) {
1562 bool needSeparator = false;
robertphillips27cdd942016-08-10 16:25:25 -07001563 SkAddFlagToString(str, this->ignoreXform(), "IgnoreXform", &needSeparator);
skia.committer@gmail.com8eaddb02013-03-19 07:15:10 +00001564 SkAddFlagToString(str,
1565 SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
robertphillips@google.com0bd80fa2013-03-18 17:53:38 +00001566 "HighQuality", &needSeparator);
1567 } else {
1568 str->append("None");
1569 }
1570 str->append("))");
1571}
1572#endif
1573
caryclark@google.comd26147a2011-12-15 14:16:43 +00001574SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
1575 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
1576SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END