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