• 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 #include "src/core/SkWriteBuffer.h"
44 
45 #include <algorithm>
46 #include <cstdint>
47 #include <cstring>
48 #include <utility>
49 
SkBlurMaskFilterImpl(SkScalar sigma,SkBlurStyle style,bool respectCTM)50 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma, SkBlurStyle style, bool respectCTM)
51     : fSigma(sigma)
52     , fBlurStyle(style)
53     , fRespectCTM(respectCTM) {
54     SkASSERT(fSigma > 0);
55     SkASSERT((unsigned)style <= kLastEnum_SkBlurStyle);
56 }
57 
getFormat() const58 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
59     return SkMask::kA8_Format;
60 }
61 
asABlur(BlurRec * rec) const62 bool SkBlurMaskFilterImpl::asABlur(BlurRec* rec) const {
63     if (this->ignoreXform()) {
64         return false;
65     }
66 
67     if (rec) {
68         rec->fSigma = fSigma;
69         rec->fStyle = fBlurStyle;
70     }
71     return true;
72 }
73 
asImageFilter(const SkMatrix & ctm) const74 sk_sp<SkImageFilter> SkBlurMaskFilterImpl::asImageFilter(const SkMatrix& ctm) const {
75     float sigma = fSigma;
76     if (this->ignoreXform()) {
77         // This is analogous to computeXformedSigma(), but it might be more correct to wrap the
78         // blur image filter in a local matrix with ctm^-1, or to control the skif::Mapping when
79         // the mask filter layer is restored. This is inaccurate when 'ctm' has skew or perspective
80         const float ctmScaleFactor = fSigma / ctm.mapRadius(fSigma);
81         sigma *= ctmScaleFactor;
82     }
83 
84     // The null input image filter will be bound to the original coverage mask.
85     sk_sp<SkImageFilter> filter = SkImageFilters::Blur(sigma, sigma, nullptr);
86     // Combine the original coverage mask (src) and the blurred coverage mask (dst)
87     switch(fBlurStyle) {
88         case kInner_SkBlurStyle: //  dst = dst * src
89                                  //      = 0 * src + src * dst
90             return SkImageFilters::Blend(SkBlendMode::kDstIn, std::move(filter), nullptr);
91         case kSolid_SkBlurStyle: //  dst = src + dst - src * dst
92                                  //      = 1 * src + (1 - src) * dst
93             return SkImageFilters::Blend(SkBlendMode::kSrcOver, std::move(filter), nullptr);
94         case kOuter_SkBlurStyle: //  dst = dst * (1 - src)
95                                  //      = 0 * src + (1 - src) * dst
96             return SkImageFilters::Blend(SkBlendMode::kDstOut, std::move(filter), nullptr);
97         case kNormal_SkBlurStyle:
98             return filter;
99     }
100     SkUNREACHABLE;
101 }
102 
computeXformedSigma(const SkMatrix & ctm) const103 SkScalar SkBlurMaskFilterImpl::computeXformedSigma(const SkMatrix& ctm) const {
104     constexpr SkScalar kMaxBlurSigma = SkIntToScalar(128);
105     SkScalar xformedSigma = this->ignoreXform() ? fSigma : ctm.mapRadius(fSigma);
106     return std::min(xformedSigma, kMaxBlurSigma);
107 }
108 
filterMask(SkMaskBuilder * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const109 bool SkBlurMaskFilterImpl::filterMask(SkMaskBuilder* dst, const SkMask& src,
110                                       const SkMatrix& matrix,
111                                       SkIPoint* margin) const {
112     SkScalar sigma = this->computeXformedSigma(matrix);
113     return SkBlurMask::BoxBlur(dst, src, sigma, fBlurStyle, margin);
114 }
115 
filterRectMask(SkMaskBuilder * dst,const SkRect & r,const SkMatrix & matrix,SkIPoint * margin,SkMaskBuilder::CreateMode createMode) const116 bool SkBlurMaskFilterImpl::filterRectMask(SkMaskBuilder* dst, const SkRect& r,
117                                           const SkMatrix& matrix,
118                                           SkIPoint* margin,
119                                           SkMaskBuilder::CreateMode createMode) const {
120     SkScalar sigma = computeXformedSigma(matrix);
121 
122     return SkBlurMask::BlurRect(sigma, dst, r, fBlurStyle, margin, createMode);
123 }
124 
prepare_to_draw_into_mask(const SkRect & bounds,SkMaskBuilder * mask)125 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMaskBuilder* mask) {
126     SkASSERT(mask != nullptr);
127 
128     mask->bounds() = bounds.roundOut();
129     mask->rowBytes() = SkAlign4(mask->fBounds.width());
130     mask->format() = SkMask::kA8_Format;
131     const size_t size = mask->computeImageSize();
132     if (size == 0) {
133         return false;
134     }
135     mask->image() = SkMaskBuilder::AllocImage(size, SkMaskBuilder::kZeroInit_Alloc);
136     if (nullptr == mask->fImage) {
137         return false;
138     }
139     return true;
140 }
141 
draw_into_mask(SkMaskBuilder * mask,const SkRect & bounds,Proc proc)142 template <typename Proc> bool draw_into_mask(SkMaskBuilder* mask, const SkRect& bounds, Proc proc) {
143     if (!prepare_to_draw_into_mask(bounds, mask)) {
144         return false;
145     }
146 
147     const int dx = mask->fBounds.fLeft;
148     const int dy = mask->fBounds.fTop;
149     SkRasterClip rclip(mask->fBounds);
150     rclip.setRect(mask->fBounds.makeOffset(-dx, -dy));
151 
152     SkASSERT(mask->fFormat == SkMask::kA8_Format);
153     auto info = SkImageInfo::MakeA8(mask->fBounds.width(), mask->fBounds.height());
154     auto pm = SkPixmap(info, mask->fImage, mask->fRowBytes);
155 
156     SkMatrix ctm = SkMatrix::Translate(-SkIntToScalar(dx), -SkIntToScalar(dy));
157 
158     SkDrawBase draw;
159     draw.fBlitterChooser = SkA8Blitter_Choose;
160     draw.fCTM = &ctm;
161     draw.fDst = pm;
162     draw.fRC  = &rclip;
163 
164     SkPaint paint;
165     paint.setAntiAlias(true);
166 
167     proc(draw, paint);
168     return true;
169 }
170 
draw_rects_into_mask(SkSpan<const SkRect> rects,SkMaskBuilder * mask)171 static bool draw_rects_into_mask(SkSpan<const SkRect> rects, SkMaskBuilder* mask) {
172     SkASSERT(rects.size() == 1 || rects.size() == 2);
173     return draw_into_mask(mask, rects[0], [&](SkDrawBase& draw, const SkPaint& paint) {
174         if (rects.size() == 1) {
175             draw.drawRect(rects[0], paint);
176         } else {
177             // todo: do I need a fast way to do this?
178             SkPath path = SkPathBuilder().addRect(rects[0])
179                                          .addRect(rects[1])
180                                          .setFillType(SkPathFillType::kEvenOdd)
181                                          .detach();
182             draw.drawPath(path, paint, nullptr, true);
183         }
184     });
185 }
186 
draw_rrect_into_mask(const SkRRect & rrect,SkMaskBuilder * mask)187 static bool draw_rrect_into_mask(const SkRRect& rrect, SkMaskBuilder* mask) {
188     return draw_into_mask(mask, rrect.rect(), [&](SkDrawBase& draw, const SkPaint& paint) {
189         draw.drawRRect(rrect, paint);
190     });
191 }
192 
rect_exceeds(const SkRect & r,SkScalar v)193 static bool rect_exceeds(const SkRect& r, SkScalar v) {
194     return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
195            r.width() > v || r.height() > v;
196 }
197 
copy_mask_to_cacheddata(SkMaskBuilder * mask)198 static SkCachedData* copy_mask_to_cacheddata(SkMaskBuilder* mask) {
199     const size_t size = mask->computeTotalImageSize();
200     SkCachedData* data = SkResourceCache::NewCachedData(size);
201     if (data) {
202         memcpy(data->writable_data(), mask->fImage, size);
203         SkMaskBuilder::FreeImage(mask->image());
204         mask->image() = (uint8_t*)data->writable_data();
205     }
206     return data;
207 }
208 
find_cached_rrect(SkTLazy<SkMask> * mask,SkScalar sigma,SkBlurStyle style,const SkRRect & rrect)209 static SkCachedData* find_cached_rrect(SkTLazy<SkMask>* mask, SkScalar sigma, SkBlurStyle style,
210                                        const SkRRect& rrect) {
211     return SkMaskCache::FindAndRef(sigma, style, rrect, mask);
212 }
213 
add_cached_rrect(SkMaskBuilder * mask,SkScalar sigma,SkBlurStyle style,const SkRRect & rrect)214 static SkCachedData* add_cached_rrect(SkMaskBuilder* mask, SkScalar sigma, SkBlurStyle style,
215                                       const SkRRect& rrect) {
216     SkCachedData* cache = copy_mask_to_cacheddata(mask);
217     if (cache) {
218         SkMaskCache::Add(sigma, style, rrect, *mask, cache);
219     }
220     return cache;
221 }
222 
find_cached_rects(SkTLazy<SkMask> * mask,SkScalar sigma,SkBlurStyle style,SkSpan<const SkRect> rects)223 static SkCachedData* find_cached_rects(SkTLazy<SkMask>* mask,
224                                        SkScalar sigma,
225                                        SkBlurStyle style,
226                                        SkSpan<const SkRect> rects) {
227     return SkMaskCache::FindAndRef(sigma, style, rects, mask);
228 }
229 
add_cached_rects(SkMaskBuilder * mask,SkScalar sigma,SkBlurStyle style,SkSpan<const SkRect> rects)230 static SkCachedData* add_cached_rects(SkMaskBuilder* mask,
231                                       SkScalar sigma,
232                                       SkBlurStyle style,
233                                       SkSpan<const SkRect> rects) {
234     SkCachedData* cache = copy_mask_to_cacheddata(mask);
235     if (cache) {
236         SkMaskCache::Add(sigma, style, rects, *mask, cache);
237     }
238     return cache;
239 }
240 
241 std::optional<SkMaskFilterBase::NinePatch>
filterRRectToNine(const SkRRect & rrect,const SkMatrix & matrix,const SkIRect & clipBounds) const242 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
243                                         const SkIRect& clipBounds) const {
244     switch (rrect.getType()) {
245         case SkRRect::kEmpty_Type:
246             // Nothing to draw.
247             return std::nullopt;
248 
249         case SkRRect::kRect_Type:
250             // We should have caught this earlier.
251             SkDEBUGFAIL("Should use a different special case");
252             return std::nullopt;
253         case SkRRect::kOval_Type:
254             // The nine patch special case does not handle ovals, and we
255             // already have code for rectangles.
256             return std::nullopt;
257 
258         // These three can take advantage of this fast path.
259         case SkRRect::kSimple_Type:
260         case SkRRect::kNinePatch_Type:
261         case SkRRect::kComplex_Type:
262             break;
263     }
264 
265     // TODO: report correct metrics for innerstyle, where we do not grow the
266     // total bounds, but we do need an inset the size of our blur-radius
267     if (kInner_SkBlurStyle == fBlurStyle) {
268         return std::nullopt;
269     }
270 
271     // TODO: take clipBounds into account to limit our coordinates up front
272     // for now, just skip too-large src rects (to take the old code path).
273     if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
274         return std::nullopt;
275     }
276 
277     // To better understand the following code, consider the concrete example
278     // where the passed in rrect is a 16x12 rounded rectangle with (rX,rY)
279     // being (2.5, 2.0) and the sigma is 0.5 (which was turned into matrix).
280     // The full non-blurred rrect would look something like this (intensity
281     // ranges from 0-F and each letter is one pixel in the bitmap).
282     // 4BFFFFFFFFFFFFB4
283     // CFFFFFFFFFFFFFFC
284     // FFFFFFFFFFFFFFFF
285     // FFFFFFFFFFFFFFFF
286     // FFFFFFFFFFFFFFFF
287     // FFFFFFFFFFFFFFFF
288     // FFFFFFFFFFFFFFFF
289     // FFFFFFFFFFFFFFFF
290     // FFFFFFFFFFFFFFFF
291     // FFFFFFFFFFFFFFFF
292     // CFFFFFFFFFFFFFFC
293     // 4BFFFFFFFFFFFFB4
294 
295     // We first figure out how much we need to expand the mask (the margin) to account
296     // for the blurred area. In the example, margin will be 1x1
297     SkIVector margin;
298     SkMaskBuilder srcM(nullptr, rrect.rect().roundOut(), 0, SkMask::kA8_Format), dstM;
299     if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
300         return std::nullopt;
301     }
302 
303     // Most of the pixels in the center of the bitmap are the same as their neighbors, so
304     // blurring is a waste of compute. If we made a smaller nine-patch rrect, blurred
305     // that, we could then expand the result later, saving cycles. As a bonus, we can cache
306     // that smaller rectangle and re-use it for other rrects with the same radii/sigma
307     // combinations.
308 
309     // To figure out the appropriate width and height of the nine patch rrect, we use the
310     // larger radius per side as well as the margin, to account for inner blur. In the example,
311     // the minimum nine patch is 9 x 7
312     //
313     // width: ceil(2.5) + 1 (margin) + 1 (stretch) + 1 (margin) + ceil(2.5) = 9
314     // height: ceil(2.0) + 1 (margin) + 1 (stretch) + 1 (margin) + ceil(2.0) = 7
315     //
316     // 4BFF F FFB4
317     // CFFF F FFFC
318     // FFFF F FFFF
319     //
320     // FFFF F FFFF
321     //
322     // FFFF F FFFF
323     // CFFF F FFFC
324     // 4BFF F FFB4
325     const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
326     const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
327     const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
328     const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
329 
330     // If there's a fractional radii, round up so that our final rrect is an integer width
331     // to allow for symmetrical blurring across the x and y axes.
332     const int32_t leftUnstretched  = SkScalarCeilToInt(std::max(UL.fX, LL.fX)) + margin.fX;
333     const int32_t rightUnstretched = SkScalarCeilToInt(std::max(UR.fX, LR.fX)) + margin.fX;
334 
335     // Extra space in the middle to ensure an unchanging piece for stretching.
336     const int32_t stretchSize = 1;
337 
338     const int32_t totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
339     if (totalSmallWidth >= rrect.rect().width()) {
340         // There is no valid piece to stretch.
341         return std::nullopt;
342     }
343 
344     const int32_t topUnstretched = SkScalarCeilToInt(std::max(UL.fY, UR.fY)) + margin.fY;
345     const int32_t botUnstretched = SkScalarCeilToInt(std::max(LL.fY, LR.fY)) + margin.fY;
346 
347     const int32_t totalSmallHeight = topUnstretched + botUnstretched + stretchSize;
348     if (totalSmallHeight >= rrect.rect().height()) {
349         // There is no valid piece to stretch.
350         return std::nullopt;
351     }
352 
353     // Now make that scaled down nine patch rrect.
354     SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
355     SkRRect smallRR;
356     smallRR.setRectRadii(smallR, rrect.radii().begin());
357 
358     const float sigma = this->computeXformedSigma(matrix);
359     // If we've already blurred this small rrect, pull it out of the cache and we are done
360     SkTLazy<SkMask> cachedMask;
361     SkCachedData* cache = find_cached_rrect(&cachedMask, sigma, fBlurStyle, smallRR);
362     if (!cache) {
363         // Blit the small rrect into a buffer (9x7)
364         // 4BFFFFFB4
365         // CFFFFFFFC
366         // FFFFFFFFF
367         // FFFFFFFFF
368         // FFFFFFFFF
369         // CFFFFFFFC
370         // 4BFFFFFB4
371         if (!draw_rrect_into_mask(smallRR, &srcM)) {
372             return std::nullopt;
373         }
374         SkAutoMaskFreeImage amf(srcM.image()); // delete small rrect's pixels when done
375 
376         // Blur the small rrect. This will expand the mask on all sides by margin to account for
377         // outer blur (11x9)
378         // 00111111100
379         // 04ADEEEDA40
380         // 1BFFFFFFFB1
381         // 1EFFFFFFFE1
382         // 1EFFFFFFFE1
383         // 1EFFFFFFFE1
384         // 1BFFFFFFFB1
385         // 04ADEEEDA40
386         // 00111111100
387         SkMaskBuilder filterM;
388         if (!this->filterMask(&filterM, srcM, matrix, nullptr)) {
389             return std::nullopt;
390         }
391         SkASSERT(filterM.fBounds.width() == (srcM.fBounds.width() + 2*margin.fX));
392         SkASSERT(filterM.fBounds.height() == (srcM.fBounds.height() + 2*margin.fY));
393 
394         cache = add_cached_rrect(&filterM, sigma, fBlurStyle, smallRR);
395         cachedMask.init(filterM);
396     }
397 
398     // The bounds of the blurred mask are at -margin, so we need to offset it back to 0,0
399     SkIRect bounds = cachedMask->fBounds;
400     bounds.offsetTo(0, 0);
401     // The ninepatch could be asymmetrical (e.g. if the left rX are wider than the right), so
402     // we must tell the caller where the center stretchy bit is in both directions. We added
403     // margin once before to unstretched to account for inner blur, but now we add to account
404     // for the outer blur. In the example, this will be (5, 4) [note that it's 0-indexed]
405     SkIPoint center = SkIPoint{margin.fX + leftUnstretched,
406                                margin.fY + topUnstretched};
407     return std::optional<SkMaskFilterBase::NinePatch>(
408             std::in_place,
409             SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat},
410             dstM.fBounds,
411             center,
412             cache);  // transfer ownership to patch
413 }
414 
415 SkMaskFilterBase::FilterReturn
filterRectsToNine(SkSpan<const SkRect> rects,const SkMatrix & matrix,const SkIRect & clipBounds,std::optional<NinePatch> * patch) const416 SkBlurMaskFilterImpl::filterRectsToNine(SkSpan<const SkRect> rects,
417                                         const SkMatrix& matrix,
418                                         const SkIRect& clipBounds,
419                                         std::optional<NinePatch>* patch) const {
420     SkASSERT(patch != nullptr);
421     SkASSERT(rects.size() == 1 || rects.size() == 2);
422 
423     // TODO: report correct metrics for innerstyle, where we do not grow the
424     // total bounds, but we do need an inset the size of our blur-radius
425     if (kInner_SkBlurStyle == fBlurStyle || kOuter_SkBlurStyle == fBlurStyle) {
426         return FilterReturn::kUnimplemented;
427     }
428 
429     // TODO: take clipBounds into account to limit our coordinates up front
430     // for now, just skip too-large src rects (to take the old code path).
431     if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
432         return FilterReturn::kUnimplemented;
433     }
434 
435     SkIPoint margin;
436     SkMaskBuilder srcM(nullptr, rects[0].roundOut(), 0, SkMask::kA8_Format), dstM;
437 
438     bool filterResult = false;
439     if (rects.size() == 1) {
440         // special case for fast rect blur
441         // don't actually do the blur the first time, just compute the correct size
442         filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
443                                             SkMaskBuilder::kJustComputeBounds_CreateMode);
444     } else {
445         filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
446     }
447 
448     if (!filterResult) {
449         return FilterReturn::kFalse;
450     }
451 
452     /*
453      *  smallR is the smallest version of 'rect' that will still guarantee that
454      *  we get the same blur results on all edges, plus 1 center row/col that is
455      *  representative of the extendible/stretchable edges of the ninepatch.
456      *  Since our actual edge may be fractional we inset 1 more to be sure we
457      *  don't miss any interior blur.
458      *  x is an added pixel of blur, and { and } are the (fractional) edge
459      *  pixels from the original rect.
460      *
461      *   x x { x x .... x x } x x
462      *
463      *  Thus, in this case, we inset by a total of 5 (on each side) beginning
464      *  with our outer-rect (dstM.fBounds)
465      */
466     SkRect smallR[2];
467     int rectCount;
468     SkIPoint center;
469 
470     // +2 is from +1 for each edge (to account for possible fractional edges
471     int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
472     int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
473     SkIRect innerIR;
474 
475     if (rects.size() == 1) {
476         rectCount = 1;
477         innerIR = srcM.fBounds;
478         center.set(smallW, smallH);
479     } else {
480         rectCount = 2;
481         rects[1].roundIn(&innerIR);
482         center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
483                    smallH + (innerIR.top() - srcM.fBounds.top()));
484     }
485 
486     // +1 so we get a clean, stretchable, center row/col
487     smallW += 1;
488     smallH += 1;
489 
490     // we want the inset amounts to be integral, so we don't change any
491     // fractional phase on the fRight or fBottom of our smallR.
492     const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
493     const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
494     if (dx < 0 || dy < 0) {
495         // we're too small, relative to our blur, to break into nine-patch,
496         // so we ask to have our normal filterMask() be called.
497         return FilterReturn::kUnimplemented;
498     }
499 
500     smallR[0].setLTRB(rects[0].left(),       rects[0].top(),
501                       rects[0].right() - dx, rects[0].bottom() - dy);
502     if (smallR[0].width() < 2 || smallR[0].height() < 2) {
503         return FilterReturn::kUnimplemented;
504     }
505     if (rectCount == 2) {
506         smallR[1].setLTRB(rects[1].left(), rects[1].top(),
507                           rects[1].right() - dx, rects[1].bottom() - dy);
508         SkASSERT(!smallR[1].isEmpty());
509     }
510 
511     const SkScalar sigma = this->computeXformedSigma(matrix);
512     SkTLazy<SkMask> cachedMask;
513     SkSpan<const SkRect> smallRects = SkSpan(smallR, rectCount);
514     SkCachedData* cache = find_cached_rects(&cachedMask, sigma, fBlurStyle, smallRects);
515     if (!cache) {
516         SkMaskBuilder filterM;
517         if (rectCount == 2) {
518             if (!draw_rects_into_mask(smallRects, &srcM)) {
519                 return FilterReturn::kFalse;
520             }
521 
522             SkAutoMaskFreeImage amf(srcM.image());
523 
524             if (!this->filterMask(&filterM, srcM, matrix, nullptr)) {
525                 return FilterReturn::kFalse;
526             }
527         } else {
528             if (!this->filterRectMask(&filterM, smallR[0], matrix, nullptr,
529                                       SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode)) {
530                 return FilterReturn::kFalse;
531             }
532         }
533         cache = add_cached_rects(&filterM, sigma, fBlurStyle, smallRects);
534         cachedMask.init(filterM);
535     }
536     SkIRect bounds = cachedMask->fBounds;
537     bounds.offsetTo(0, 0);
538     patch->emplace(SkMask{cachedMask->fImage, bounds, cachedMask->fRowBytes, cachedMask->fFormat},
539                    dstM.fBounds,
540                    center,
541                    cache);  // transfer ownership to patch
542     return FilterReturn::kTrue;
543 }
544 
computeFastBounds(const SkRect & src,SkRect * dst) const545 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
546                                              SkRect* dst) const {
547     // TODO: if we're doing kInner blur, should we return a different outset?
548     //       i.e. pad == 0 ?
549 
550     SkScalar pad = 3.0f * fSigma;
551 
552     dst->setLTRB(src.fLeft  - pad, src.fTop    - pad,
553                  src.fRight + pad, src.fBottom + pad);
554 }
555 
CreateProc(SkReadBuffer & buffer)556 sk_sp<SkFlattenable> SkBlurMaskFilterImpl::CreateProc(SkReadBuffer& buffer) {
557     const SkScalar sigma = buffer.readScalar();
558     SkBlurStyle style = buffer.read32LE(kLastEnum_SkBlurStyle);
559 
560     uint32_t flags = buffer.read32LE(0x3);  // historically we only recorded 2 bits
561     bool respectCTM = !(flags & 1); // historically we stored ignoreCTM in low bit
562 
563     return SkMaskFilter::MakeBlur((SkBlurStyle)style, sigma, respectCTM);
564 }
565 
flatten(SkWriteBuffer & buffer) const566 void SkBlurMaskFilterImpl::flatten(SkWriteBuffer& buffer) const {
567     buffer.writeScalar(fSigma);
568     buffer.writeUInt(fBlurStyle);
569     buffer.writeUInt(!fRespectCTM); // historically we recorded ignoreCTM
570 }
571 
sk_register_blur_maskfilter_createproc()572 void sk_register_blur_maskfilter_createproc() { SK_REGISTER_FLATTENABLE(SkBlurMaskFilterImpl); }
573 
MakeBlur(SkBlurStyle style,SkScalar sigma,bool respectCTM)574 sk_sp<SkMaskFilter> SkMaskFilter::MakeBlur(SkBlurStyle style, SkScalar sigma, bool respectCTM) {
575     if (SkIsFinite(sigma) && sigma > 0) {
576         return sk_sp<SkMaskFilter>(new SkBlurMaskFilterImpl(sigma, style, respectCTM));
577     }
578     return nullptr;
579 }
580