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
14class SkBlurMaskFilterImpl : public SkMaskFilter {
15public:
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
28protected:
29    virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
30                                           const SkIRect& clipBounds,
31                                           NinePatch*) const SK_OVERRIDE;
32
33private:
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
44SkMaskFilter* 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
58SkBlurMaskFilterImpl::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
77SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
78    return SkMask::kA8_Format;
79}
80
81bool 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
111static 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
148static 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
153SkMaskFilter::FilterReturn
154SkBlurMaskFilterImpl::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
253void 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
259SkBlurMaskFilterImpl::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
268void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
269    this->INHERITED::flatten(buffer);
270    buffer.writeScalar(fRadius);
271    buffer.writeInt(fBlurStyle);
272    buffer.writeUInt(fBlurFlags);
273}
274
275static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = {
276    SkMaskFilter::kNormal_BlurType,
277    SkMaskFilter::kSolid_BlurType,
278    SkMaskFilter::kOuter_BlurType,
279    SkMaskFilter::kInner_BlurType,
280};
281
282SkMaskFilter::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
291SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkBlurMaskFilter)
292    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurMaskFilterImpl)
293SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
294