1 | /* |
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 | |
8 | #include "include/core/SkMaskFilter.h" |
9 | #include "include/core/SkRRect.h" |
10 | #include "include/core/SkStrokeRec.h" |
11 | #include "include/core/SkVertices.h" |
12 | #include "src/core/SkBlurMask.h" |
13 | #include "src/core/SkBlurPriv.h" |
14 | #include "src/core/SkGpuBlurUtils.h" |
15 | #include "src/core/SkMaskFilterBase.h" |
16 | #include "src/core/SkRRectPriv.h" |
17 | #include "src/core/SkReadBuffer.h" |
18 | #include "src/core/SkStringUtils.h" |
19 | #include "src/core/SkWriteBuffer.h" |
20 | |
21 | #if SK_SUPPORT_GPU |
22 | #include "include/private/GrRecordingContext.h" |
23 | #include "src/gpu/GrClip.h" |
24 | #include "src/gpu/GrFragmentProcessor.h" |
25 | #include "src/gpu/GrRecordingContextPriv.h" |
26 | #include "src/gpu/GrRenderTargetContext.h" |
27 | #include "src/gpu/GrResourceProvider.h" |
28 | #include "src/gpu/GrShaderCaps.h" |
29 | #include "src/gpu/GrStyle.h" |
30 | #include "src/gpu/GrTextureProxy.h" |
31 | #include "src/gpu/effects/GrTextureEffect.h" |
32 | #include "src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h" |
33 | #include "src/gpu/effects/generated/GrRRectBlurEffect.h" |
34 | #include "src/gpu/effects/generated/GrRectBlurEffect.h" |
35 | #include "src/gpu/geometry/GrShape.h" |
36 | #include "src/gpu/glsl/GrGLSLFragmentProcessor.h" |
37 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
38 | #include "src/gpu/glsl/GrGLSLProgramDataManager.h" |
39 | #include "src/gpu/glsl/GrGLSLUniformHandler.h" |
40 | #endif |
41 | |
42 | class SkBlurMaskFilterImpl : public SkMaskFilterBase { |
43 | public: |
44 | SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle, bool respectCTM); |
45 | |
46 | // overrides from SkMaskFilter |
47 | SkMask::Format getFormat() const override; |
48 | bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&, |
49 | SkIPoint* margin) const override; |
50 | |
51 | #if SK_SUPPORT_GPU |
52 | bool canFilterMaskGPU(const GrShape& shape, |
53 | const SkIRect& devSpaceShapeBounds, |
54 | const SkIRect& clipBounds, |
55 | const SkMatrix& ctm, |
56 | SkIRect* maskRect) const override; |
57 | bool directFilterMaskGPU(GrRecordingContext*, |
58 | GrRenderTargetContext* renderTargetContext, |
59 | GrPaint&&, |
60 | const GrClip&, |
61 | const SkMatrix& viewMatrix, |
62 | const GrShape& shape) const override; |
63 | GrSurfaceProxyView filterMaskGPU(GrRecordingContext*, |
64 | GrSurfaceProxyView srcView, |
65 | GrColorType srcColorType, |
66 | SkAlphaType srcAlphaType, |
67 | const SkMatrix& ctm, |
68 | const SkIRect& maskRect) const override; |
69 | #endif |
70 | |
71 | void computeFastBounds(const SkRect&, SkRect*) const override; |
72 | bool asABlur(BlurRec*) const override; |
73 | |
74 | |
75 | protected: |
76 | FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&, |
77 | const SkIRect& clipBounds, |
78 | NinePatch*) const override; |
79 | |
80 | FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&, |
81 | const SkIRect& clipBounds, |
82 | NinePatch*) const override; |
83 | |
84 | bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix, |
85 | SkIPoint* margin, SkMask::CreateMode createMode) const; |
86 | bool filterRRectMask(SkMask* dstM, const SkRRect& r, const SkMatrix& matrix, |
87 | SkIPoint* margin, SkMask::CreateMode createMode) const; |
88 | |
89 | bool ignoreXform() const { return !fRespectCTM; } |
90 | |
91 | private: |
92 | SK_FLATTENABLE_HOOKS(SkBlurMaskFilterImpl) |
93 | // To avoid unseemly allocation requests (esp. for finite platforms like |
94 | // handset) we limit the radius so something manageable. (as opposed to |
95 | // a request like 10,000) |
96 | static const SkScalar kMAX_BLUR_SIGMA; |
97 | |
98 | SkScalar fSigma; |
99 | SkBlurStyle fBlurStyle; |
100 | bool fRespectCTM; |
101 | |
102 | SkBlurMaskFilterImpl(SkReadBuffer&); |
103 | void flatten(SkWriteBuffer&) const override; |
104 | |
105 | SkScalar computeXformedSigma(const SkMatrix& ctm) const { |
106 | SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma); |
107 | return std::min(xformedSigma, kMAX_BLUR_SIGMA); |
108 | } |
109 | |
110 | friend class SkBlurMaskFilter; |
111 | |
112 | typedef SkMaskFilter INHERITED; |
113 | friend void sk_register_blur_maskfilter_createproc(); |
114 | }; |
115 | |
116 | const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128); |
117 | |
118 | // linearly interpolate between y1 & y3 to match x2's position between x1 & x3 |
119 | static SkScalar interp(SkScalar x1, SkScalar x2, SkScalar x3, SkScalar y1, SkScalar y3) { |
120 | SkASSERT(x1 <= x2 && x2 <= x3); |
121 | SkASSERT(y1 <= y3); |
122 | |
123 | SkScalar t = (x2 - x1) / (x3 - x1); |
124 | return y1 + t * (y3 - y1); |
125 | } |
126 | |
127 | // Insert 'lower' and 'higher' into 'array1' and insert a new value at each matching insertion |
128 | // point in 'array2' that linearly interpolates between the existing values. |
129 | // Return a bit mask which contains a copy of 'inputMask' for all the cells between the two |
130 | // insertion points. |
131 | static uint32_t insert_into_arrays(SkScalar* array1, SkScalar* array2, |
132 | SkScalar lower, SkScalar higher, |
133 | int* num, uint32_t inputMask, int maskSize) { |
134 | SkASSERT(lower < higher); |
135 | SkASSERT(lower >= array1[0] && higher <= array1[*num-1]); |
136 | |
137 | int32_t skipMask = 0x0; |
138 | int i; |
139 | for (i = 0; i < *num; ++i) { |
140 | if (lower >= array1[i] && lower < array1[i+1]) { |
141 | if (!SkScalarNearlyEqual(lower, array1[i])) { |
142 | memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar)); |
143 | array1[i+1] = lower; |
144 | memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar)); |
145 | array2[i+1] = interp(array1[i], lower, array1[i+2], array2[i], array2[i+2]); |
146 | i++; |
147 | (*num)++; |
148 | } |
149 | break; |
150 | } |
151 | } |
152 | for ( ; i < *num; ++i) { |
153 | skipMask |= inputMask << (i*maskSize); |
154 | if (higher > array1[i] && higher <= array1[i+1]) { |
155 | if (!SkScalarNearlyEqual(higher, array1[i+1])) { |
156 | memmove(&array1[i+2], &array1[i+1], (*num-i-1)*sizeof(SkScalar)); |
157 | array1[i+1] = higher; |
158 | memmove(&array2[i+2], &array2[i+1], (*num-i-1)*sizeof(SkScalar)); |
159 | array2[i+1] = interp(array1[i], higher, array1[i+2], array2[i], array2[i+2]); |
160 | (*num)++; |
161 | } |
162 | break; |
163 | } |
164 | } |
165 | |
166 | return skipMask; |
167 | } |
168 | |
169 | bool SkComputeBlurredRRectParams(const SkRRect& srcRRect, const SkRRect& devRRect, |
170 | const SkRect& occluder, |
171 | SkScalar sigma, SkScalar xformedSigma, |
172 | SkRRect* rrectToDraw, |
173 | SkISize* widthHeight, |
174 | SkScalar rectXs[kSkBlurRRectMaxDivisions], |
175 | SkScalar rectYs[kSkBlurRRectMaxDivisions], |
176 | SkScalar texXs[kSkBlurRRectMaxDivisions], |
177 | SkScalar texYs[kSkBlurRRectMaxDivisions], |
178 | int* numXs, int* numYs, uint32_t* skipMask) { |
179 | unsigned int devBlurRadius = 3*SkScalarCeilToInt(xformedSigma-1/6.0f); |
180 | SkScalar srcBlurRadius = 3.0f * sigma; |
181 | |
182 | const SkRect& devOrig = devRRect.getBounds(); |
183 | const SkVector& devRadiiUL = devRRect.radii(SkRRect::kUpperLeft_Corner); |
184 | const SkVector& devRadiiUR = devRRect.radii(SkRRect::kUpperRight_Corner); |
185 | const SkVector& devRadiiLR = devRRect.radii(SkRRect::kLowerRight_Corner); |
186 | const SkVector& devRadiiLL = devRRect.radii(SkRRect::kLowerLeft_Corner); |
187 | |
188 | const int devLeft = SkScalarCeilToInt(std::max<SkScalar>(devRadiiUL.fX, devRadiiLL.fX)); |
189 | const int devTop = SkScalarCeilToInt(std::max<SkScalar>(devRadiiUL.fY, devRadiiUR.fY)); |
190 | const int devRight = SkScalarCeilToInt(std::max<SkScalar>(devRadiiUR.fX, devRadiiLR.fX)); |
191 | const int devBot = SkScalarCeilToInt(std::max<SkScalar>(devRadiiLL.fY, devRadiiLR.fY)); |
192 | |
193 | // This is a conservative check for nine-patchability |
194 | if (devOrig.fLeft + devLeft + devBlurRadius >= devOrig.fRight - devRight - devBlurRadius || |
195 | devOrig.fTop + devTop + devBlurRadius >= devOrig.fBottom - devBot - devBlurRadius) { |
196 | return false; |
197 | } |
198 | |
199 | const SkVector& srcRadiiUL = srcRRect.radii(SkRRect::kUpperLeft_Corner); |
200 | const SkVector& srcRadiiUR = srcRRect.radii(SkRRect::kUpperRight_Corner); |
201 | const SkVector& srcRadiiLR = srcRRect.radii(SkRRect::kLowerRight_Corner); |
202 | const SkVector& srcRadiiLL = srcRRect.radii(SkRRect::kLowerLeft_Corner); |
203 | |
204 | const SkScalar srcLeft = std::max<SkScalar>(srcRadiiUL.fX, srcRadiiLL.fX); |
205 | const SkScalar srcTop = std::max<SkScalar>(srcRadiiUL.fY, srcRadiiUR.fY); |
206 | const SkScalar srcRight = std::max<SkScalar>(srcRadiiUR.fX, srcRadiiLR.fX); |
207 | const SkScalar srcBot = std::max<SkScalar>(srcRadiiLL.fY, srcRadiiLR.fY); |
208 | |
209 | int newRRWidth = 2*devBlurRadius + devLeft + devRight + 1; |
210 | int newRRHeight = 2*devBlurRadius + devTop + devBot + 1; |
211 | widthHeight->fWidth = newRRWidth + 2 * devBlurRadius; |
212 | widthHeight->fHeight = newRRHeight + 2 * devBlurRadius; |
213 | |
214 | const SkRect srcProxyRect = srcRRect.getBounds().makeOutset(srcBlurRadius, srcBlurRadius); |
215 | |
216 | rectXs[0] = srcProxyRect.fLeft; |
217 | rectXs[1] = srcProxyRect.fLeft + 2*srcBlurRadius + srcLeft; |
218 | rectXs[2] = srcProxyRect.fRight - 2*srcBlurRadius - srcRight; |
219 | rectXs[3] = srcProxyRect.fRight; |
220 | |
221 | rectYs[0] = srcProxyRect.fTop; |
222 | rectYs[1] = srcProxyRect.fTop + 2*srcBlurRadius + srcTop; |
223 | rectYs[2] = srcProxyRect.fBottom - 2*srcBlurRadius - srcBot; |
224 | rectYs[3] = srcProxyRect.fBottom; |
225 | |
226 | texXs[0] = 0.0f; |
227 | texXs[1] = 2.0f*devBlurRadius + devLeft; |
228 | texXs[2] = 2.0f*devBlurRadius + devLeft + 1; |
229 | texXs[3] = SkIntToScalar(widthHeight->fWidth); |
230 | |
231 | texYs[0] = 0.0f; |
232 | texYs[1] = 2.0f*devBlurRadius + devTop; |
233 | texYs[2] = 2.0f*devBlurRadius + devTop + 1; |
234 | texYs[3] = SkIntToScalar(widthHeight->fHeight); |
235 | |
236 | SkRect temp = occluder; |
237 | |
238 | *numXs = 4; |
239 | *numYs = 4; |
240 | *skipMask = 0; |
241 | if (!temp.isEmpty() && (srcProxyRect.contains(temp) || temp.intersect(srcProxyRect))) { |
242 | *skipMask = insert_into_arrays(rectXs, texXs, temp.fLeft, temp.fRight, numXs, 0x1, 1); |
243 | *skipMask = insert_into_arrays(rectYs, texYs, temp.fTop, temp.fBottom, |
244 | numYs, *skipMask, *numXs-1); |
245 | } |
246 | |
247 | const SkRect newRect = SkRect::MakeXYWH(SkIntToScalar(devBlurRadius), |
248 | SkIntToScalar(devBlurRadius), |
249 | SkIntToScalar(newRRWidth), |
250 | SkIntToScalar(newRRHeight)); |
251 | SkVector newRadii[4]; |
252 | newRadii[0] = { SkScalarCeilToScalar(devRadiiUL.fX), SkScalarCeilToScalar(devRadiiUL.fY) }; |
253 | newRadii[1] = { SkScalarCeilToScalar(devRadiiUR.fX), SkScalarCeilToScalar(devRadiiUR.fY) }; |
254 | newRadii[2] = { SkScalarCeilToScalar(devRadiiLR.fX), SkScalarCeilToScalar(devRadiiLR.fY) }; |
255 | newRadii[3] = { SkScalarCeilToScalar(devRadiiLL.fX), SkScalarCeilToScalar(devRadiiLL.fY) }; |
256 | |
257 | rrectToDraw->setRectRadii(newRect, newRadii); |
258 | return true; |
259 | } |
260 | |
261 | /////////////////////////////////////////////////////////////////////////////// |
262 | |
263 | SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, bool respectCTM) |
264 | : fSigma(sigma) |
265 | , fBlurStyle(style) |
266 | , fRespectCTM(respectCTM) { |
267 | SkASSERT(fSigma > 0); |
268 | SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle); |
269 | } |
270 | |
271 | SkMask::Format SkBlurMaskFilterImpl::getFormat() const { |
272 | return SkMask::kA8_Format; |
273 | } |
274 | |
275 | bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const { |
276 | if (this->ignoreXform()) { |
277 | return false; |
278 | } |
279 | |
280 | if (rec) { |
281 | rec->fSigma = fSigma; |
282 | rec->fStyle = fBlurStyle; |
283 | } |
284 | return true; |
285 | } |
286 | |
287 | bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src, |
288 | const SkMatrix& matrix, |
289 | SkIPoint* margin) const { |
290 | SkScalar sigma = this->computeXformedSigma(matrix); |
291 | return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, margin); |
292 | } |
293 | |
294 | bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r, |
295 | const SkMatrix& matrix, |
296 | SkIPoint* margin, SkMask::CreateMode createMode) const { |
297 | SkScalar sigma = computeXformedSigma(matrix); |
298 | |
299 | return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode); |
300 | } |
301 | |
302 | bool SkBlurMaskFilterImpl::filterRRectMask(SkMask* dst, const SkRRect& r, |
303 | const SkMatrix& matrix, |
304 | SkIPoint* margin, SkMask::CreateMode createMode) const { |
305 | SkScalar sigma = computeXformedSigma(matrix); |
306 | |
307 | return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode); |
308 | } |
309 | |
310 | #include "include/core/SkCanvas.h" |
311 | |
312 | static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) { |
313 | SkASSERT(mask != nullptr); |
314 | |
315 | mask->fBounds = bounds.roundOut(); |
316 | mask->fRowBytes = SkAlign4(mask->fBounds.width()); |
317 | mask->fFormat = SkMask::kA8_Format; |
318 | const size_t size = mask->computeImageSize(); |
319 | mask->fImage = SkMask::AllocImage(size, SkMask::kZeroInit_Alloc); |
320 | if (nullptr == mask->fImage) { |
321 | return false; |
322 | } |
323 | return true; |
324 | } |
325 | |
326 | static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) { |
327 | if (!prepare_to_draw_into_mask(rrect.rect(), mask)) { |
328 | return false; |
329 | } |
330 | |
331 | // FIXME: This code duplicates code in draw_rects_into_mask, below. Is there a |
332 | // clean way to share more code? |
333 | SkBitmap bitmap; |
334 | bitmap.installMaskPixels(*mask); |
335 | |
336 | SkCanvas canvas(bitmap); |
337 | canvas.translate(-SkIntToScalar(mask->fBounds.left()), |
338 | -SkIntToScalar(mask->fBounds.top())); |
339 | |
340 | SkPaint paint; |
341 | paint.setAntiAlias(true); |
342 | canvas.drawRRect(rrect, paint); |
343 | return true; |
344 | } |
345 | |
346 | static bool draw_rects_into_mask(const SkRect rects[], int count, SkMask* mask) { |
347 | if (!prepare_to_draw_into_mask(rects[0], mask)) { |
348 | return false; |
349 | } |
350 | |
351 | SkBitmap bitmap; |
352 | bitmap.installPixels(SkImageInfo::Make(mask->fBounds.width(), |
353 | mask->fBounds.height(), |
354 | kAlpha_8_SkColorType, |
355 | kPremul_SkAlphaType), |
356 | mask->fImage, mask->fRowBytes); |
357 | |
358 | SkCanvas canvas(bitmap); |
359 | canvas.translate(-SkIntToScalar(mask->fBounds.left()), |
360 | -SkIntToScalar(mask->fBounds.top())); |
361 | |
362 | SkPaint paint; |
363 | paint.setAntiAlias(true); |
364 | |
365 | if (1 == count) { |
366 | canvas.drawRect(rects[0], paint); |
367 | } else { |
368 | // todo: do I need a fast way to do this? |
369 | SkPath path; |
370 | path.addRect(rects[0]); |
371 | path.addRect(rects[1]); |
372 | path.setFillType(SkPathFillType::kEvenOdd); |
373 | canvas.drawPath(path, paint); |
374 | } |
375 | return true; |
376 | } |
377 | |
378 | static bool rect_exceeds(const SkRect& r, SkScalar v) { |
379 | return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v || |
380 | r.width() > v || r.height() > v; |
381 | } |
382 | |
383 | #include "src/core/SkMaskCache.h" |
384 | |
385 | static SkCachedData* copy_mask_to_cacheddata(SkMask* mask) { |
386 | const size_t size = mask->computeTotalImageSize(); |
387 | SkCachedData* data = SkResourceCache::NewCachedData(size); |
388 | if (data) { |
389 | memcpy(data->writable_data(), mask->fImage, size); |
390 | SkMask::FreeImage(mask->fImage); |
391 | mask->fImage = (uint8_t*)data->data(); |
392 | } |
393 | return data; |
394 | } |
395 | |
396 | static SkCachedData* find_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style, |
397 | const SkRRect& rrect) { |
398 | return SkMaskCache::FindAndRef(sigma, style, rrect, mask); |
399 | } |
400 | |
401 | static SkCachedData* add_cached_rrect(SkMask* mask, SkScalar sigma, SkBlurStyle style, |
402 | const SkRRect& rrect) { |
403 | SkCachedData* cache = copy_mask_to_cacheddata(mask); |
404 | if (cache) { |
405 | SkMaskCache::Add(sigma, style, rrect, *mask, cache); |
406 | } |
407 | return cache; |
408 | } |
409 | |
410 | static SkCachedData* find_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style, |
411 | const SkRect rects[], int count) { |
412 | return SkMaskCache::FindAndRef(sigma, style, rects, count, mask); |
413 | } |
414 | |
415 | static SkCachedData* add_cached_rects(SkMask* mask, SkScalar sigma, SkBlurStyle style, |
416 | const SkRect rects[], int count) { |
417 | SkCachedData* cache = copy_mask_to_cacheddata(mask); |
418 | if (cache) { |
419 | SkMaskCache::Add(sigma, style, rects, count, *mask, cache); |
420 | } |
421 | return cache; |
422 | } |
423 | |
424 | static const bool c_analyticBlurRRect{true}; |
425 | |
426 | SkMaskFilterBase::FilterReturn |
427 | SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix, |
428 | const SkIRect& clipBounds, |
429 | NinePatch* patch) const { |
430 | SkASSERT(patch != nullptr); |
431 | switch (rrect.getType()) { |
432 | case SkRRect::kEmpty_Type: |
433 | // Nothing to draw. |
434 | return kFalse_FilterReturn; |
435 | |
436 | case SkRRect::kRect_Type: |
437 | // We should have caught this earlier. |
438 | SkASSERT(false); |
439 | // Fall through. |
440 | case SkRRect::kOval_Type: |
441 | // The nine patch special case does not handle ovals, and we |
442 | // already have code for rectangles. |
443 | return kUnimplemented_FilterReturn; |
444 | |
445 | // These three can take advantage of this fast path. |
446 | case SkRRect::kSimple_Type: |
447 | case SkRRect::kNinePatch_Type: |
448 | case SkRRect::kComplex_Type: |
449 | break; |
450 | } |
451 | |
452 | // TODO: report correct metrics for innerstyle, where we do not grow the |
453 | // total bounds, but we do need an inset the size of our blur-radius |
454 | if (kInner_SkBlurStyle == fBlurStyle) { |
455 | return kUnimplemented_FilterReturn; |
456 | } |
457 | |
458 | // TODO: take clipBounds into account to limit our coordinates up front |
459 | // for now, just skip too-large src rects (to take the old code path). |
460 | if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) { |
461 | return kUnimplemented_FilterReturn; |
462 | } |
463 | |
464 | SkIPoint margin; |
465 | SkMask srcM, dstM; |
466 | srcM.fBounds = rrect.rect().roundOut(); |
467 | srcM.fFormat = SkMask::kA8_Format; |
468 | srcM.fRowBytes = 0; |
469 | |
470 | bool filterResult = false; |
471 | if (c_analyticBlurRRect) { |
472 | // special case for fast round rect blur |
473 | // don't actually do the blur the first time, just compute the correct size |
474 | filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin, |
475 | SkMask::kJustComputeBounds_CreateMode); |
476 | } |
477 | |
478 | if (!filterResult) { |
479 | filterResult = this->filterMask(&dstM, srcM, matrix, &margin); |
480 | } |
481 | |
482 | if (!filterResult) { |
483 | return kFalse_FilterReturn; |
484 | } |
485 | |
486 | // Now figure out the appropriate width and height of the smaller round rectangle |
487 | // to stretch. It will take into account the larger radius per side as well as double |
488 | // the margin, to account for inner and outer blur. |
489 | const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner); |
490 | const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner); |
491 | const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner); |
492 | const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner); |
493 | |
494 | const SkScalar leftUnstretched = std::max(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX); |
495 | const SkScalar rightUnstretched = std::max(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX); |
496 | |
497 | // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover |
498 | // any fractional space on either side plus 1 for the part to stretch. |
499 | const SkScalar stretchSize = SkIntToScalar(3); |
500 | |
501 | const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize; |
502 | if (totalSmallWidth >= rrect.rect().width()) { |
503 | // There is no valid piece to stretch. |
504 | return kUnimplemented_FilterReturn; |
505 | } |
506 | |
507 | const SkScalar topUnstretched = std::max(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY); |
508 | const SkScalar bottomUnstretched = std::max(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY); |
509 | |
510 | const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize; |
511 | if (totalSmallHeight >= rrect.rect().height()) { |
512 | // There is no valid piece to stretch. |
513 | return kUnimplemented_FilterReturn; |
514 | } |
515 | |
516 | SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight); |
517 | |
518 | SkRRect smallRR; |
519 | SkVector radii[4]; |
520 | radii[SkRRect::kUpperLeft_Corner] = UL; |
521 | radii[SkRRect::kUpperRight_Corner] = UR; |
522 | radii[SkRRect::kLowerRight_Corner] = LR; |
523 | radii[SkRRect::kLowerLeft_Corner] = LL; |
524 | smallRR.setRectRadii(smallR, radii); |
525 | |
526 | const SkScalar sigma = this->computeXformedSigma(matrix); |
527 | SkCachedData* cache = find_cached_rrect(&patch->fMask, sigma, fBlurStyle, smallRR); |
528 | if (!cache) { |
529 | bool analyticBlurWorked = false; |
530 | if (c_analyticBlurRRect) { |
531 | analyticBlurWorked = |
532 | this->filterRRectMask(&patch->fMask, smallRR, matrix, &margin, |
533 | SkMask::kComputeBoundsAndRenderImage_CreateMode); |
534 | } |
535 | |
536 | if (!analyticBlurWorked) { |
537 | if (!draw_rrect_into_mask(smallRR, &srcM)) { |
538 | return kFalse_FilterReturn; |
539 | } |
540 | |
541 | SkAutoMaskFreeImage amf(srcM.fImage); |
542 | |
543 | if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { |
544 | return kFalse_FilterReturn; |
545 | } |
546 | } |
547 | cache = add_cached_rrect(&patch->fMask, sigma, fBlurStyle, smallRR); |
548 | } |
549 | |
550 | patch->fMask.fBounds.offsetTo(0, 0); |
551 | patch->fOuterRect = dstM.fBounds; |
552 | patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1; |
553 | patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1; |
554 | SkASSERT(nullptr == patch->fCache); |
555 | patch->fCache = cache; // transfer ownership to patch |
556 | return kTrue_FilterReturn; |
557 | } |
558 | |
559 | // Use the faster analytic blur approach for ninepatch rects |
560 | static const bool c_analyticBlurNinepatch{true}; |
561 | |
562 | SkMaskFilterBase::FilterReturn |
563 | SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count, |
564 | const SkMatrix& matrix, |
565 | const SkIRect& clipBounds, |
566 | NinePatch* patch) const { |
567 | if (count < 1 || count > 2) { |
568 | return kUnimplemented_FilterReturn; |
569 | } |
570 | |
571 | // TODO: report correct metrics for innerstyle, where we do not grow the |
572 | // total bounds, but we do need an inset the size of our blur-radius |
573 | if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) { |
574 | return kUnimplemented_FilterReturn; |
575 | } |
576 | |
577 | // TODO: take clipBounds into account to limit our coordinates up front |
578 | // for now, just skip too-large src rects (to take the old code path). |
579 | if (rect_exceeds(rects[0], SkIntToScalar(32767))) { |
580 | return kUnimplemented_FilterReturn; |
581 | } |
582 | |
583 | SkIPoint margin; |
584 | SkMask srcM, dstM; |
585 | srcM.fBounds = rects[0].roundOut(); |
586 | srcM.fFormat = SkMask::kA8_Format; |
587 | srcM.fRowBytes = 0; |
588 | |
589 | bool filterResult = false; |
590 | if (count == 1 && c_analyticBlurNinepatch) { |
591 | // special case for fast rect blur |
592 | // don't actually do the blur the first time, just compute the correct size |
593 | filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin, |
594 | SkMask::kJustComputeBounds_CreateMode); |
595 | } else { |
596 | filterResult = this->filterMask(&dstM, srcM, matrix, &margin); |
597 | } |
598 | |
599 | if (!filterResult) { |
600 | return kFalse_FilterReturn; |
601 | } |
602 | |
603 | /* |
604 | * smallR is the smallest version of 'rect' that will still guarantee that |
605 | * we get the same blur results on all edges, plus 1 center row/col that is |
606 | * representative of the extendible/stretchable edges of the ninepatch. |
607 | * Since our actual edge may be fractional we inset 1 more to be sure we |
608 | * don't miss any interior blur. |
609 | * x is an added pixel of blur, and { and } are the (fractional) edge |
610 | * pixels from the original rect. |
611 | * |
612 | * x x { x x .... x x } x x |
613 | * |
614 | * Thus, in this case, we inset by a total of 5 (on each side) beginning |
615 | * with our outer-rect (dstM.fBounds) |
616 | */ |
617 | SkRect smallR[2]; |
618 | SkIPoint center; |
619 | |
620 | // +2 is from +1 for each edge (to account for possible fractional edges |
621 | int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2; |
622 | int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2; |
623 | SkIRect innerIR; |
624 | |
625 | if (1 == count) { |
626 | innerIR = srcM.fBounds; |
627 | center.set(smallW, smallH); |
628 | } else { |
629 | SkASSERT(2 == count); |
630 | rects[1].roundIn(&innerIR); |
631 | center.set(smallW + (innerIR.left() - srcM.fBounds.left()), |
632 | smallH + (innerIR.top() - srcM.fBounds.top())); |
633 | } |
634 | |
635 | // +1 so we get a clean, stretchable, center row/col |
636 | smallW += 1; |
637 | smallH += 1; |
638 | |
639 | // we want the inset amounts to be integral, so we don't change any |
640 | // fractional phase on the fRight or fBottom of our smallR. |
641 | const SkScalar dx = SkIntToScalar(innerIR.width() - smallW); |
642 | const SkScalar dy = SkIntToScalar(innerIR.height() - smallH); |
643 | if (dx < 0 || dy < 0) { |
644 | // we're too small, relative to our blur, to break into nine-patch, |
645 | // so we ask to have our normal filterMask() be called. |
646 | return kUnimplemented_FilterReturn; |
647 | } |
648 | |
649 | smallR[0].setLTRB(rects[0].left(), rects[0].top(), |
650 | rects[0].right() - dx, rects[0].bottom() - dy); |
651 | if (smallR[0].width() < 2 || smallR[0].height() < 2) { |
652 | return kUnimplemented_FilterReturn; |
653 | } |
654 | if (2 == count) { |
655 | smallR[1].setLTRB(rects[1].left(), rects[1].top(), |
656 | rects[1].right() - dx, rects[1].bottom() - dy); |
657 | SkASSERT(!smallR[1].isEmpty()); |
658 | } |
659 | |
660 | const SkScalar sigma = this->computeXformedSigma(matrix); |
661 | SkCachedData* cache = find_cached_rects(&patch->fMask, sigma, fBlurStyle, smallR, count); |
662 | if (!cache) { |
663 | if (count > 1 || !c_analyticBlurNinepatch) { |
664 | if (!draw_rects_into_mask(smallR, count, &srcM)) { |
665 | return kFalse_FilterReturn; |
666 | } |
667 | |
668 | SkAutoMaskFreeImage amf(srcM.fImage); |
669 | |
670 | if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) { |
671 | return kFalse_FilterReturn; |
672 | } |
673 | } else { |
674 | if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin, |
675 | SkMask::kComputeBoundsAndRenderImage_CreateMode)) { |
676 | return kFalse_FilterReturn; |
677 | } |
678 | } |
679 | cache = add_cached_rects(&patch->fMask, sigma, fBlurStyle, smallR, count); |
680 | } |
681 | patch->fMask.fBounds.offsetTo(0, 0); |
682 | patch->fOuterRect = dstM.fBounds; |
683 | patch->fCenter = center; |
684 | SkASSERT(nullptr == patch->fCache); |
685 | patch->fCache = cache; // transfer ownership to patch |
686 | return kTrue_FilterReturn; |
687 | } |
688 | |
689 | void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, |
690 | SkRect* dst) const { |
691 | SkScalar pad = 3.0f * fSigma; |
692 | |
693 | dst->setLTRB(src.fLeft - pad, src.fTop - pad, |
694 | src.fRight + pad, src.fBottom + pad); |
695 | } |
696 | |
697 | sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) { |
698 | const SkScalar sigma = buffer.readScalar(); |
699 | SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle); |
700 | |
701 | uint32_t flags = buffer.read32LE(0x3); // historically we only recorded 2 bits |
702 | bool respectCTM = !(flags & 1); // historically we stored ignoreCTM in low bit |
703 | |
704 | if (buffer.isVersionLT(SkPicturePriv::kRemoveOccluderFromBlurMaskFilter)) { |
705 | SkRect unused; |
706 | buffer.readRect(&unused); |
707 | } |
708 | |
709 | return SkMaskFilter::MakeBlur((SkBlurStyle)style, sigma, respectCTM); |
710 | } |
711 | |
712 | void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const { |
713 | buffer.writeScalar(fSigma); |
714 | buffer.writeUInt(fBlurStyle); |
715 | buffer.writeUInt(!fRespectCTM); // historically we recorded ignoreCTM |
716 | } |
717 | |
718 | |
719 | #if SK_SUPPORT_GPU |
720 | |
721 | bool SkBlurMaskFilterImpl::directFilterMaskGPU(GrRecordingContext* context, |
722 | GrRenderTargetContext* renderTargetContext, |
723 | GrPaint&& paint, |
724 | const GrClip& clip, |
725 | const SkMatrix& viewMatrix, |
726 | const GrShape& shape) const { |
727 | SkASSERT(renderTargetContext); |
728 | |
729 | if (fBlurStyle != kNormal_SkBlurStyle) { |
730 | return false; |
731 | } |
732 | |
733 | if (!viewMatrix.isScaleTranslate()) { |
734 | return false; |
735 | } |
736 | |
737 | // TODO: we could handle blurred stroked circles |
738 | if (!shape.style().isSimpleFill()) { |
739 | return false; |
740 | } |
741 | |
742 | SkScalar xformedSigma = this->computeXformedSigma(viewMatrix); |
743 | if (xformedSigma <= 0) { |
744 | return false; |
745 | } |
746 | |
747 | SkRRect srcRRect; |
748 | bool inverted; |
749 | if (!shape.asRRect(&srcRRect, nullptr, nullptr, &inverted) || inverted) { |
750 | return false; |
751 | } |
752 | |
753 | SkRRect devRRect; |
754 | if (!srcRRect.transform(viewMatrix, &devRRect)) { |
755 | return false; |
756 | } |
757 | |
758 | if (!SkRRectPriv::AllCornersCircular(devRRect)) { |
759 | return false; |
760 | } |
761 | |
762 | std::unique_ptr<GrFragmentProcessor> fp; |
763 | |
764 | if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) { |
765 | if (devRRect.isRect()) { |
766 | fp = GrRectBlurEffect::Make(context, *context->priv().caps()->shaderCaps(), |
767 | devRRect.rect(), xformedSigma); |
768 | } else { |
769 | fp = GrCircleBlurFragmentProcessor::Make(context, devRRect.rect(), xformedSigma); |
770 | } |
771 | |
772 | if (!fp) { |
773 | return false; |
774 | } |
775 | paint.addCoverageFragmentProcessor(std::move(fp)); |
776 | |
777 | SkRect srcProxyRect = srcRRect.rect(); |
778 | SkScalar outsetX = 3.0f*fSigma; |
779 | SkScalar outsetY = 3.0f*fSigma; |
780 | if (this->ignoreXform()) { |
781 | // When we're ignoring the CTM the padding added to the source rect also needs to ignore |
782 | // the CTM. The matrix passed in here is guaranteed to be just scale and translate so we |
783 | // can just grab the X and Y scales off the matrix and pre-undo the scale. |
784 | outsetX /= SkScalarAbs(viewMatrix.getScaleX()); |
785 | outsetY /= SkScalarAbs(viewMatrix.getScaleY()); |
786 | } |
787 | srcProxyRect.outset(outsetX, outsetY); |
788 | |
789 | renderTargetContext->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect); |
790 | return true; |
791 | } |
792 | |
793 | fp = GrRRectBlurEffect::Make(context, fSigma, xformedSigma, srcRRect, devRRect); |
794 | if (!fp) { |
795 | return false; |
796 | } |
797 | |
798 | if (!this->ignoreXform()) { |
799 | SkRect srcProxyRect = srcRRect.rect(); |
800 | srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma); |
801 | |
802 | SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 4, 6, 0); |
803 | srcProxyRect.toQuad(builder.positions()); |
804 | |
805 | static const uint16_t fullIndices[6] = { 0, 1, 2, 0, 2, 3 }; |
806 | memcpy(builder.indices(), fullIndices, sizeof(fullIndices)); |
807 | sk_sp<SkVertices> vertices = builder.detach(); |
808 | |
809 | paint.addCoverageFragmentProcessor(std::move(fp)); |
810 | renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices)); |
811 | } else { |
812 | SkMatrix inverse; |
813 | if (!viewMatrix.invert(&inverse)) { |
814 | return false; |
815 | } |
816 | |
817 | float =3.f*SkScalarCeilToScalar(xformedSigma-1/6.0f); |
818 | SkRect proxyRect = devRRect.rect(); |
819 | proxyRect.outset(extra, extra); |
820 | |
821 | paint.addCoverageFragmentProcessor(std::move(fp)); |
822 | renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, |
823 | SkMatrix::I(), proxyRect, inverse); |
824 | } |
825 | |
826 | return true; |
827 | } |
828 | |
829 | bool SkBlurMaskFilterImpl::canFilterMaskGPU(const GrShape& shape, |
830 | const SkIRect& devSpaceShapeBounds, |
831 | const SkIRect& clipBounds, |
832 | const SkMatrix& ctm, |
833 | SkIRect* maskRect) const { |
834 | SkScalar xformedSigma = this->computeXformedSigma(ctm); |
835 | if (xformedSigma <= 0) { |
836 | maskRect->setEmpty(); |
837 | return false; |
838 | } |
839 | |
840 | if (maskRect) { |
841 | float sigma3 = 3 * SkScalarToFloat(xformedSigma); |
842 | |
843 | // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area. |
844 | SkIRect clipRect = clipBounds.makeOutset(sigma3, sigma3); |
845 | SkIRect srcRect = devSpaceShapeBounds.makeOutset(sigma3, sigma3); |
846 | |
847 | if (!srcRect.intersect(clipRect)) { |
848 | srcRect.setEmpty(); |
849 | } |
850 | *maskRect = srcRect; |
851 | } |
852 | |
853 | // We prefer to blur paths with small blur radii on the CPU. |
854 | if (ctm.rectStaysRect()) { |
855 | static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64); |
856 | static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32); |
857 | |
858 | if (devSpaceShapeBounds.width() <= kMIN_GPU_BLUR_SIZE && |
859 | devSpaceShapeBounds.height() <= kMIN_GPU_BLUR_SIZE && |
860 | xformedSigma <= kMIN_GPU_BLUR_SIGMA) { |
861 | return false; |
862 | } |
863 | } |
864 | |
865 | return true; |
866 | } |
867 | |
868 | GrSurfaceProxyView SkBlurMaskFilterImpl::filterMaskGPU(GrRecordingContext* context, |
869 | GrSurfaceProxyView srcView, |
870 | GrColorType srcColorType, |
871 | SkAlphaType srcAlphaType, |
872 | const SkMatrix& ctm, |
873 | const SkIRect& maskRect) const { |
874 | // 'maskRect' isn't snapped to the UL corner but the mask in 'src' is. |
875 | const SkIRect clipRect = SkIRect::MakeWH(maskRect.width(), maskRect.height()); |
876 | |
877 | SkScalar xformedSigma = this->computeXformedSigma(ctm); |
878 | SkASSERT(xformedSigma > 0); |
879 | |
880 | // If we're doing a normal blur, we can clobber the pathTexture in the |
881 | // gaussianBlur. Otherwise, we need to save it for later compositing. |
882 | bool isNormalBlur = (kNormal_SkBlurStyle == fBlurStyle); |
883 | auto srcBounds = SkIRect::MakeSize(srcView.proxy()->dimensions()); |
884 | auto renderTargetContext = SkGpuBlurUtils::GaussianBlur(context, |
885 | srcView, |
886 | srcColorType, |
887 | srcAlphaType, |
888 | nullptr, |
889 | clipRect, |
890 | srcBounds, |
891 | xformedSigma, |
892 | xformedSigma, |
893 | SkTileMode::kClamp); |
894 | if (!renderTargetContext || !renderTargetContext->asTextureProxy()) { |
895 | return {}; |
896 | } |
897 | |
898 | if (!isNormalBlur) { |
899 | GrPaint paint; |
900 | // Blend pathTexture over blurTexture. |
901 | paint.addCoverageFragmentProcessor(GrTextureEffect::Make(std::move(srcView), srcAlphaType)); |
902 | if (kInner_SkBlurStyle == fBlurStyle) { |
903 | // inner: dst = dst * src |
904 | paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op); |
905 | } else if (kSolid_SkBlurStyle == fBlurStyle) { |
906 | // solid: dst = src + dst - src * dst |
907 | // = src + (1 - src) * dst |
908 | paint.setCoverageSetOpXPFactory(SkRegion::kUnion_Op); |
909 | } else if (kOuter_SkBlurStyle == fBlurStyle) { |
910 | // outer: dst = dst * (1 - src) |
911 | // = 0 * src + (1 - src) * dst |
912 | paint.setCoverageSetOpXPFactory(SkRegion::kDifference_Op); |
913 | } else { |
914 | paint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op); |
915 | } |
916 | |
917 | renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), |
918 | SkRect::Make(clipRect)); |
919 | } |
920 | |
921 | return renderTargetContext->readSurfaceView(); |
922 | } |
923 | |
924 | #endif // SK_SUPPORT_GPU |
925 | |
926 | void sk_register_blur_maskfilter_createproc() { SK_REGISTER_FLATTENABLE(SkBlurMaskFilterImpl); } |
927 | |
928 | sk_sp<SkMaskFilter> SkMaskFilter::MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM) { |
929 | if (SkScalarIsFinite(sigma) && sigma > 0) { |
930 | return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, respectCTM)); |
931 | } |
932 | return nullptr; |
933 | } |
934 | |