• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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