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