SkMaskFilter.cpp revision ab1c13864df34aecfd4840ea7d1e4f8730b44f4e
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
10#include "SkMaskFilter.h"
11#include "SkBlitter.h"
12#include "SkBounder.h"
13#include "SkDraw.h"
14#include "SkRasterClip.h"
15#include "SkRRect.h"
16#include "SkTypes.h"
17
18#if SK_SUPPORT_GPU
19#include "GrTexture.h"
20#include "SkGr.h"
21#include "SkGrPixelRef.h"
22#endif
23
24bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
25                              SkIPoint*) const {
26    return false;
27}
28
29static void extractMaskSubset(const SkMask& src, SkMask* dst) {
30    SkASSERT(src.fBounds.contains(dst->fBounds));
31
32    const int dx = dst->fBounds.left() - src.fBounds.left();
33    const int dy = dst->fBounds.top() - src.fBounds.top();
34    dst->fImage = src.fImage + dy * src.fRowBytes + dx;
35    dst->fRowBytes = src.fRowBytes;
36    dst->fFormat = src.fFormat;
37}
38
39static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
40                            const SkIRect& bounds, const SkIRect& clipR) {
41    SkIRect r;
42    if (r.intersect(bounds, clipR)) {
43        blitter->blitMask(mask, r);
44    }
45}
46
47static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
48    SkIRect r;
49    if (r.intersect(rect, clipR)) {
50        blitter->blitRect(r.left(), r.top(), r.width(), r.height());
51    }
52}
53
54#if 0
55static void dump(const SkMask& mask) {
56    for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
57        for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
58            SkDebugf("%02X", *mask.getAddr8(x, y));
59        }
60        SkDebugf("\n");
61    }
62    SkDebugf("\n");
63}
64#endif
65
66static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
67                              const SkIPoint& center, bool fillCenter,
68                              const SkIRect& clipR, SkBlitter* blitter) {
69    int cx = center.x();
70    int cy = center.y();
71    SkMask m;
72
73    // top-left
74    m.fBounds = mask.fBounds;
75    m.fBounds.fRight = cx;
76    m.fBounds.fBottom = cy;
77    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
78        extractMaskSubset(mask, &m);
79        m.fBounds.offsetTo(outerR.left(), outerR.top());
80        blitClippedMask(blitter, m, m.fBounds, clipR);
81    }
82
83    // top-right
84    m.fBounds = mask.fBounds;
85    m.fBounds.fLeft = cx + 1;
86    m.fBounds.fBottom = cy;
87    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
88        extractMaskSubset(mask, &m);
89        m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
90        blitClippedMask(blitter, m, m.fBounds, clipR);
91    }
92
93    // bottom-left
94    m.fBounds = mask.fBounds;
95    m.fBounds.fRight = cx;
96    m.fBounds.fTop = cy + 1;
97    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
98        extractMaskSubset(mask, &m);
99        m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
100        blitClippedMask(blitter, m, m.fBounds, clipR);
101    }
102
103    // bottom-right
104    m.fBounds = mask.fBounds;
105    m.fBounds.fLeft = cx + 1;
106    m.fBounds.fTop = cy + 1;
107    if (m.fBounds.width() > 0 && m.fBounds.height() > 0) {
108        extractMaskSubset(mask, &m);
109        m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
110                           outerR.bottom() - m.fBounds.height());
111        blitClippedMask(blitter, m, m.fBounds, clipR);
112    }
113
114    SkIRect innerR;
115    innerR.set(outerR.left() + cx - mask.fBounds.left(),
116               outerR.top() + cy - mask.fBounds.top(),
117               outerR.right() + (cx + 1 - mask.fBounds.right()),
118               outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
119    if (fillCenter) {
120        blitClippedRect(blitter, innerR, clipR);
121    }
122
123    const int innerW = innerR.width();
124    size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
125    SkAutoSMalloc<4*1024> storage(storageSize);
126    int16_t* runs = (int16_t*)storage.get();
127    uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
128
129    SkIRect r;
130    // top
131    r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
132    if (r.intersect(clipR)) {
133        int startY = SkMax32(0, r.top() - outerR.top());
134        int stopY = startY + r.height();
135        int width = r.width();
136        for (int y = startY; y < stopY; ++y) {
137            runs[0] = width;
138            runs[width] = 0;
139            alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
140            blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
141        }
142    }
143    // bottom
144    r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
145    if (r.intersect(clipR)) {
146        int startY = outerR.bottom() - r.bottom();
147        int stopY = startY + r.height();
148        int width = r.width();
149        for (int y = startY; y < stopY; ++y) {
150            runs[0] = width;
151            runs[width] = 0;
152            alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
153            blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
154        }
155    }
156    // left
157    r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
158    if (r.intersect(clipR)) {
159        int startX = r.left() - outerR.left();
160        int stopX = startX + r.width();
161        int height = r.height();
162        for (int x = startX; x < stopX; ++x) {
163            blitter->blitV(outerR.left() + x, r.top(), height,
164                           *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
165        }
166    }
167    // right
168    r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
169    if (r.intersect(clipR)) {
170        int startX = outerR.right() - r.right();
171        int stopX = startX + r.width();
172        int height = r.height();
173        for (int x = startX; x < stopX; ++x) {
174            blitter->blitV(outerR.right() - x - 1, r.top(), height,
175                           *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
176        }
177    }
178}
179
180static void draw_nine(const SkMask& mask, const SkIRect& outerR,
181                      const SkIPoint& center, bool fillCenter,
182                      const SkRasterClip& clip, SkBounder* bounder,
183                      SkBlitter* blitter) {
184    // if we get here, we need to (possibly) resolve the clip and blitter
185    SkAAClipBlitterWrapper wrapper(clip, blitter);
186    blitter = wrapper.getBlitter();
187
188    SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
189
190    if (!clipper.done() && (!bounder || bounder->doIRect(outerR))) {
191        const SkIRect& cr = clipper.rect();
192        do {
193            draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
194            clipper.next();
195        } while (!clipper.done());
196    }
197}
198
199static int countNestedRects(const SkPath& path, SkRect rects[2]) {
200    if (path.isNestedRects(rects)) {
201        return 2;
202    }
203    return path.isRect(&rects[0]);
204}
205
206bool SkMaskFilter::filterRRect(const SkRRect& devRRect, const SkMatrix& matrix,
207                               const SkRasterClip& clip, SkBounder* bounder,
208                               SkBlitter* blitter, SkPaint::Style style) const {
209    // Attempt to speed up drawing by creating a nine patch. If a nine patch
210    // cannot be used, return false to allow our caller to recover and perform
211    // the drawing another way.
212    NinePatch patch;
213    patch.fMask.fImage = NULL;
214    if (kTrue_FilterReturn != this->filterRRectToNine(devRRect, matrix,
215                                                      clip.getBounds(),
216                                                      &patch)) {
217        SkASSERT(NULL == patch.fMask.fImage);
218        return false;
219    }
220    draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter, true, clip,
221              bounder, blitter);
222    SkMask::FreeImage(patch.fMask.fImage);
223    return true;
224}
225
226bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
227                              const SkRasterClip& clip, SkBounder* bounder,
228                              SkBlitter* blitter, SkPaint::Style style) const {
229    SkRect rects[2];
230    int rectCount = 0;
231    if (SkPaint::kFill_Style == style) {
232        rectCount = countNestedRects(devPath, rects);
233    }
234    if (rectCount > 0) {
235        NinePatch patch;
236
237        patch.fMask.fImage = NULL;
238        switch (this->filterRectsToNine(rects, rectCount, matrix,
239                                        clip.getBounds(), &patch)) {
240            case kFalse_FilterReturn:
241                SkASSERT(NULL == patch.fMask.fImage);
242                return false;
243
244            case kTrue_FilterReturn:
245                draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter,
246                          1 == rectCount, clip, bounder, blitter);
247                SkMask::FreeImage(patch.fMask.fImage);
248                return true;
249
250            case kUnimplemented_FilterReturn:
251                SkASSERT(NULL == patch.fMask.fImage);
252                // fall through
253                break;
254        }
255    }
256
257    SkMask  srcM, dstM;
258
259    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
260                            SkMask::kComputeBoundsAndRenderImage_CreateMode,
261                            style)) {
262        return false;
263    }
264    SkAutoMaskFreeImage autoSrc(srcM.fImage);
265
266    if (!this->filterMask(&dstM, srcM, matrix, NULL)) {
267        return false;
268    }
269    SkAutoMaskFreeImage autoDst(dstM.fImage);
270
271    // if we get here, we need to (possibly) resolve the clip and blitter
272    SkAAClipBlitterWrapper wrapper(clip, blitter);
273    blitter = wrapper.getBlitter();
274
275    SkRegion::Cliperator clipper(wrapper.getRgn(), dstM.fBounds);
276
277    if (!clipper.done() && (bounder == NULL || bounder->doIRect(dstM.fBounds))) {
278        const SkIRect& cr = clipper.rect();
279        do {
280            blitter->blitMask(dstM, cr);
281            clipper.next();
282        } while (!clipper.done());
283    }
284
285    return true;
286}
287
288SkMaskFilter::FilterReturn
289SkMaskFilter::filterRRectToNine(const SkRRect&, const SkMatrix&,
290                                const SkIRect& clipBounds, NinePatch*) const {
291    return kUnimplemented_FilterReturn;
292}
293
294SkMaskFilter::FilterReturn
295SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
296                                const SkIRect& clipBounds, NinePatch*) const {
297    return kUnimplemented_FilterReturn;
298}
299
300#if SK_SUPPORT_GPU
301bool SkMaskFilter::asNewEffect(GrEffectRef** effect, GrTexture*) const {
302    return false;
303}
304
305bool SkMaskFilter::canFilterMaskGPU(const SkRect& devBounds,
306                                    const SkIRect& clipBounds,
307                                    const SkMatrix& ctm,
308                                    SkRect* maskRect) const {
309    return false;
310}
311
312bool SkMaskFilter::filterMaskGPU(GrContext* context,
313                                 const SkBitmap& srcBM,
314                                 const SkRect& maskRect,
315                                 SkBitmap* resultBM) const {
316    SkAutoTUnref<GrTexture> src;
317    bool canOverwriteSrc = false;
318    if (NULL == srcBM.getTexture()) {
319        GrTextureDesc desc;
320        // Needs to be a render target to be overwritten in filterMaskGPU
321        desc.fFlags     = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
322        desc.fConfig    = SkBitmapConfig2GrPixelConfig(srcBM.config());
323        desc.fWidth     = srcBM.width();
324        desc.fHeight    = srcBM.height();
325
326        // TODO: right now this is exact to guard against out of bounds reads
327        // by the filter code. More thought needs to be devoted to the
328        // "filterMaskGPU" contract and then enforced (i.e., clamp the code
329        // in "filterMaskGPU" so it never samples beyond maskRect)
330        GrAutoScratchTexture ast(context, desc, GrContext::kExact_ScratchTexMatch);
331        if (NULL == ast.texture()) {
332            return false;
333        }
334
335        SkAutoLockPixels alp(srcBM);
336        ast.texture()->writePixels(0, 0, srcBM.width(), srcBM.height(),
337                                   desc.fConfig,
338                                   srcBM.getPixels(), srcBM.rowBytes());
339
340        src.reset(ast.detach());
341        canOverwriteSrc = true;
342    } else {
343        src.reset((GrTexture*) srcBM.getTexture());
344        src.get()->ref();
345    }
346    GrTexture* dst;
347
348    bool result = this->filterMaskGPU(src, maskRect, &dst, canOverwriteSrc);
349    if (!result) {
350        return false;
351    }
352
353    resultBM->setConfig(srcBM.config(), dst->width(), dst->height());
354    resultBM->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (dst)))->unref();
355    dst->unref();
356    return true;
357}
358
359bool SkMaskFilter::filterMaskGPU(GrTexture* src,
360                                 const SkRect& maskRect,
361                                 GrTexture** result,
362                                 bool canOverwriteSrc) const {
363    return false;
364}
365#endif
366
367void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
368    SkMask  srcM, dstM;
369
370    srcM.fImage = NULL;
371    src.roundOut(&srcM.fBounds);
372    srcM.fRowBytes = 0;
373    srcM.fFormat = SkMask::kA8_Format;
374
375    SkIPoint margin;    // ignored
376    if (this->filterMask(&dstM, srcM, SkMatrix::I(), &margin)) {
377        dst->set(dstM.fBounds);
378    } else {
379        dst->set(srcM.fBounds);
380    }
381}
382