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 "SkRRect.h"
     15 #include "SkRTConf.h"
     16 #include "SkStringUtils.h"
     17 #include "SkStrokeRec.h"
     18 
     19 #if SK_SUPPORT_GPU
     20 #include "GrContext.h"
     21 #include "GrTexture.h"
     22 #include "effects/GrSimpleTextureEffect.h"
     23 #include "SkGrPixelRef.h"
     24 #endif
     25 
     26 class SkBlurMaskFilterImpl : public SkMaskFilter {
     27 public:
     28     SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle,
     29                          uint32_t flags);
     30 
     31     // overrides from SkMaskFilter
     32     virtual SkMask::Format getFormat() const SK_OVERRIDE;
     33     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
     34                             SkIPoint* margin) const SK_OVERRIDE;
     35 
     36 #if SK_SUPPORT_GPU
     37     virtual bool canFilterMaskGPU(const SkRect& devBounds,
     38                                   const SkIRect& clipBounds,
     39                                   const SkMatrix& ctm,
     40                                   SkRect* maskRect) const SK_OVERRIDE;
     41     virtual bool filterMaskGPU(GrTexture* src,
     42                                const SkRect& maskRect,
     43                                GrTexture** result,
     44                                bool canOverwriteSrc) const;
     45 #endif
     46 
     47     virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
     48 
     49     SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;)
     50     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
     51 
     52 protected:
     53     virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
     54                                            const SkIRect& clipBounds,
     55                                            NinePatch*) const SK_OVERRIDE;
     56 
     57     virtual FilterReturn filterRRectToNine(const SkRRect&, const SkMatrix&,
     58                                            const SkIRect& clipBounds,
     59                                            NinePatch*) const SK_OVERRIDE;
     60 
     61     bool filterRectMask(SkMask* dstM, const SkRect& r, const SkMatrix& matrix,
     62                         SkIPoint* margin, SkMask::CreateMode createMode) const;
     63 
     64 private:
     65     // To avoid unseemly allocation requests (esp. for finite platforms like
     66     // handset) we limit the radius so something manageable. (as opposed to
     67     // a request like 10,000)
     68     static const SkScalar kMAX_BLUR_RADIUS;
     69     // This constant approximates the scaling done in the software path's
     70     // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
     71     // IMHO, it actually should be 1:  we blur "less" than we should do
     72     // according to the CSS and canvas specs, simply because Safari does the same.
     73     // Firefox used to do the same too, until 4.0 where they fixed it.  So at some
     74     // point we should probably get rid of these scaling constants and rebaseline
     75     // all the blur tests.
     76     static const SkScalar kBLUR_SIGMA_SCALE;
     77 
     78     SkScalar                    fRadius;
     79     SkBlurMaskFilter::BlurStyle fBlurStyle;
     80     uint32_t                    fBlurFlags;
     81 
     82     SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
     83     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
     84 #if SK_SUPPORT_GPU
     85     SkScalar computeXformedRadius(const SkMatrix& ctm) const {
     86         bool ignoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
     87 
     88         SkScalar xformedRadius = ignoreTransform ? fRadius
     89                                                  : ctm.mapRadius(fRadius);
     90         return SkMinScalar(xformedRadius, kMAX_BLUR_RADIUS);
     91     }
     92 #endif
     93 
     94     typedef SkMaskFilter INHERITED;
     95 };
     96 
     97 const SkScalar SkBlurMaskFilterImpl::kMAX_BLUR_RADIUS = SkIntToScalar(128);
     98 const SkScalar SkBlurMaskFilterImpl::kBLUR_SIGMA_SCALE = SkFloatToScalar(0.6f);
     99 
    100 SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
    101                                        SkBlurMaskFilter::BlurStyle style,
    102                                        uint32_t flags) {
    103     // use !(radius > 0) instead of radius <= 0 to reject NaN values
    104     if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
    105         || flags > SkBlurMaskFilter::kAll_BlurFlag) {
    106         return NULL;
    107     }
    108 
    109     return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags));
    110 }
    111 
    112 ///////////////////////////////////////////////////////////////////////////////
    113 
    114 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius,
    115                                            SkBlurMaskFilter::BlurStyle style,
    116                                            uint32_t flags)
    117     : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) {
    118 #if 0
    119     fGamma = NULL;
    120     if (gammaScale) {
    121         fGamma = new U8[256];
    122         if (gammaScale > 0)
    123             SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
    124         else
    125             SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
    126     }
    127 #endif
    128     SkASSERT(radius >= 0);
    129     SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
    130     SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
    131 }
    132 
    133 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
    134     return SkMask::kA8_Format;
    135 }
    136 
    137 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
    138                                       const SkMatrix& matrix,
    139                                       SkIPoint* margin) const{
    140     SkScalar radius;
    141     if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
    142         radius = fRadius;
    143     } else {
    144         radius = matrix.mapRadius(fRadius);
    145     }
    146 
    147     radius = SkMinScalar(radius, kMAX_BLUR_RADIUS);
    148     SkBlurMask::Quality blurQuality =
    149         (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
    150             SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
    151 
    152     return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
    153                             blurQuality, margin);
    154 }
    155 
    156 bool SkBlurMaskFilterImpl::filterRectMask(SkMask* dst, const SkRect& r,
    157                                           const SkMatrix& matrix,
    158                                           SkIPoint* margin, SkMask::CreateMode createMode) const{
    159     SkScalar radius;
    160     if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
    161         radius = fRadius;
    162     } else {
    163         radius = matrix.mapRadius(fRadius);
    164     }
    165 
    166     radius = SkMinScalar(radius, kMAX_BLUR_RADIUS);
    167 
    168     return SkBlurMask::BlurRect(dst, r, radius, (SkBlurMask::Style)fBlurStyle,
    169                                 margin, createMode);
    170 }
    171 
    172 #include "SkCanvas.h"
    173 
    174 static bool prepare_to_draw_into_mask(const SkRect& bounds, SkMask* mask) {
    175     SkASSERT(mask != NULL);
    176 
    177     bounds.roundOut(&mask->fBounds);
    178     mask->fRowBytes = SkAlign4(mask->fBounds.width());
    179     mask->fFormat = SkMask::kA8_Format;
    180     const size_t size = mask->computeImageSize();
    181     mask->fImage = SkMask::AllocImage(size);
    182     if (NULL == mask->fImage) {
    183         return false;
    184     }
    185 
    186     // FIXME: use sk_calloc in AllocImage?
    187     sk_bzero(mask->fImage, size);
    188     return true;
    189 }
    190 
    191 static bool draw_rrect_into_mask(const SkRRect rrect, SkMask* mask) {
    192     if (!prepare_to_draw_into_mask(rrect.rect(), mask)) {
    193         return false;
    194     }
    195 
    196     // FIXME: This code duplicates code in drawRectsIntoMask, below. Is there a
    197     // clean way to share more code?
    198     SkBitmap bitmap;
    199     bitmap.setConfig(SkBitmap::kA8_Config,
    200                      mask->fBounds.width(), mask->fBounds.height(),
    201                      mask->fRowBytes);
    202     bitmap.setPixels(mask->fImage);
    203 
    204     SkCanvas canvas(bitmap);
    205     canvas.translate(-SkIntToScalar(mask->fBounds.left()),
    206                      -SkIntToScalar(mask->fBounds.top()));
    207 
    208     SkPaint paint;
    209     paint.setAntiAlias(true);
    210     canvas.drawRRect(rrect, paint);
    211     return true;
    212 }
    213 
    214 static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
    215     if (!prepare_to_draw_into_mask(rects[0], mask)) {
    216         return false;
    217     }
    218 
    219     SkBitmap bitmap;
    220     bitmap.setConfig(SkBitmap::kA8_Config,
    221                      mask->fBounds.width(), mask->fBounds.height(),
    222                      mask->fRowBytes);
    223     bitmap.setPixels(mask->fImage);
    224 
    225     SkCanvas canvas(bitmap);
    226     canvas.translate(-SkIntToScalar(mask->fBounds.left()),
    227                      -SkIntToScalar(mask->fBounds.top()));
    228 
    229     SkPaint paint;
    230     paint.setAntiAlias(true);
    231 
    232     if (1 == count) {
    233         canvas.drawRect(rects[0], paint);
    234     } else {
    235         // todo: do I need a fast way to do this?
    236         SkPath path;
    237         path.addRect(rects[0]);
    238         path.addRect(rects[1]);
    239         path.setFillType(SkPath::kEvenOdd_FillType);
    240         canvas.drawPath(path, paint);
    241     }
    242     return true;
    243 }
    244 
    245 static bool rect_exceeds(const SkRect& r, SkScalar v) {
    246     return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
    247            r.width() > v || r.height() > v;
    248 }
    249 
    250 SkMaskFilter::FilterReturn
    251 SkBlurMaskFilterImpl::filterRRectToNine(const SkRRect& rrect, const SkMatrix& matrix,
    252                                         const SkIRect& clipBounds,
    253                                         NinePatch* patch) const {
    254     SkASSERT(patch != NULL);
    255     switch (rrect.getType()) {
    256         case SkRRect::kUnknown_Type:
    257             // Unknown should never be returned.
    258             SkASSERT(false);
    259             // Fall through.
    260         case SkRRect::kEmpty_Type:
    261             // Nothing to draw.
    262             return kFalse_FilterReturn;
    263 
    264         case SkRRect::kRect_Type:
    265             // We should have caught this earlier.
    266             SkASSERT(false);
    267             // Fall through.
    268         case SkRRect::kOval_Type:
    269             // The nine patch special case does not handle ovals, and we
    270             // already have code for rectangles.
    271             return kUnimplemented_FilterReturn;
    272 
    273         case SkRRect::kSimple_Type:
    274             // Fall through.
    275         case SkRRect::kComplex_Type:
    276             // These can take advantage of this fast path.
    277             break;
    278     }
    279 
    280     // TODO: report correct metrics for innerstyle, where we do not grow the
    281     // total bounds, but we do need an inset the size of our blur-radius
    282     if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
    283         return kUnimplemented_FilterReturn;
    284     }
    285 
    286     // TODO: take clipBounds into account to limit our coordinates up front
    287     // for now, just skip too-large src rects (to take the old code path).
    288     if (rect_exceeds(rrect.rect(), SkIntToScalar(32767))) {
    289         return kUnimplemented_FilterReturn;
    290     }
    291 
    292     SkIPoint margin;
    293     SkMask  srcM, dstM;
    294     rrect.rect().roundOut(&srcM.fBounds);
    295     srcM.fImage = NULL;
    296     srcM.fFormat = SkMask::kA8_Format;
    297     srcM.fRowBytes = 0;
    298 
    299     if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
    300         return kFalse_FilterReturn;
    301     }
    302 
    303     // Now figure out the appropriate width and height of the smaller round rectangle
    304     // to stretch. It will take into account the larger radius per side as well as double
    305     // the margin, to account for inner and outer blur.
    306     const SkVector& UL = rrect.radii(SkRRect::kUpperLeft_Corner);
    307     const SkVector& UR = rrect.radii(SkRRect::kUpperRight_Corner);
    308     const SkVector& LR = rrect.radii(SkRRect::kLowerRight_Corner);
    309     const SkVector& LL = rrect.radii(SkRRect::kLowerLeft_Corner);
    310 
    311     const SkScalar leftUnstretched = SkTMax(UL.fX, LL.fX) + SkIntToScalar(2 * margin.fX);
    312     const SkScalar rightUnstretched = SkTMax(UR.fX, LR.fX) + SkIntToScalar(2 * margin.fX);
    313 
    314     // Extra space in the middle to ensure an unchanging piece for stretching. Use 3 to cover
    315     // any fractional space on either side plus 1 for the part to stretch.
    316     const SkScalar stretchSize = SkIntToScalar(3);
    317 
    318     const SkScalar totalSmallWidth = leftUnstretched + rightUnstretched + stretchSize;
    319     if (totalSmallWidth >= rrect.rect().width()) {
    320         // There is no valid piece to stretch.
    321         return kUnimplemented_FilterReturn;
    322     }
    323 
    324     const SkScalar topUnstretched = SkTMax(UL.fY, UR.fY) + SkIntToScalar(2 * margin.fY);
    325     const SkScalar bottomUnstretched = SkTMax(LL.fY, LR.fY) + SkIntToScalar(2 * margin.fY);
    326 
    327     const SkScalar totalSmallHeight = topUnstretched + bottomUnstretched + stretchSize;
    328     if (totalSmallHeight >= rrect.rect().height()) {
    329         // There is no valid piece to stretch.
    330         return kUnimplemented_FilterReturn;
    331     }
    332 
    333     SkRect smallR = SkRect::MakeWH(totalSmallWidth, totalSmallHeight);
    334 
    335     SkRRect smallRR;
    336     SkVector radii[4];
    337     radii[SkRRect::kUpperLeft_Corner] = UL;
    338     radii[SkRRect::kUpperRight_Corner] = UR;
    339     radii[SkRRect::kLowerRight_Corner] = LR;
    340     radii[SkRRect::kLowerLeft_Corner] = LL;
    341     smallRR.setRectRadii(smallR, radii);
    342 
    343     if (!draw_rrect_into_mask(smallRR, &srcM)) {
    344         return kFalse_FilterReturn;
    345     }
    346 
    347     SkAutoMaskFreeImage amf(srcM.fImage);
    348 
    349     if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
    350         return kFalse_FilterReturn;
    351     }
    352 
    353     patch->fMask.fBounds.offsetTo(0, 0);
    354     patch->fOuterRect = dstM.fBounds;
    355     patch->fCenter.fX = SkScalarCeilToInt(leftUnstretched) + 1;
    356     patch->fCenter.fY = SkScalarCeilToInt(topUnstretched) + 1;
    357     return kTrue_FilterReturn;
    358 }
    359 
    360 #ifdef SK_IGNORE_FAST_RECT_BLUR
    361 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", false, "Use the faster analytic blur approach for ninepatch rects" );
    362 #else
    363 SK_CONF_DECLARE( bool, c_analyticBlurNinepatch, "mask.filter.analyticNinePatch", true, "Use the faster analytic blur approach for ninepatch rects" );
    364 #endif
    365 
    366 SkMaskFilter::FilterReturn
    367 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
    368                                         const SkMatrix& matrix,
    369                                         const SkIRect& clipBounds,
    370                                         NinePatch* patch) const {
    371     if (count < 1 || count > 2) {
    372         return kUnimplemented_FilterReturn;
    373     }
    374 
    375     // TODO: report correct metrics for innerstyle, where we do not grow the
    376     // total bounds, but we do need an inset the size of our blur-radius
    377     if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
    378         return kUnimplemented_FilterReturn;
    379     }
    380 
    381     // TODO: take clipBounds into account to limit our coordinates up front
    382     // for now, just skip too-large src rects (to take the old code path).
    383     if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
    384         return kUnimplemented_FilterReturn;
    385     }
    386 
    387     SkIPoint margin;
    388     SkMask  srcM, dstM;
    389     rects[0].roundOut(&srcM.fBounds);
    390     srcM.fImage = NULL;
    391     srcM.fFormat = SkMask::kA8_Format;
    392     srcM.fRowBytes = 0;
    393 
    394     bool filterResult = false;
    395     if (count == 1 && c_analyticBlurNinepatch) {
    396         // special case for fast rect blur
    397         // don't actually do the blur the first time, just compute the correct size
    398         filterResult = this->filterRectMask(&dstM, rects[0], matrix, &margin,
    399                                             SkMask::kJustComputeBounds_CreateMode);
    400     } else {
    401         filterResult = this->filterMask(&dstM, srcM, matrix, &margin);
    402     }
    403 
    404     if (!filterResult) {
    405         return kFalse_FilterReturn;
    406     }
    407 
    408     /*
    409      *  smallR is the smallest version of 'rect' that will still guarantee that
    410      *  we get the same blur results on all edges, plus 1 center row/col that is
    411      *  representative of the extendible/stretchable edges of the ninepatch.
    412      *  Since our actual edge may be fractional we inset 1 more to be sure we
    413      *  don't miss any interior blur.
    414      *  x is an added pixel of blur, and { and } are the (fractional) edge
    415      *  pixels from the original rect.
    416      *
    417      *   x x { x x .... x x } x x
    418      *
    419      *  Thus, in this case, we inset by a total of 5 (on each side) beginning
    420      *  with our outer-rect (dstM.fBounds)
    421      */
    422     SkRect smallR[2];
    423     SkIPoint center;
    424 
    425     // +2 is from +1 for each edge (to account for possible fractional edges
    426     int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
    427     int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
    428     SkIRect innerIR;
    429 
    430     if (1 == count) {
    431         innerIR = srcM.fBounds;
    432         center.set(smallW, smallH);
    433     } else {
    434         SkASSERT(2 == count);
    435         rects[1].roundIn(&innerIR);
    436         center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
    437                    smallH + (innerIR.top() - srcM.fBounds.top()));
    438     }
    439 
    440     // +1 so we get a clean, stretchable, center row/col
    441     smallW += 1;
    442     smallH += 1;
    443 
    444     // we want the inset amounts to be integral, so we don't change any
    445     // fractional phase on the fRight or fBottom of our smallR.
    446     const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
    447     const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
    448     if (dx < 0 || dy < 0) {
    449         // we're too small, relative to our blur, to break into nine-patch,
    450         // so we ask to have our normal filterMask() be called.
    451         return kUnimplemented_FilterReturn;
    452     }
    453 
    454     smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
    455     SkASSERT(!smallR[0].isEmpty());
    456     if (2 == count) {
    457         smallR[1].set(rects[1].left(), rects[1].top(),
    458                       rects[1].right() - dx, rects[1].bottom() - dy);
    459         SkASSERT(!smallR[1].isEmpty());
    460     }
    461 
    462     if (count > 1 || !c_analyticBlurNinepatch) {
    463         if (!drawRectsIntoMask(smallR, count, &srcM)) {
    464             return kFalse_FilterReturn;
    465         }
    466 
    467         SkAutoMaskFreeImage amf(srcM.fImage);
    468 
    469         if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
    470             return kFalse_FilterReturn;
    471         }
    472     } else {
    473         if (!this->filterRectMask(&patch->fMask, smallR[0], matrix, &margin,
    474                                   SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
    475             return kFalse_FilterReturn;
    476         }
    477     }
    478     patch->fMask.fBounds.offsetTo(0, 0);
    479     patch->fOuterRect = dstM.fBounds;
    480     patch->fCenter = center;
    481     return kTrue_FilterReturn;
    482 }
    483 
    484 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
    485                                              SkRect* dst) const {
    486     SkScalar gpuPad, rasterPad;
    487 
    488     {
    489         // GPU path
    490         SkScalar sigma = SkScalarMul(fRadius, kBLUR_SIGMA_SCALE);
    491         gpuPad = sigma * 3.0f;
    492     }
    493 
    494     {
    495         // raster path
    496         SkScalar radius = SkScalarMul(fRadius, SkBlurMask::kBlurRadiusFudgeFactor);
    497 
    498         radius = (radius + .5f) * 2.f;
    499 
    500         rasterPad = SkIntToScalar(SkScalarRoundToInt(radius * 3)/2);
    501     }
    502 
    503     SkScalar pad = SkMaxScalar(gpuPad, rasterPad);
    504 
    505     dst->set(src.fLeft  - pad, src.fTop    - pad,
    506              src.fRight + pad, src.fBottom + pad);
    507 }
    508 
    509 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
    510         : SkMaskFilter(buffer) {
    511     fRadius = buffer.readScalar();
    512     fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
    513     fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
    514     SkASSERT(fRadius >= 0);
    515     SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
    516 }
    517 
    518 void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
    519     this->INHERITED::flatten(buffer);
    520     buffer.writeScalar(fRadius);
    521     buffer.writeInt(fBlurStyle);
    522     buffer.writeUInt(fBlurFlags);
    523 }
    524 
    525 #if SK_SUPPORT_GPU
    526 
    527 bool SkBlurMaskFilterImpl::canFilterMaskGPU(const SkRect& srcBounds,
    528                                             const SkIRect& clipBounds,
    529                                             const SkMatrix& ctm,
    530                                             SkRect* maskRect) const {
    531     SkScalar xformedRadius = this->computeXformedRadius(ctm);
    532     if (xformedRadius <= 0) {
    533         return false;
    534     }
    535 
    536     static const SkScalar kMIN_GPU_BLUR_SIZE = SkIntToScalar(64);
    537     static const SkScalar kMIN_GPU_BLUR_RADIUS = SkIntToScalar(32);
    538 
    539     if (srcBounds.width() <= kMIN_GPU_BLUR_SIZE &&
    540         srcBounds.height() <= kMIN_GPU_BLUR_SIZE &&
    541         xformedRadius <= kMIN_GPU_BLUR_RADIUS) {
    542         // We prefer to blur small rect with small radius via CPU.
    543         return false;
    544     }
    545 
    546     if (NULL == maskRect) {
    547         // don't need to compute maskRect
    548         return true;
    549     }
    550 
    551     float sigma3 = 3 * SkScalarToFloat(xformedRadius) * kBLUR_SIGMA_SCALE;
    552 
    553     SkRect clipRect = SkRect::MakeFromIRect(clipBounds);
    554     SkRect srcRect(srcBounds);
    555 
    556     // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
    557     srcRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
    558     clipRect.outset(SkFloatToScalar(sigma3), SkFloatToScalar(sigma3));
    559     srcRect.intersect(clipRect);
    560     *maskRect = srcRect;
    561     return true;
    562 }
    563 
    564 bool SkBlurMaskFilterImpl::filterMaskGPU(GrTexture* src,
    565                                          const SkRect& maskRect,
    566                                          GrTexture** result,
    567                                          bool canOverwriteSrc) const {
    568     SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());
    569 
    570     GrContext* context = src->getContext();
    571 
    572     GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
    573 
    574     SkScalar xformedRadius = this->computeXformedRadius(context->getMatrix());
    575     SkASSERT(xformedRadius > 0);
    576 
    577     float sigma = SkScalarToFloat(xformedRadius) * kBLUR_SIGMA_SCALE;
    578 
    579     // If we're doing a normal blur, we can clobber the pathTexture in the
    580     // gaussianBlur.  Otherwise, we need to save it for later compositing.
    581     bool isNormalBlur = (SkBlurMaskFilter::kNormal_BlurStyle == fBlurStyle);
    582     *result = SkGpuBlurUtils::GaussianBlur(context, src, isNormalBlur && canOverwriteSrc,
    583                                            clipRect, false, sigma, sigma);
    584     if (NULL == *result) {
    585         return false;
    586     }
    587 
    588     if (!isNormalBlur) {
    589         context->setIdentityMatrix();
    590         GrPaint paint;
    591         SkMatrix matrix;
    592         matrix.setIDiv(src->width(), src->height());
    593         // Blend pathTexture over blurTexture.
    594         GrContext::AutoRenderTarget art(context, (*result)->asRenderTarget());
    595         paint.addColorEffect(GrSimpleTextureEffect::Create(src, matrix))->unref();
    596         if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
    597             // inner:  dst = dst * src
    598             paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
    599         } else if (SkBlurMaskFilter::kSolid_BlurStyle == fBlurStyle) {
    600             // solid:  dst = src + dst - src * dst
    601             //             = (1 - dst) * src + 1 * dst
    602             paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
    603         } else if (SkBlurMaskFilter::kOuter_BlurStyle == fBlurStyle) {
    604             // outer:  dst = dst * (1 - src)
    605             //             = 0 * src + (1 - src) * dst
    606             paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
    607         }
    608         context->drawRect(paint, clipRect);
    609     }
    610 
    611     return true;
    612 }
    613 
    614 #endif // SK_SUPPORT_GPU
    615 
    616 
    617 #ifdef SK_DEVELOPER
    618 void SkBlurMaskFilterImpl::toString(SkString* str) const {
    619     str->append("SkBlurMaskFilterImpl: (");
    620 
    621     str->append("radius: ");
    622     str->appendScalar(fRadius);
    623     str->append(" ");
    624 
    625     static const char* gStyleName[SkBlurMaskFilter::kBlurStyleCount] = {
    626         "normal", "solid", "outer", "inner"
    627     };
    628 
    629     str->appendf("style: %s ", gStyleName[fBlurStyle]);
    630     str->append("flags: (");
    631     if (fBlurFlags) {
    632         bool needSeparator = false;
    633         SkAddFlagToString(str,
    634                           SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag),
    635                           "IgnoreXform", &needSeparator);
    636         SkAddFlagToString(str,
    637                           SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag),
    638                           "HighQuality", &needSeparator);
    639     } else {
    640         str->append("None");
    641     }
    642     str->append("))");
    643 }
    644 #endif
    645 
    646 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
    647     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
    648 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
    649