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 "SkFlattenableBuffers.h"
     12 #include "SkMaskFilter.h"
     13 
     14 class SkBlurMaskFilterImpl : public SkMaskFilter {
     15 public:
     16     SkBlurMaskFilterImpl(SkScalar radius, SkBlurMaskFilter::BlurStyle,
     17                          uint32_t flags);
     18 
     19     // overrides from SkMaskFilter
     20     virtual SkMask::Format getFormat() const SK_OVERRIDE;
     21     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
     22                             SkIPoint* margin) const SK_OVERRIDE;
     23     virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
     24     virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
     25 
     26     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
     27 
     28 protected:
     29     virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
     30                                            const SkIRect& clipBounds,
     31                                            NinePatch*) const SK_OVERRIDE;
     32 
     33 private:
     34     SkScalar                    fRadius;
     35     SkBlurMaskFilter::BlurStyle fBlurStyle;
     36     uint32_t                    fBlurFlags;
     37 
     38     SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
     39     virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
     40 
     41     typedef SkMaskFilter INHERITED;
     42 };
     43 
     44 SkMaskFilter* SkBlurMaskFilter::Create(SkScalar radius,
     45                                        SkBlurMaskFilter::BlurStyle style,
     46                                        uint32_t flags) {
     47     // use !(radius > 0) instead of radius <= 0 to reject NaN values
     48     if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
     49         || flags > SkBlurMaskFilter::kAll_BlurFlag) {
     50         return NULL;
     51     }
     52 
     53     return SkNEW_ARGS(SkBlurMaskFilterImpl, (radius, style, flags));
     54 }
     55 
     56 ///////////////////////////////////////////////////////////////////////////////
     57 
     58 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkScalar radius,
     59                                            SkBlurMaskFilter::BlurStyle style,
     60                                            uint32_t flags)
     61     : fRadius(radius), fBlurStyle(style), fBlurFlags(flags) {
     62 #if 0
     63     fGamma = NULL;
     64     if (gammaScale) {
     65         fGamma = new U8[256];
     66         if (gammaScale > 0)
     67             SkBlurMask::BuildSqrGamma(fGamma, gammaScale);
     68         else
     69             SkBlurMask::BuildSqrtGamma(fGamma, -gammaScale);
     70     }
     71 #endif
     72     SkASSERT(radius >= 0);
     73     SkASSERT((unsigned)style < SkBlurMaskFilter::kBlurStyleCount);
     74     SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
     75 }
     76 
     77 SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
     78     return SkMask::kA8_Format;
     79 }
     80 
     81 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
     82                                       const SkMatrix& matrix,
     83                                       SkIPoint* margin) const{
     84     SkScalar radius;
     85     if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
     86         radius = fRadius;
     87     } else {
     88         radius = matrix.mapRadius(fRadius);
     89     }
     90 
     91     // To avoid unseemly allocation requests (esp. for finite platforms like
     92     // handset) we limit the radius so something manageable. (as opposed to
     93     // a request like 10,000)
     94     static const SkScalar MAX_RADIUS = SkIntToScalar(128);
     95     radius = SkMinScalar(radius, MAX_RADIUS);
     96     SkBlurMask::Quality blurQuality =
     97         (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
     98             SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
     99 
    100 #ifndef SK_DISABLE_SEPARABLE_MASK_BLUR
    101     return SkBlurMask::BlurSeparable(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
    102                             blurQuality, margin);
    103 #else
    104     return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
    105                             blurQuality, margin);
    106 #endif
    107 }
    108 
    109 #include "SkCanvas.h"
    110 
    111 static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
    112     rects[0].roundOut(&mask->fBounds);
    113     mask->fRowBytes = SkAlign4(mask->fBounds.width());
    114     mask->fFormat = SkMask::kA8_Format;
    115     size_t size = mask->computeImageSize();
    116     mask->fImage = SkMask::AllocImage(size);
    117     if (NULL == mask->fImage) {
    118         return false;
    119     }
    120     sk_bzero(mask->fImage, size);
    121 
    122     SkBitmap bitmap;
    123     bitmap.setConfig(SkBitmap::kA8_Config,
    124                      mask->fBounds.width(), mask->fBounds.height(),
    125                      mask->fRowBytes);
    126     bitmap.setPixels(mask->fImage);
    127 
    128     SkCanvas canvas(bitmap);
    129     canvas.translate(-SkIntToScalar(mask->fBounds.left()),
    130                      -SkIntToScalar(mask->fBounds.top()));
    131 
    132     SkPaint paint;
    133     paint.setAntiAlias(true);
    134 
    135     if (1 == count) {
    136         canvas.drawRect(rects[0], paint);
    137     } else {
    138         // todo: do I need a fast way to do this?
    139         SkPath path;
    140         path.addRect(rects[0]);
    141         path.addRect(rects[1]);
    142         path.setFillType(SkPath::kEvenOdd_FillType);
    143         canvas.drawPath(path, paint);
    144     }
    145     return true;
    146 }
    147 
    148 static bool rect_exceeds(const SkRect& r, SkScalar v) {
    149     return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
    150            r.width() > v || r.height() > v;
    151 }
    152 
    153 SkMaskFilter::FilterReturn
    154 SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
    155                                         const SkMatrix& matrix,
    156                                         const SkIRect& clipBounds,
    157                                         NinePatch* patch) const {
    158     if (count < 1 || count > 2) {
    159         return kUnimplemented_FilterReturn;
    160     }
    161 
    162     // TODO: report correct metrics for innerstyle, where we do not grow the
    163     // total bounds, but we do need an inset the size of our blur-radius
    164     if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
    165         return kUnimplemented_FilterReturn;
    166     }
    167 
    168     // TODO: take clipBounds into account to limit our coordinates up front
    169     // for now, just skip too-large src rects (to take the old code path).
    170     if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
    171         return kUnimplemented_FilterReturn;
    172     }
    173 
    174     SkIPoint margin;
    175     SkMask  srcM, dstM;
    176     rects[0].roundOut(&srcM.fBounds);
    177     srcM.fImage = NULL;
    178     srcM.fFormat = SkMask::kA8_Format;
    179     srcM.fRowBytes = 0;
    180     if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
    181         return kFalse_FilterReturn;
    182     }
    183 
    184     /*
    185      *  smallR is the smallest version of 'rect' that will still guarantee that
    186      *  we get the same blur results on all edges, plus 1 center row/col that is
    187      *  representative of the extendible/stretchable edges of the ninepatch.
    188      *  Since our actual edge may be fractional we inset 1 more to be sure we
    189      *  don't miss any interior blur.
    190      *  x is an added pixel of blur, and { and } are the (fractional) edge
    191      *  pixels from the original rect.
    192      *
    193      *   x x { x x .... x x } x x
    194      *
    195      *  Thus, in this case, we inset by a total of 5 (on each side) beginning
    196      *  with our outer-rect (dstM.fBounds)
    197      */
    198     SkRect smallR[2];
    199     SkIPoint center;
    200 
    201     // +2 is from +1 for each edge (to account for possible fractional edges
    202     int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
    203     int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
    204     SkIRect innerIR;
    205 
    206     if (1 == count) {
    207         innerIR = srcM.fBounds;
    208         center.set(smallW, smallH);
    209     } else {
    210         SkASSERT(2 == count);
    211         rects[1].roundIn(&innerIR);
    212         center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
    213                    smallH + (innerIR.top() - srcM.fBounds.top()));
    214     }
    215 
    216     // +1 so we get a clean, stretchable, center row/col
    217     smallW += 1;
    218     smallH += 1;
    219 
    220     // we want the inset amounts to be integral, so we don't change any
    221     // fractional phase on the fRight or fBottom of our smallR.
    222     const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
    223     const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
    224     if (dx < 0 || dy < 0) {
    225         // we're too small, relative to our blur, to break into nine-patch,
    226         // so we ask to have our normal filterMask() be called.
    227         return kUnimplemented_FilterReturn;
    228     }
    229 
    230     smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
    231     SkASSERT(!smallR[0].isEmpty());
    232     if (2 == count) {
    233         smallR[1].set(rects[1].left(), rects[1].top(),
    234                       rects[1].right() - dx, rects[1].bottom() - dy);
    235         SkASSERT(!smallR[1].isEmpty());
    236     }
    237 
    238     if (!drawRectsIntoMask(smallR, count, &srcM)) {
    239         return kFalse_FilterReturn;
    240     }
    241 
    242     SkAutoMaskFreeImage amf(srcM.fImage);
    243 
    244     if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
    245         return kFalse_FilterReturn;
    246     }
    247     patch->fMask.fBounds.offsetTo(0, 0);
    248     patch->fOuterRect = dstM.fBounds;
    249     patch->fCenter = center;
    250     return kTrue_FilterReturn;
    251 }
    252 
    253 void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
    254                                              SkRect* dst) const {
    255     dst->set(src.fLeft - fRadius, src.fTop - fRadius,
    256              src.fRight + fRadius, src.fBottom + fRadius);
    257 }
    258 
    259 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
    260         : SkMaskFilter(buffer) {
    261     fRadius = buffer.readScalar();
    262     fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
    263     fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
    264     SkASSERT(fRadius >= 0);
    265     SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
    266 }
    267 
    268 void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
    269     this->INHERITED::flatten(buffer);
    270     buffer.writeScalar(fRadius);
    271     buffer.writeInt(fBlurStyle);
    272     buffer.writeUInt(fBlurFlags);
    273 }
    274 
    275 static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = {
    276     SkMaskFilter::kNormal_BlurType,
    277     SkMaskFilter::kSolid_BlurType,
    278     SkMaskFilter::kOuter_BlurType,
    279     SkMaskFilter::kInner_BlurType,
    280 };
    281 
    282 SkMaskFilter::BlurType SkBlurMaskFilterImpl::asABlur(BlurInfo* info) const {
    283     if (info) {
    284         info->fRadius = fRadius;
    285         info->fIgnoreTransform = SkToBool(fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
    286         info->fHighQuality = SkToBool(fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag);
    287     }
    288     return gBlurStyle2BlurType[fBlurStyle];
    289 }
    290 
    291 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
    292     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
    293 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
    294