SkBlurMaskFilter.cpp revision 96ac2f693b3a4b7bb504ec2a13b15eeeaa5ff1fd
1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "SkBlurMaskFilter.h"
10#include "SkBlurMask.h"
11#include "SkGpuBlurUtils.h"
12#include "SkFlattenableBuffers.h"
13#include "SkMaskFilter.h"
14#include "SkRTConf.h"
15#include "SkStringUtils.h"
16#include "SkStrokeRec.h"
17
18#if SK_SUPPORT_GPU
19#include "GrContext.h"
20#include "GrTexture.h"
21#include "effects/GrSimpleTextureEffect.h"
22#include "SkGrPixelRef.h"
23#endif
24
25class SkBlurMaskFilterImpl : public SkMaskFilter {
26public:
27    SkBlurMaskFilterImpl(SkScalar sigma, SkBlurMaskFilter::BlurStyle, uint32_t flags);
28
29    // overrides from SkMaskFilter
30    virtual SkMask::Format getFormat() const SK_OVERRIDE;
31    virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
32                            SkIPoint* margin) const SK_OVERRIDE;
33
34#if SK_SUPPORT_GPU
35    virtual bool canFilterMaskGPU(const SkRect& devBounds,
36                                  const SkIRect& clipBounds,
37                                  const SkMatrix& ctm,
38                                  SkRect* maskRect) const SK_OVERRIDE;
39    virtual bool filterMaskGPU(GrTexture* src,
40                               const SkRect& maskRect,
41                               GrTexture** result,
42                               bool canOverwriteSrc) const;
43#endif
44
45    virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
46
47    SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
48    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
49
50protected:
51    virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
52                                           const SkIRect& clipBounds,
53                                           NinePatch*) const SK_OVERRIDE;
54
55    bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
56                        SkIPoint* margin, SkMask::CreateMode createMode) const;
57
58private:
59    // To avoid unseemly allocation requests (esp. for finite platforms like
60    // handset) we limit the radius so something manageable. (as opposed to
61    // a request like 10,000)
62    static const SkScalar kMAX_BLUR_SIGMA;
63
64    SkScalar                    fSigma;
65    SkBlurMaskFilter::BlurStyle fBlurStyle;
66    uint32_t                    fBlurFlags;
67
68    SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
69    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
70
71    SkScalar computeXformedSigma(const SkMatrix& ctm) const {
72        bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
73
74        SkScalar xformedSigma = ignoreTransform ? fSigma : ctm.mapRadius(fSigma);
75        return SkMinScalar(xformedSigma, kMAX_BLUR_SIGMA);
76    }
77
78    typedef SkMaskFilter INHERITED;
79};
80
81const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_SIGMA = SkIntToScalar(128);
82
83SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
84                                       SkBlurMaskFilter::BlurStyle style,
85                                       uint32_t flags) {
86    // use !(radius > 0) instead of radius <= 0 to reject NaN values
87    if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
88        || flags > SkBlurMaskFilter::kAll_BlurFlag) {
89        return NULL;
90    }
91
92    SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
93
94    return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
95}
96
97SkMaskFilter* SkBlurMaskFilter::Create(SkBlurMaskFilter::BlurStyle style,
98                                       SkScalar sigma,
99                                       uint32_t flags) {
100    // use !(sigma > 0) instead of sigma <= 0 to reject NaN values
101    if (!(sigma > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
102        || flags > SkBlurMaskFilter::kAll_BlurFlag) {
103        return NULL;
104    }
105
106    return SkNEW_ARGS(SkBlurMaskFilterImpl, (sigma, style, flags));
107}
108
109///////////////////////////////////////////////////////////////////////////////
110
111SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar sigma,
112                                           SkBlurMaskFilter::BlurStyle style,
113                                           uint32_t flags)
114    : fSigma(sigma), fBlurStyle(style), fBlurFlags(flags) {
115#if 0
116    fGamma = NULL;
117    if (gammaScale) {
118        fGamma = new U8[256];
119        if (gammaScale > 0)
120            SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
121        else
122            SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
123    }
124#endif
125    SkASSERT(fSigma >= 0);
126    SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
127    SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
128}
129
130SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
131    return SkMask::kA8_Format;
132}
133
134bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
135                                      const SkMatrix& matrix,
136                                      SkIPoint* margin) const{
137    SkScalar sigma = this->computeXformedSigma(matrix);
138
139    SkBlurMask::Quality blurQuality =
140        (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
141            SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
142
143    return SkBlurMask::BoxBlur(dst, src, sigma, (SkBlurMask::Style)fBlurStyle,
144                               blurQuality, margin);
145}
146
147bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
148                                          const SkMatrix& matrix,
149                                          SkIPoint* margin, SkMask::CreateMode createMode) const{
150    SkScalar sigma = computeXformedSigma(matrix);
151
152    return SkBlurMask::BlurRect(sigma, dst, r, (SkBlurMask::Style)fBlurStyle,
153                                margin, createMode);
154}
155
156#include "SkCanvas.h"
157
158static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
159    rects[0].roundOut(&mask->fBounds);
160    mask->fRowBytes = SkAlign4(mask->fBounds.width());
161    mask->fFormat = SkMask::kA8_Format;
162    size_t size = mask->computeImageSize();
163    mask->fImage = SkMask::AllocImage(size);
164    if (NULL == mask->fImage) {
165        return false;
166    }
167    sk_bzero(mask->fImage, size);
168
169    SkBitmap bitmap;
170    bitmap.setConfig(SkBitmap::kA8_Config,
171                     mask->fBounds.width(), mask->fBounds.height(),
172                     mask->fRowBytes);
173    bitmap.setPixels(mask->fImage);
174
175    SkCanvas canvas(bitmap);
176    canvas.translate(-SkIntToScalar(mask->fBounds.left()),
177                     -SkIntToScalar(mask->fBounds.top()));
178
179    SkPaint paint;
180    paint.setAntiAlias(true);
181
182    if (1 == count) {
183        canvas.drawRect(rects[0], paint);
184    } else {
185        // todo: do I need a fast way to do this?
186        SkPath path;
187        path.addRect(rects[0]);
188        path.addRect(rects[1]);
189        path.setFillType(SkPath::kEvenOdd_FillType);
190        canvas.drawPath(path, paint);
191    }
192    return true;
193}
194
195static bool rect_exceeds(const SkRect& r, SkScalar v) {
196    return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
197           r.width() > v || r.height() > v;
198}
199
200#ifdef SK_IGNORE_FAST_RECT_BLUR
201SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" );
202#else
203SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
204#endif
205
206SkMaskFilter::FilterReturn
207SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
208                                        const SkMatrix& matrix,
209                                        const SkIRect& clipBounds,
210                                        NinePatch* patch) const {
211    if (count < 1 || count > 2) {
212        return kUnimplemented_FilterReturn;
213    }
214
215    // TODO: report correct metrics for innerstyle, where we do not grow the
216    // total bounds, but we do need an inset the size of our blur-radius
217    if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle ||
218        SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
219        return kUnimplemented_FilterReturn;
220    }
221
222    // TODO: take clipBounds into account to limit our coordinates up front
223    // for now, just skip too-large src rects (to take the old code path).
224    if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
225        return kUnimplemented_FilterReturn;
226    }
227
228    SkIPoint margin;
229    SkMask  srcM, dstM;
230    rects[0].roundOut(&srcM.fBounds);
231    srcM.fImage = NULL;
232    srcM.fFormat = SkMask::kA8_Format;
233    srcM.fRowBytes = 0;
234
235    bool filterResult = false;
236    if (count == 1 && c_analyticBlurNinepatch) {
237        // special case for fast rect blur
238        // don't actually do the blur the first time, just compute the correct size
239        filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
240                                            SkMask::kJustComputeBounds_CreateMode);
241    } else {
242        filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
243    }
244
245    if (!filterResult) {
246        return kFalse_FilterReturn;
247    }
248
249    /*
250     *  smallR is the smallest version of 'rect' that will still guarantee that
251     *  we get the same blur results on all edges, plus 1 center row/col that is
252     *  representative of the extendible/stretchable edges of the ninepatch.
253     *  Since our actual edge may be fractional we inset 1 more to be sure we
254     *  don't miss any interior blur.
255     *  x is an added pixel of blur, and { and } are the (fractional) edge
256     *  pixels from the original rect.
257     *
258     *   x x { x x .... x x } x x
259     *
260     *  Thus, in this case, we inset by a total of 5 (on each side) beginning
261     *  with our outer-rect (dstM.fBounds)
262     */
263    SkRect smallR[2];
264    SkIPoint center;
265
266    // +2 is from +1 for each edge (to account for possible fractional edges
267    int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
268    int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
269    SkIRect innerIR;
270
271    if (1 == count) {
272        innerIR = srcM.fBounds;
273        center.set(smallW, smallH);
274    } else {
275        SkASSERT(2 == count);
276        rects[1].roundIn(&innerIR);
277        center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
278                   smallH + (innerIR.top() - srcM.fBounds.top()));
279    }
280
281    // +1 so we get a clean, stretchable, center row/col
282    smallW += 1;
283    smallH += 1;
284
285    // we want the inset amounts to be integral, so we don't change any
286    // fractional phase on the fRight or fBottom of our smallR.
287    const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
288    const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
289    if (dx < 0 || dy < 0) {
290        // we're too small, relative to our blur, to break into nine-patch,
291        // so we ask to have our normal filterMask() be called.
292        return kUnimplemented_FilterReturn;
293    }
294
295    smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
296    if (smallR[0].width() < 2 || smallR[0].height() < 2) {
297        return kUnimplemented_FilterReturn;
298    }
299    if (2 == count) {
300        smallR[1].set(rects[1].left(), rects[1].top(),
301                      rects[1].right() - dx, rects[1].bottom() - dy);
302        SkASSERT(!smallR[1].isEmpty());
303    }
304
305    if (count > 1 || !c_analyticBlurNinepatch) {
306        if (!drawRectsIntoMask(smallR, count, &srcM)) {
307            return kFalse_FilterReturn;
308        }
309
310        SkAutoMaskFreeImage amf(srcM.fImage);
311
312        if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
313            return kFalse_FilterReturn;
314        }
315    } else {
316        if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
317                                  SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
318            return kFalse_FilterReturn;
319        }
320    }
321    patch->fMask.fBounds.offsetTo(0, 0);
322    patch->fOuterRect = dstM.fBounds;
323    patch->fCenter = center;
324    return kTrue_FilterReturn;
325}
326
327void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
328                                             SkRect* dst) const {
329    SkScalar pad = 3.0f * fSigma;
330
331    dst->set(src.fLeft  - pad, src.fTop    - pad,
332             src.fRight + pad, src.fBottom + pad);
333}
334
335SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
336        : SkMaskFilter(buffer) {
337    fSigma = buffer.readScalar();
338#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V13_AND_ALL_OTHER_INSTANCES_TOO
339    // Fixing this must be done in two stages. When the skps are recaptured in V13,
340    // remove the ConvertRadiusToSigma but retain the absolute value.
341    // At the same time, switch the code in flatten to write a positive value.
342    // When the skps are captured in V14 the absolute value can be removed.
343    if (fSigma > 0) {
344        fSigma = SkBlurMask::ConvertRadiusToSigma(fSigma);
345    } else {
346        fSigma = -fSigma;
347    }
348#endif
349    fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
350    fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
351    SkASSERT(fSigma >= 0);
352    SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
353}
354
355void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
356    this->INHERITED::flatten(buffer);
357    buffer.writeScalar(-fSigma);
358    buffer.writeInt(fBlurStyle);
359    buffer.writeUInt(fBlurFlags);
360}
361
362#if SK_SUPPORT_GPU
363
364bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
365                                            const SkIRect& clipBounds,
366                                            const SkMatrix& ctm,
367                                            SkRect* maskRect) const {
368    SkScalar xformedSigma = this->computeXformedSigma(ctm);
369    if (xformedSigma <= 0) {
370        return false;
371    }
372
373    static const SkScalar kMIN_GPU_BLUR_SIZE  = SkIntToScalar(64);
374    static const SkScalar kMIN_GPU_BLUR_SIGMA = SkIntToScalar(32);
375
376    if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
377        srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
378        xformedSigma <= kMIN_GPU_BLUR_SIGMA) {
379        // We prefer to blur small rect with small radius via CPU.
380        return false;
381    }
382
383    if (NULL == maskRect) {
384        // don't need to compute maskRect
385        return true;
386    }
387
388    float sigma3 = 3 * SkScalarToFloat(xformedSigma);
389
390    SkRect clipRect = SkRect::Make(clipBounds);
391    SkRect srcRect(srcBounds);
392
393    // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
394    srcRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
395    clipRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
396    srcRect.intersect(clipRect);
397    *maskRect = srcRect;
398    return true;
399}
400
401bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
402                                         const SkRect& maskRect,
403                                         GrTexture** result,
404                                         bool canOverwriteSrc) const {
405    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
406
407    GrContext* context = src->getContext();
408
409    GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
410
411    SkScalar xformedSigma = this->computeXformedSigma(context->getMatrix());
412    SkASSERT(xformedSigma > 0);
413
414    // If we're doing a normal blur, we can clobber the pathTexture in the
415    // gaussianBlur.  Otherwise, we need to save it for later compositing.
416    bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
417    *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
418                                           clipRect, false, xformedSigma, xformedSigma);
419    if (NULL == *result) {
420        return false;
421    }
422
423    if (!isNormalBlur) {
424        context->setIdentityMatrix();
425        GrPaint paint;
426        SkMatrix matrix;
427        matrix.setIDiv(src->width(), src->height());
428        // Blend pathTexture over blurTexture.
429        GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget());
430        paint.addColorEffect(GrSimpleTextureEffect::Create(src, matrix))->unref();
431        if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
432            // inner:  dst = dst * src
433            paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
434        } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) {
435            // solid:  dst = src + dst - src * dst
436            //             = (1 - dst) * src + 1 * dst
437            paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
438        } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
439            // outer:  dst = dst * (1 - src)
440            //             = 0 * src + (1 - src) * dst
441            paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
442        }
443        context->drawRect(paint, clipRect);
444    }
445
446    return true;
447}
448
449#endif // SK_SUPPORT_GPU
450
451
452#ifdef SK_DEVELOPER
453void SkBlurMaskFilterImpl::toString(SkString* str) const {
454    str->append("SkBlurMaskFilterImpl: (");
455
456    str->append("sigma: ");
457    str->appendScalar(fSigma);
458    str->append(" ");
459
460    static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = {
461        "normal", "solid", "outer", "inner"
462    };
463
464    str->appendf("style: %s ", gStyleName[fBlurStyle]);
465    str->append("flags: (");
466    if (fBlurFlags) {
467        bool needSeparator = false;
468        SkAddFlagToString(str,
469                          SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
470                          "IgnoreXform", &needSeparator);
471        SkAddFlagToString(str,
472                          SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
473                          "HighQuality", &needSeparator);
474    } else {
475        str->append("None");
476    }
477    str->append("))");
478}
479#endif
480
481SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
482    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
483SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
484