SkImageFilter.cpp revision bd7746da97e389c1068333117407b213b378f9db
1/*
2 * Copyright 2012 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkImageFilter.h"
9
10#include "SkBitmap.h"
11#include "SkDevice.h"
12#include "SkReadBuffer.h"
13#include "SkWriteBuffer.h"
14#include "SkRect.h"
15#include "SkTDynamicHash.h"
16#include "SkValidationUtils.h"
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#include "SkGrPixelRef.h"
20#include "SkGr.h"
21#endif
22
23SkImageFilter::Cache* gExternalCache;
24
25SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect)
26  : fInputCount(inputCount),
27    fInputs(new SkImageFilter*[inputCount]),
28    fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)) {
29    for (int i = 0; i < inputCount; ++i) {
30        fInputs[i] = inputs[i];
31        SkSafeRef(fInputs[i]);
32    }
33}
34
35SkImageFilter::~SkImageFilter() {
36    for (int i = 0; i < fInputCount; i++) {
37        SkSafeUnref(fInputs[i]);
38    }
39    delete[] fInputs;
40}
41
42SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) {
43    fInputCount = buffer.readInt();
44    if (buffer.validate((fInputCount >= 0) && ((inputCount < 0) || (fInputCount == inputCount)))) {
45        fInputs = new SkImageFilter*[fInputCount];
46        for (int i = 0; i < fInputCount; i++) {
47            if (buffer.readBool()) {
48                fInputs[i] = buffer.readImageFilter();
49            } else {
50                fInputs[i] = NULL;
51            }
52            if (!buffer.isValid()) {
53                fInputCount = i; // Do not use fInputs past that point in the destructor
54                break;
55            }
56        }
57        SkRect rect;
58        buffer.readRect(&rect);
59        if (buffer.isValid() && buffer.validate(SkIsValidRect(rect))) {
60            uint32_t flags = buffer.readUInt();
61            fCropRect = CropRect(rect, flags);
62        }
63    } else {
64        fInputCount = 0;
65        fInputs = NULL;
66    }
67}
68
69void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
70    buffer.writeInt(fInputCount);
71    for (int i = 0; i < fInputCount; i++) {
72        SkImageFilter* input = getInput(i);
73        buffer.writeBool(input != NULL);
74        if (input != NULL) {
75            buffer.writeFlattenable(input);
76        }
77    }
78    buffer.writeRect(fCropRect.rect());
79    buffer.writeUInt(fCropRect.flags());
80}
81
82bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
83                                const Context& context,
84                                SkBitmap* result, SkIPoint* offset) const {
85    Cache* cache = context.cache();
86    SkASSERT(result);
87    SkASSERT(offset);
88    SkASSERT(cache);
89    if (cache->get(this, result, offset)) {
90        return true;
91    }
92    /*
93     *  Give the proxy first shot at the filter. If it returns false, ask
94     *  the filter to do it.
95     */
96    if ((proxy && proxy->filterImage(this, src, context, result, offset)) ||
97        this->onFilterImage(proxy, src, context, result, offset)) {
98        cache->set(this, *result, *offset);
99        return true;
100    }
101    return false;
102}
103
104bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
105                                 SkIRect* dst) const {
106    SkASSERT(&src);
107    SkASSERT(dst);
108    if (SkImageFilter::GetExternalCache()) {
109        /*
110         *  When the external cache is active, do not intersect the saveLayer
111         *  bounds with the clip bounds. This is so that the cached result
112         *  is always the full size of the primitive's bounds,
113         *  regardless of the clip active on first draw.
114         */
115        *dst = SkIRect::MakeLargest();
116        return true;
117    }
118    return this->onFilterBounds(src, ctm, dst);
119}
120
121void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
122    if (0 == fInputCount) {
123        *dst = src;
124        return;
125    }
126    if (this->getInput(0)) {
127        this->getInput(0)->computeFastBounds(src, dst);
128    } else {
129        *dst = src;
130    }
131    for (int i = 1; i < fInputCount; i++) {
132        SkImageFilter* input = this->getInput(i);
133        if (input) {
134            SkRect bounds;
135            input->computeFastBounds(src, &bounds);
136            dst->join(bounds);
137        } else {
138            dst->join(src);
139        }
140    }
141}
142
143bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&,
144                                  SkBitmap*, SkIPoint*) const {
145    return false;
146}
147
148bool SkImageFilter::canFilterImageGPU() const {
149    return this->asNewEffect(NULL, NULL, SkMatrix::I(), SkIRect());
150}
151
152bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
153                                   SkBitmap* result, SkIPoint* offset) const {
154#if SK_SUPPORT_GPU
155    SkBitmap input = src;
156    SkASSERT(fInputCount == 1);
157    SkIPoint srcOffset = SkIPoint::Make(0, 0);
158    if (this->getInput(0) &&
159        !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
160        return false;
161    }
162    GrTexture* srcTexture = input.getTexture();
163    SkIRect bounds;
164    if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) {
165        return false;
166    }
167    SkRect srcRect = SkRect::Make(bounds);
168    SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
169    GrContext* context = srcTexture->getContext();
170
171    GrTextureDesc desc;
172    desc.fFlags = kRenderTarget_GrTextureFlagBit,
173    desc.fWidth = bounds.width();
174    desc.fHeight = bounds.height();
175    desc.fConfig = kRGBA_8888_GrPixelConfig;
176
177    GrAutoScratchTexture dst(context, desc);
178    GrContext::AutoMatrix am;
179    am.setIdentity(context);
180    GrContext::AutoRenderTarget art(context, dst.texture()->asRenderTarget());
181    GrContext::AutoClip acs(context, dstRect);
182    GrEffect* effect;
183    offset->fX = bounds.left();
184    offset->fY = bounds.top();
185    bounds.offset(-srcOffset);
186    SkMatrix matrix(ctx.ctm());
187    matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
188    this->asNewEffect(&effect, srcTexture, matrix, bounds);
189    SkASSERT(effect);
190    GrPaint paint;
191    paint.addColorEffect(effect)->unref();
192    context->drawRectToRect(paint, dstRect, srcRect);
193
194    SkAutoTUnref<GrTexture> resultTex(dst.detach());
195    WrapTexture(resultTex, bounds.width(), bounds.height(), result);
196    return true;
197#else
198    return false;
199#endif
200}
201
202bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src,
203                                  const SkIPoint& srcOffset, SkIRect* bounds) const {
204    SkIRect srcBounds;
205    src.getBounds(&srcBounds);
206    srcBounds.offset(srcOffset);
207    SkRect cropRect;
208    ctx.ctm().mapRect(&cropRect, fCropRect.rect());
209    SkIRect cropRectI;
210    cropRect.roundOut(&cropRectI);
211    uint32_t flags = fCropRect.flags();
212    if (flags & CropRect::kHasLeft_CropEdge) srcBounds.fLeft = cropRectI.fLeft;
213    if (flags & CropRect::kHasTop_CropEdge) srcBounds.fTop = cropRectI.fTop;
214    if (flags & CropRect::kHasRight_CropEdge) srcBounds.fRight = cropRectI.fRight;
215    if (flags & CropRect::kHasBottom_CropEdge) srcBounds.fBottom = cropRectI.fBottom;
216    if (!srcBounds.intersect(ctx.clipBounds())) {
217        return false;
218    }
219    *bounds = srcBounds;
220    return true;
221}
222
223bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
224                                  SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const {
225    SkIRect srcBounds;
226    src.getBounds(&srcBounds);
227    srcBounds.offset(*srcOffset);
228    SkRect cropRect;
229    ctx.ctm().mapRect(&cropRect, fCropRect.rect());
230    SkIRect cropRectI;
231    cropRect.roundOut(&cropRectI);
232    uint32_t flags = fCropRect.flags();
233    *bounds = srcBounds;
234    if (flags & CropRect::kHasLeft_CropEdge) bounds->fLeft = cropRectI.fLeft;
235    if (flags & CropRect::kHasTop_CropEdge) bounds->fTop = cropRectI.fTop;
236    if (flags & CropRect::kHasRight_CropEdge) bounds->fRight = cropRectI.fRight;
237    if (flags & CropRect::kHasBottom_CropEdge) bounds->fBottom = cropRectI.fBottom;
238    if (!bounds->intersect(ctx.clipBounds())) {
239        return false;
240    }
241    if (srcBounds.contains(*bounds)) {
242        *dst = src;
243        return true;
244    } else {
245        SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
246        if (!device) {
247            return false;
248        }
249        SkCanvas canvas(device);
250        canvas.clear(0x00000000);
251        canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
252        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
253        *dst = device->accessBitmap(false);
254        return true;
255    }
256}
257
258bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
259                                   SkIRect* dst) const {
260    if (fInputCount < 1) {
261        return false;
262    }
263
264    SkIRect bounds;
265    for (int i = 0; i < fInputCount; ++i) {
266        SkImageFilter* filter = this->getInput(i);
267        SkIRect rect = src;
268        if (filter && !filter->filterBounds(src, ctm, &rect)) {
269            return false;
270        }
271        if (0 == i) {
272            bounds = rect;
273        } else {
274            bounds.join(rect);
275        }
276    }
277
278    // don't modify dst until now, so we don't accidentally change it in the
279    // loop, but then return false on the next filter.
280    *dst = bounds;
281    return true;
282}
283
284bool SkImageFilter::asNewEffect(GrEffect**, GrTexture*, const SkMatrix&, const SkIRect&) const {
285    return false;
286}
287
288bool SkImageFilter::asColorFilter(SkColorFilter**) const {
289    return false;
290}
291
292void SkImageFilter::SetExternalCache(Cache* cache) {
293    SkRefCnt_SafeAssign(gExternalCache, cache);
294}
295
296SkImageFilter::Cache* SkImageFilter::GetExternalCache() {
297    return gExternalCache;
298}
299
300#if SK_SUPPORT_GPU
301
302void SkImageFilter::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
303    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
304    result->setInfo(info);
305    result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
306}
307
308bool SkImageFilter::getInputResultGPU(SkImageFilter::Proxy* proxy,
309                                      const SkBitmap& src, const Context& ctx,
310                                      SkBitmap* result, SkIPoint* offset) const {
311    // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
312    // matrix with no clip and that the matrix, clip, and render target set before this function was
313    // called are restored before we return to the caller.
314    GrContext* context = src.getTexture()->getContext();
315    GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
316    if (this->canFilterImageGPU()) {
317        return this->filterImageGPU(proxy, src, ctx, result, offset);
318    } else {
319        if (this->filterImage(proxy, src, ctx, result, offset)) {
320            if (!result->getTexture()) {
321                const SkImageInfo info = result->info();
322                if (kUnknown_SkColorType == info.colorType()) {
323                    return false;
324                }
325                GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context, *result, NULL);
326                result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
327                GrUnlockAndUnrefCachedBitmapTexture(resultTex);
328            }
329            return true;
330        } else {
331            return false;
332        }
333    }
334}
335#endif
336
337static uint32_t compute_hash(const uint32_t* data, int count) {
338    uint32_t hash = 0;
339
340    for (int i = 0; i < count; ++i) {
341        uint32_t k = data[i];
342        k *= 0xcc9e2d51;
343        k = (k << 15) | (k >> 17);
344        k *= 0x1b873593;
345
346        hash ^= k;
347        hash = (hash << 13) | (hash >> 19);
348        hash *= 5;
349        hash += 0xe6546b64;
350    }
351
352    //    hash ^= size;
353    hash ^= hash >> 16;
354    hash *= 0x85ebca6b;
355    hash ^= hash >> 13;
356    hash *= 0xc2b2ae35;
357    hash ^= hash >> 16;
358
359    return hash;
360}
361
362class CacheImpl : public SkImageFilter::Cache {
363public:
364    explicit CacheImpl(int minChildren) : fMinChildren(minChildren) {
365        SkASSERT(fMinChildren <= 2);
366    }
367
368    virtual ~CacheImpl();
369    bool get(const SkImageFilter* key, SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
370    void set(const SkImageFilter* key, const SkBitmap& result, const SkIPoint& offset) SK_OVERRIDE;
371    void remove(const SkImageFilter* key) SK_OVERRIDE;
372private:
373    typedef const SkImageFilter* Key;
374    struct Value {
375        Value(Key key, const SkBitmap& bitmap, const SkIPoint& offset)
376            : fKey(key), fBitmap(bitmap), fOffset(offset) {}
377        Key fKey;
378        SkBitmap fBitmap;
379        SkIPoint fOffset;
380        static const Key& GetKey(const Value& v) {
381            return v.fKey;
382        }
383        static uint32_t Hash(Key key) {
384            return compute_hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key) / sizeof(uint32_t));
385        }
386    };
387    SkTDynamicHash<Value, Key> fData;
388    int fMinChildren;
389};
390
391bool CacheImpl::get(const SkImageFilter* key, SkBitmap* result, SkIPoint* offset) {
392    Value* v = fData.find(key);
393    if (v) {
394        *result = v->fBitmap;
395        *offset = v->fOffset;
396        return true;
397    }
398    return false;
399}
400
401void CacheImpl::remove(const SkImageFilter* key) {
402    Value* v = fData.find(key);
403    if (v) {
404        fData.remove(key);
405        delete v;
406    }
407}
408
409void CacheImpl::set(const SkImageFilter* key, const SkBitmap& result, const SkIPoint& offset) {
410    if (fMinChildren < 2 || !key->unique()) {
411        // We take !key->unique() as a signal that there are probably at least 2 refs on the key,
412        // meaning this filter probably has at least two children and is worth caching when
413        // fMinChildren is 2.  If fMinChildren is less than two, we'll just always cache.
414        fData.add(new Value(key, result, offset));
415    }
416}
417
418SkImageFilter::Cache* SkImageFilter::Cache::Create(int minChildren) {
419    return new CacheImpl(minChildren);
420}
421
422CacheImpl::~CacheImpl() {
423    SkTDynamicHash<Value, Key>::Iter iter(&fData);
424
425    while (!iter.done()) {
426        Value* v = &*iter;
427        ++iter;
428        delete v;
429    }
430}
431