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