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 "src/core/SkBlurMaskFilterImpl.h"
9
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkBlurTypes.h"
12 #include "include/core/SkFlattenable.h"
13 #include "include/core/SkImageFilter.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkMaskFilter.h"
16 #include "include/core/SkMatrix.h"
17 #include "include/core/SkPaint.h"
18 #include "include/core/SkPath.h"
19 #include "include/core/SkPathBuilder.h"
20 #include "include/core/SkPathTypes.h"
21 #include "include/core/SkPixmap.h"
22 #include "include/core/SkPoint.h"
23 #include "include/core/SkRRect.h"
24 #include "include/core/SkRect.h"
25 #include "include/core/SkRefCnt.h"
26 #include "include/core/SkScalar.h"
27 #include "include/effects/SkImageFilters.h"
28 #include "include/private/base/SkAlign.h"
29 #include "include/private/base/SkAssert.h"
30 #include "include/private/base/SkFloatingPoint.h"
31 #include "include/private/base/SkTemplates.h"
32 #include "src/base/SkTLazy.h"
33 #include "src/core/SkBlitter_A8.h"
34 #include "src/core/SkBlurMask.h"
35 #include "src/core/SkCachedData.h"
36 #include "src/core/SkDrawBase.h"
37 #include "src/core/SkMask.h"
38 #include "src/core/SkMaskCache.h"
39 #include "src/core/SkMaskFilterBase.h"
40 #include "src/core/SkRasterClip.h"
41 #include "src/core/SkReadBuffer.h"
42 #include "src/core/SkResourceCache.h"
43 #ifdef SKIA_OHOS
44 #include "src/core/SkBlurEngine.h"
45 #include "src/core/SkRuntimeEffectPriv.h"
46 #include "src/core/SkSDFFilter.h"
47 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
48 #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
49 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
50 #include "src/gpu/ganesh/SkGr.h"
51 #endif
52 #include "src/core/SkWriteBuffer.h"
53
54 #include <algorithm>
55 #include <cstdint>
56 #include <cstring>
57 #include <utility>
58
SkBlurMaskFilterImpl(SkScalar sigma,SkBlurStyle style,bool respectCTM)59 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, bool respectCTM)
60 : fSigma(sigma)
61 , fBlurStyle(style)
62 , fRespectCTM(respectCTM) {
63 SkASSERT(fSigma > 0);
64 SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
65 }
66
getFormat() const67 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
68 return SkMask::kA8_Format;
69 }
70
asABlur(BlurRec * rec) const71 bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
72 if (this->ignoreXform()) {
73 return false;
74 }
75
76 if (rec) {
77 rec->fSigma = fSigma;
78 rec->fStyle = fBlurStyle;
79 }
80 return true;
81 }
82
asImageFilter(const SkMatrix & ctm) const83 sk_sp<SkImageFilter> SkBlurMaskFilterImpl::asImageFilter(const SkMatrix& ctm) const {
84 float sigma = fSigma;
85 if (this->ignoreXform()) {
86 // This is analogous to computeXformedSigma(), but it might be more correct to wrap the
87 // blur image filter in a local matrix with ctm^-1, or to control the skif::Mapping when
88 // the mask filter layer is restored. This is inaccurate when 'ctm' has skew or perspective
89 const float ctmScaleFactor = fSigma / ctm.mapRadius(fSigma);
90 sigma *= ctmScaleFactor;
91 }
92
93 // The null input image filter will be bound to the original coverage mask.
94 sk_sp<SkImageFilter> filter = SkImageFilters::Blur(sigma, sigma, nullptr);
95 // Combine the original coverage mask (src) and the blurred coverage mask (dst)
96 switch(fBlurStyle) {
97 case kInner_SkBlurStyle: // dst = dst * src
98 // = 0 * src + src * dst
99 return SkImageFilters::Blend(SkBlendMode::kDstIn, std::move(filter), nullptr);
100 case kSolid_SkBlurStyle: // dst = src + dst - src * dst
101 // = 1 * src + (1 - src) * dst
102 return SkImageFilters::Blend(SkBlendMode::kSrcOver, std::move(filter), nullptr);
103 case kOuter_SkBlurStyle: // dst = dst * (1 - src)
104 // = 0 * src + (1 - src) * dst
105 return SkImageFilters::Blend(SkBlendMode::kDstOut, std::move(filter), nullptr);
106 case kNormal_SkBlurStyle:
107 return filter;
108 }
109 SkUNREACHABLE;
110 }
111
computeXformedSigma(const SkMatrix & ctm) const112 SkScalar SkBlurMaskFilterImpl::computeXformedSigma(const SkMatrix& ctm) const {
113 constexpr SkScalar kMaxBlurSigma = SkIntToScalar(128);
114 SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma);
115 return std::min(xformedSigma, kMaxBlurSigma);
116 }
117
118 #ifdef SKIA_OHOS
make_simple_rrect_sdf(GrRecordingContext * context,GrPaint & paint,float sdfRadius,const SkRRect & srcRRect)119 static std::unique_ptr<GrFragmentProcessor> make_simple_rrect_sdf(GrRecordingContext* context,
120 GrPaint& paint,
121 float sdfRadius,
122 const SkRRect& srcRRect)
123 {
124 SkPMColor4f origColor = paint.getColor4f();
125 SkV2 wh = {srcRRect.width(), srcRRect.height()};
126 SkVector rr = srcRRect.getSimpleRadii();
127 float r = rr.x();
128
129 static auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, R"(
130 uniform half sdfRadius;
131 uniform vec2 wh;
132 uniform half r;
133 uniform half4 origColor;
134
135 half4 main(float2 pos) {
136 vec2 a = vec2(wh.x / 2, wh.y / 2);
137 vec2 q = abs(pos)-a + r;
138 float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
139 float alpha = smoothstep(sdfRadius / 2, -sdfRadius / 2, d);
140 return half4(alpha) * origColor;
141 }
142 )");
143
144 std::unique_ptr<GrFragmentProcessor> fp =
145 GrSkSLFP::Make(effect, "SimpleRRectSDF", nullptr,
146 GrSkSLFP::OptFlags::kNone,
147 "sdfRadius", sdfRadius, "wh", wh, "r", r, "origColor", origColor);
148
149 if (!fp) {
150 return nullptr;
151 }
152
153 SkMatrix matrix;
154 matrix.setTranslateX(- srcRRect.rect().fLeft - srcRRect.width() * SK_ScalarHalf);
155 matrix.setTranslateY(- srcRRect.rect().fTop - srcRRect.height() * SK_ScalarHalf);
156
157 auto paintFP = GrMatrixEffect::Make(matrix, std::move(fp));
158
159 #ifndef SK_IGNORE_GPU_DITHER
160 // add dither effect to reduce color discontinuity
161 constexpr float ditherRange = 1.f / 255.f;
162 paintFP = make_dither_effect(context, std::move(paintFP), ditherRange, context->priv().caps());
163 #endif
164
165 return paintFP;
166 }
167
168
make_complex_rrect_sdf(GrRecordingContext * context,GrPaint & paint,float sdfRadius,const SkRRect & srcRRect)169 static std::unique_ptr<GrFragmentProcessor> make_complex_rrect_sdf(GrRecordingContext* context,
170 GrPaint& paint,
171 float sdfRadius,
172 const SkRRect& srcRRect)
173 {
174 SkPMColor4f origColor = paint.getColor4f();
175 SkV2 wh = {srcRRect.width(), srcRRect.height()};
176 SkVector rr = srcRRect.getSimpleRadii();
177 SkVector rr3 = srcRRect.radii(SkRRect::Corner::kLowerLeft_Corner);
178 float r = rr.x();
179 float r3 = rr3.x();
180
181 static auto effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader, R"(
182 uniform half sdfRadius;
183 uniform vec2 wh;
184 uniform half r0;
185 uniform half r3;
186 uniform half4 origColor;
187
188 half4 main(float2 pos) {
189 vec2 a = vec2(wh.x / 2, wh.y / 2);
190 half r = (pos.y < 0.0) ? r0 : r3;
191 vec2 q = abs(pos) - a + r;
192 float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
193 float alpha = smoothstep(sdfRadius / 2, -sdfRadius / 2, d);
194 return half4(alpha) * origColor;
195 }
196 )");
197
198 std::unique_ptr<GrFragmentProcessor> fp =
199 GrSkSLFP::Make(effect, "ComplexRRectSDF", nullptr,
200 GrSkSLFP::OptFlags::kNone,
201 "sdfRadius", sdfRadius, "wh", wh, "r0", r, "r3", r3, "origColor", origColor);
202
203 if (!fp) {
204 return nullptr;
205 }
206
207 SkMatrix matrix;
208 matrix.setTranslateX(- srcRRect.rect().fLeft - srcRRect.width() * SK_ScalarHalf);
209 matrix.setTranslateY(- srcRRect.rect().fTop - srcRRect.height() * SK_ScalarHalf);
210
211 auto paintFP = GrMatrixEffect::Make(matrix, std::move(fp));
212
213 #ifndef SK_IGNORE_GPU_DITHER
214 // add dither effect to reduce color discontinuity
215 constexpr float ditherRange = 1.f / 255.f;
216 paintFP = make_dither_effect(context, std::move(paintFP), ditherRange, context->priv().caps());
217 #endif
218
219 return paintFP;
220 }
221 #endif // SKIA_OHOS
222
filterMask(SkMaskBuilder * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const223 bool SkBlurMaskFilterImpl::filterMask(SkMaskBuilder* dst, const SkMask& src,
224 const SkMatrix& matrix,
225 SkIPoint* margin) const {
226 SkScalar sigma = this->computeXformedSigma(matrix);
227 return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, margin);
228 }
229
filterRectMask(SkMaskBuilder * dst,const SkRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMaskBuilder::CreateMode createMode) const230 bool SkBlurMaskFilterImpl::filterRectMask(SkMaskBuilder* dst, const SkRect& r,
231 const SkMatrix& matrix,
232 SkIPoint* margin,
233 SkMaskBuilder::CreateMode createMode) const {
234 SkScalar sigma = computeXformedSigma(matrix);
235
236 return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
237 }
238
filterRRectMask(SkMaskBuilder * dst,const SkRRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMaskBuilder::CreateMode createMode) const239 bool SkBlurMaskFilterImpl::filterRRectMask(SkMaskBuilder* dst, const SkRRect& r,
240 const SkMatrix& matrix,
241 SkIPoint* margin,
242 SkMaskBuilder::CreateMode createMode) const {
243 SkScalar sigma = computeXformedSigma(matrix);
244
245 return SkBlurMask::BlurRRect(sigma, dst, r, fBlurStyle, margin, createMode);
246 }
247
248 #ifdef SKIA_OHOS
directFilterRRectMaskGPU(GrRecordingContext * context,skgpu::ganesh::SurfaceDrawContext * sdc,GrPaint && paint,const GrClip * clip,const SkMatrix & viewMatrix,const GrStyledShape & shape,const SkRRect & srcRRect) const249 bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrRecordingContext* context,
250 skgpu::ganesh::SurfaceDrawContext* sdc,
251 GrPaint&& paint,
252 const GrClip* clip,
253 const SkMatrix& viewMatrix,
254 const GrStyledShape& shape,
255 const SkRRect& srcRRect) const
256 {
257 SkASSERT(sdc);
258
259 if (fBlurStyle != kNormal_SkBlurStyle) {
260 return false;
261 }
262
263 if (!shape.style().isSimpleFill()) {
264 return false;
265 }
266
267 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
268 if (SkBlurEngine::IsEffectivelyIdentity(xformedSigma)) {
269 sdc->drawShape(clip, std::move(paint), GrAA::kYes, viewMatrix, GrStyledShape(shape));
270 return true;
271 }
272
273 std::unique_ptr<GrFragmentProcessor> fp;
274
275 SkRRect devRRect;
276 srcRRect.transform(viewMatrix, &devRRect);
277
278 constexpr float kSigma_Factor = 3.f;
279 float sdfRadius = kSigma_Factor * fSigma;
280 if (SDFBlur::isSimpleRRectSDF(srcRRect)) {
281 fp = make_simple_rrect_sdf(context, paint, sdfRadius, srcRRect);
282 } else if (SDFBlur::isComplexRRectSDF(srcRRect)) {
283 fp = make_complex_rrect_sdf(context, paint, sdfRadius, srcRRect);
284 } else {
285 return false;
286 }
287
288 if (!fp) {
289 return false;
290 }
291
292 if (!this->ignoreXform()) {
293 SkRect srcProxyRect = srcRRect.rect();
294 srcProxyRect.outset(3.0f*fSigma, 3.0f*fSigma);
295 paint.setColorFragmentProcessor(std::move(fp));
296 sdc->drawRect(clip, std::move(paint), GrAA::kNo, viewMatrix, srcProxyRect);
297 } else {
298 SkMatrix inverse;
299 if (!viewMatrix.invert(&inverse)) {
300 return false;
301 }
302
303 SkIRect proxyBounds;
304 float extra = 3.f * SkScalarCeilToScalar(xformedSigma - 1 / 6.0f);
305 devRRect.rect().makeOutset(extra, extra).roundOut(&proxyBounds);
306
307 paint.setColorFragmentProcessor(std::move(fp));
308 sdc->fillPixelsWithLocalMatrix(clip, std::move(paint), proxyBounds, inverse);
309 }
310 return true;
311 }
312 #endif // SKIA_OHOS
313
prepare_to_draw_into_mask(const SkRect & bounds,SkMaskBuilder * mask)314 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMaskBuilder* mask) {
315 SkASSERT(mask != nullptr);
316
317 mask->bounds() = bounds.roundOut();
318 mask->rowBytes() = SkAlign4(mask->fBounds.width());
319 mask->format() = SkMask::kA8_Format;
320 const size_t size = mask->computeImageSize();
321 if (size == 0) {
322 return false;
323 }
324 mask->image() = SkMaskBuilder::AllocImage(size, SkMaskBuilder::kZeroInit_Alloc);
325 if (nullptr == mask->fImage) {
326 return false;
327 }
328 return true;
329 }
330
draw_into_mask(SkMaskBuilder * mask,const SkRect & bounds,Proc proc)331 template <typename Proc> bool draw_into_mask(SkMaskBuilder* mask, const SkRect& bounds, Proc proc) {
332 if (!prepare_to_draw_into_mask(bounds, mask)) {
333 return false;
334 }
335
336 const int dx = mask->fBounds.fLeft;
337 const int dy = mask->fBounds.fTop;
338 SkRasterClip rclip(mask->fBounds);
339 rclip.setRect(mask->fBounds.makeOffset(-dx, -dy));
340
341 SkASSERT(mask->fFormat == SkMask::kA8_Format);
342 auto info = SkImageInfo::MakeA8(mask->fBounds.width(), mask->fBounds.height());
343 auto pm = SkPixmap(info, mask->fImage, mask->fRowBytes);
344
345 SkMatrix ctm = SkMatrix::Translate(-SkIntToScalar(dx), -SkIntToScalar(dy));
346
347 SkDrawBase draw;
348 draw.fBlitterChooser = SkA8Blitter_Choose;
349 draw.fCTM = &ctm;
350 draw.fDst = pm;
351 draw.fRC = &rclip;
352
353 SkPaint paint;
354 paint.setAntiAlias(true);
355
356 proc(draw, paint);
357 return true;
358 }
359
draw_rects_into_mask(const SkRect rects[],int count,SkMaskBuilder * mask)360 static bool draw_rects_into_mask(const SkRect rects[], int count, SkMaskBuilder* mask) {
361 return draw_into_mask(mask, rects[0], [&](SkDrawBase& draw, const SkPaint& paint) {
362 if (1 == count) {
363 draw.drawRect(rects[0], paint);
364 } else {
365 // todo: do I need a fast way to do this?
366 SkPath path = SkPathBuilder().addRect(rects[0])
367 .addRect(rects[1])
368 .setFillType(SkPathFillType::kEvenOdd)
369 .detach();
370 draw.drawPath(path, paint);
371 }
372 });
373 }
374
draw_rrect_into_mask(const SkRRect rrect,SkMaskBuilder * mask)375 static bool draw_rrect_into_mask(const SkRRect rrect, SkMaskBuilder* mask) {
376 return draw_into_mask(mask, rrect.rect(), [&](SkDrawBase& draw, const SkPaint& paint) {
377 draw.drawRRect(rrect, paint);
378 });
379 }
380
rect_exceeds(const SkRect & r,SkScalar v)381 static bool rect_exceeds(const SkRect& r, SkScalar v) {
382 return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
383 r.width() > v || r.height() > v;
384 }
385
copy_mask_to_cacheddata(SkMaskBuilder * mask)386 static SkCachedData* copy_mask_to_cacheddata(SkMaskBuilder* mask) {
387 const size_t size = mask->computeTotalImageSize();
388 SkCachedData* data = SkResourceCache::NewCachedData(size);
389 if (data) {
390 memcpy(data->writable_data(), mask->fImage, size);
391 SkMaskBuilder::FreeImage(mask->image());
392 mask->image() = (uint8_t*)data->writable_data();
393 }
394 return data;
395 }
396
find_cached_rrect(SkTLazy<SkMask> * mask,SkScalar sigma,SkBlurStyle style,const SkRRect & rrect)397 static SkCachedData* find_cached_rrect(SkTLazy<SkMask>* mask, SkScalar sigma, SkBlurStyle style,
398 const SkRRect& rrect) {
399 return SkMaskCache::FindAndRef(sigma, style, rrect, mask);
400 }
401
add_cached_rrect(SkMaskBuilder * mask,SkScalar sigma,SkBlurStyle style,const SkRRect & rrect)402 static SkCachedData* add_cached_rrect(SkMaskBuilder* mask, SkScalar sigma, SkBlurStyle style,
403 const SkRRect& rrect) {
404 SkCachedData* cache = copy_mask_to_cacheddata(mask);
405 if (cache) {
406 SkMaskCache::Add(sigma, style, rrect, *mask, cache);
407 }
408 return cache;
409 }
410
find_cached_rects(SkTLazy<SkMask> * mask,SkScalar sigma,SkBlurStyle style,const SkRect rects[],int count)411 static SkCachedData* find_cached_rects(SkTLazy<SkMask>* mask, SkScalar sigma, SkBlurStyle style,
412 const SkRect rects[], int count) {
413 return SkMaskCache::FindAndRef(sigma, style, rects, count, mask);
414 }
415
add_cached_rects(SkMaskBuilder * mask,SkScalar sigma,SkBlurStyle style,const SkRect rects[],int count)416 static SkCachedData* add_cached_rects(SkMaskBuilder* mask, SkScalar sigma, SkBlurStyle style,
417 const SkRect rects[], int count) {
418 SkCachedData* cache = copy_mask_to_cacheddata(mask);
419 if (cache) {
420 SkMaskCache::Add(sigma, style, rects, count, *mask, cache);
421 }
422 return cache;
423 }
424
425 static const bool c_analyticBlurRRect{true};
426
427 SkMaskFilterBase::FilterReturn
filterRRectToNine(const SkRRect & rrect,const SkMatrix & matrix,const SkIRect & clipBounds,SkTLazy<NinePatch> * patch) const428 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
429 const SkIRect& clipBounds,
430 SkTLazy<NinePatch>* patch) const {
431 SkASSERT(patch != nullptr);
432 switch (rrect.getType()) {
433 case SkRRect::kEmpty_Type:
434 // Nothing to draw.
435 return kFalse_FilterReturn;
436
437 case SkRRect::kRect_Type:
438 // We should have caught this earlier.
439 SkASSERT(false);
440 [[fallthrough]];
441 case SkRRect::kOval_Type:
442 // The nine patch special case does not handle ovals, and we
443 // already have code for rectangles.
444 return kUnimplemented_FilterReturn;
445
446 // These three can take advantage of this fast path.
447 case SkRRect::kSimple_Type:
448 case SkRRect::kNinePatch_Type:
449 case SkRRect::kComplex_Type:
450 break;
451 }
452
453 // TODO: report correct metrics for innerstyle, where we do not grow the
454 // total bounds, but we do need an inset the size of our blur-radius
455 if (kInner_SkBlurStyle == fBlurStyle) {
456 return kUnimplemented_FilterReturn;
457 }
458
459 // TODO: take clipBounds into account to limit our coordinates up front
460 // for now, just skip too-large src rects (to take the old code path).
461 if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
462 return kUnimplemented_FilterReturn;
463 }
464
465 SkIPoint margin;
466 SkMaskBuilder srcM(nullptr, rrect.rect().roundOut(), 0, SkMask::kA8_Format), dstM;
467
468 bool filterResult = false;
469 if (c_analyticBlurRRect) {
470 // special case for fast round rect blur
471 // don't actually do the blur the first time, just compute the correct size
472 filterResult = this->filterRRectMask(&dstM, rrect, matrix, &margin,
473 SkMaskBuilder::kJustComputeBounds_CreateMode);
474 }
475
476 if (!filterResult) {
477 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
478 }
479
480 if (!filterResult) {
481 return kFalse_FilterReturn;
482 }
483
484 // Now figure out the appropriate width and height of the smaller round rectangle
485 // to stretch. It will take into account the larger radius per side as well as double
486 // the margin, to account for inner and outer blur.
487 const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
488 const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
489 const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
490 const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
491
492 const SkScalar leftUnstretched = std::max(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
493 const SkScalar rightUnstretched = std::max(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
494
495 // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
496 // any fractional space on either side plus 1 for the part to stretch.
497 const SkScalar stretchSize = SkIntToScalar(3);
498
499 const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
500 if (totalSmallWidth >= rrect.rect().width()) {
501 // There is no valid piece to stretch.
502 return kUnimplemented_FilterReturn;
503 }
504
505 const SkScalar topUnstretched = std::max(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
506 const SkScalar bottomUnstretched = std::max(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
507
508 const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
509 if (totalSmallHeight >= rrect.rect().height()) {
510 // There is no valid piece to stretch.
511 return kUnimplemented_FilterReturn;
512 }
513
514 SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
515
516 SkRRect smallRR;
517 SkVector radii[4];
518 radii[SkRRect::kUpperLeft_Corner] = UL;
519 radii[SkRRect::kUpperRight_Corner] = UR;
520 radii[SkRRect::kLowerRight_Corner] = LR;
521 radii[SkRRect::kLowerLeft_Corner] = LL;
522 smallRR.setRectRadii(smallR, radii);
523
524 const SkScalar sigma = this->computeXformedSigma(matrix);
525 SkTLazy<SkMask> cachedMask;
526 SkCachedData* cache = find_cached_rrect(&cachedMask, sigma, fBlurStyle, smallRR);
527 if (!cache) {
528 SkMaskBuilder filterM;
529 bool analyticBlurWorked = false;
530 if (c_analyticBlurRRect) {
531 analyticBlurWorked =
532 this->filterRRectMask(&filterM, smallRR, matrix, &margin,
533 SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode);
534 }
535
536 if (!analyticBlurWorked) {
537 if (!draw_rrect_into_mask(smallRR, &srcM)) {
538 return kFalse_FilterReturn;
539 }
540 SkAutoMaskFreeImage amf(srcM.image());
541
542 if (!this->filterMask(&filterM, srcM, matrix, &margin)) {
543 return kFalse_FilterReturn;
544 }
545 }
546 cache = add_cached_rrect(&filterM, sigma, fBlurStyle, smallRR);
547 cachedMask.init(filterM);
548 }
549
550 SkIRect bounds = cachedMask->fBounds;
551 bounds.offsetTo(0, 0);
552 patch->init(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat},
553 dstM.fBounds,
554 SkIPoint{SkScalarCeilToInt(leftUnstretched) + 1,
555 SkScalarCeilToInt(topUnstretched) + 1},
556 cache); // transfer ownership to patch
557 return kTrue_FilterReturn;
558 }
559
560 // Use the faster analytic blur approach for ninepatch rects
561 static const bool c_analyticBlurNinepatch{true};
562
563 SkMaskFilterBase::FilterReturn
filterRectsToNine(const SkRect rects[],int count,const SkMatrix & matrix,const SkIRect & clipBounds,SkTLazy<NinePatch> * patch) const564 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
565 const SkMatrix& matrix,
566 const SkIRect& clipBounds,
567 SkTLazy<NinePatch>* patch) const {
568 if (count < 1 || count > 2) {
569 return kUnimplemented_FilterReturn;
570 }
571
572 // TODO: report correct metrics for innerstyle, where we do not grow the
573 // total bounds, but we do need an inset the size of our blur-radius
574 if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
575 return kUnimplemented_FilterReturn;
576 }
577
578 // TODO: take clipBounds into account to limit our coordinates up front
579 // for now, just skip too-large src rects (to take the old code path).
580 if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
581 return kUnimplemented_FilterReturn;
582 }
583
584 SkIPoint margin;
585 SkMaskBuilder srcM(nullptr, rects[0].roundOut(), 0, SkMask::kA8_Format), dstM;
586
587 bool filterResult = false;
588 if (count == 1 && c_analyticBlurNinepatch) {
589 // special case for fast rect blur
590 // don't actually do the blur the first time, just compute the correct size
591 filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
592 SkMaskBuilder::kJustComputeBounds_CreateMode);
593 } else {
594 filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
595 }
596
597 if (!filterResult) {
598 return kFalse_FilterReturn;
599 }
600
601 /*
602 * smallR is the smallest version of 'rect' that will still guarantee that
603 * we get the same blur results on all edges, plus 1 center row/col that is
604 * representative of the extendible/stretchable edges of the ninepatch.
605 * Since our actual edge may be fractional we inset 1 more to be sure we
606 * don't miss any interior blur.
607 * x is an added pixel of blur, and { and } are the (fractional) edge
608 * pixels from the original rect.
609 *
610 * x x { x x .... x x } x x
611 *
612 * Thus, in this case, we inset by a total of 5 (on each side) beginning
613 * with our outer-rect (dstM.fBounds)
614 */
615 SkRect smallR[2];
616 SkIPoint center;
617
618 // +2 is from +1 for each edge (to account for possible fractional edges
619 int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
620 int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
621 SkIRect innerIR;
622
623 if (1 == count) {
624 innerIR = srcM.fBounds;
625 center.set(smallW, smallH);
626 } else {
627 SkASSERT(2 == count);
628 rects[1].roundIn(&innerIR);
629 center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
630 smallH + (innerIR.top() - srcM.fBounds.top()));
631 }
632
633 // +1 so we get a clean, stretchable, center row/col
634 smallW += 1;
635 smallH += 1;
636
637 // we want the inset amounts to be integral, so we don't change any
638 // fractional phase on the fRight or fBottom of our smallR.
639 const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
640 const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
641 if (dx < 0 || dy < 0) {
642 // we're too small, relative to our blur, to break into nine-patch,
643 // so we ask to have our normal filterMask() be called.
644 return kUnimplemented_FilterReturn;
645 }
646
647 smallR[0].setLTRB(rects[0].left(), rects[0].top(),
648 rects[0].right() - dx, rects[0].bottom() - dy);
649 if (smallR[0].width() < 2 || smallR[0].height() < 2) {
650 return kUnimplemented_FilterReturn;
651 }
652 if (2 == count) {
653 smallR[1].setLTRB(rects[1].left(), rects[1].top(),
654 rects[1].right() - dx, rects[1].bottom() - dy);
655 SkASSERT(!smallR[1].isEmpty());
656 }
657
658 const SkScalar sigma = this->computeXformedSigma(matrix);
659 SkTLazy<SkMask> cachedMask;
660 SkCachedData* cache = find_cached_rects(&cachedMask, sigma, fBlurStyle, smallR, count);
661 if (!cache) {
662 SkMaskBuilder filterM;
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.image());
669
670 if (!this->filterMask(&filterM, srcM, matrix, &margin)) {
671 return kFalse_FilterReturn;
672 }
673 } else {
674 if (!this->filterRectMask(&filterM, smallR[0], matrix, &margin,
675 SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode)) {
676 return kFalse_FilterReturn;
677 }
678 }
679 cache = add_cached_rects(&filterM, sigma, fBlurStyle, smallR, count);
680 cachedMask.init(filterM);
681 }
682 SkIRect bounds = cachedMask->fBounds;
683 bounds.offsetTo(0, 0);
684 patch->init(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat},
685 dstM.fBounds, center, cache); // transfer ownership to patch
686 return kTrue_FilterReturn;
687 }
688
computeFastBounds(const SkRect & src,SkRect * dst) const689 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
690 SkRect* dst) const {
691 // TODO: if we're doing kInner blur, should we return a different outset?
692 // i.e. pad == 0 ?
693
694 SkScalar pad = 3.0f * fSigma;
695
696 dst->setLTRB(src.fLeft - pad, src.fTop - pad,
697 src.fRight + pad, src.fBottom + pad);
698 }
699
CreateProc(SkReadBuffer & buffer)700 sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
701 const SkScalar sigma = buffer.readScalar();
702 SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle);
703
704 uint32_t flags = buffer.read32LE(0x3); // historically we only recorded 2 bits
705 bool respectCTM = !(flags & 1); // historically we stored ignoreCTM in low bit
706
707 return SkMaskFilter::MakeBlur((SkBlurStyle)style, sigma, respectCTM);
708 }
709
flatten(SkWriteBuffer & buffer) const710 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
711 buffer.writeScalar(fSigma);
712 buffer.writeUInt(fBlurStyle);
713 buffer.writeUInt(!fRespectCTM); // historically we recorded ignoreCTM
714 }
715
sk_register_blur_maskfilter_createproc()716 void sk_register_blur_maskfilter_createproc() { SK_REGISTER_FLATTENABLE(SkBlurMaskFilterImpl); }
717
MakeBlur(SkBlurStyle style,SkScalar sigma,bool respectCTM)718 sk_sp<SkMaskFilter> SkMaskFilter::MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM) {
719 if (SkIsFinite(sigma) && sigma > 0) {
720 return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, respectCTM));
721 }
722 return nullptr;
723 }
724
725 #ifdef SKIA_OHOS
quick_check_gpu_draw(const SkMatrix & viewMatrix,SkIRect & devSpaceShapeBounds) const726 bool SkBlurMaskFilterImpl::quick_check_gpu_draw(const SkMatrix& viewMatrix,
727 SkIRect& devSpaceShapeBounds) const {
728 SkScalar xformedSigma = this->computeXformedSigma(viewMatrix);
729
730 // According to the advice in skia, We prefer to blur paths with small blur radii on the CPU.
731 static const SkScalar SKIA_MIN_GPU_BLUR_SIZE = SkIntToScalar(64);
732 static const SkScalar SKIA_GPU_BLUR_SIGMA = SkIntToScalar(32);
733
734 if (devSpaceShapeBounds.width() <= SKIA_MIN_GPU_BLUR_SIZE &&
735 devSpaceShapeBounds.height() <= SKIA_MIN_GPU_BLUR_SIZE &&
736 xformedSigma <= SKIA_GPU_BLUR_SIGMA) {
737 return false;
738 }
739 return true;
740 }
741 #endif // SKIA_OHOS