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 "SkCanvas.h"
11#include "SkColorSpace_Base.h"
12#include "SkFuzzLogging.h"
13#include "SkImageFilterCache.h"
14#include "SkLocalMatrixImageFilter.h"
15#include "SkMatrixImageFilter.h"
16#include "SkReadBuffer.h"
17#include "SkRect.h"
18#include "SkSpecialImage.h"
19#include "SkSpecialSurface.h"
20#include "SkValidationUtils.h"
21#include "SkWriteBuffer.h"
22#if SK_SUPPORT_GPU
23#include "GrContext.h"
24#include "GrFixedClip.h"
25#include "GrRenderTargetContext.h"
26#include "GrTextureProxy.h"
27#include "SkGr.h"
28#endif
29
30#ifndef SK_IGNORE_TO_STRING
31void SkImageFilter::CropRect::toString(SkString* str) const {
32    if (!fFlags) {
33        return;
34    }
35
36    str->appendf("cropRect (");
37    if (fFlags & CropRect::kHasLeft_CropEdge) {
38        str->appendf("%.2f, ", fRect.fLeft);
39    } else {
40        str->appendf("X, ");
41    }
42    if (fFlags & CropRect::kHasTop_CropEdge) {
43        str->appendf("%.2f, ", fRect.fTop);
44    } else {
45        str->appendf("X, ");
46    }
47    if (fFlags & CropRect::kHasWidth_CropEdge) {
48        str->appendf("%.2f, ", fRect.width());
49    } else {
50        str->appendf("X, ");
51    }
52    if (fFlags & CropRect::kHasHeight_CropEdge) {
53        str->appendf("%.2f", fRect.height());
54    } else {
55        str->appendf("X");
56    }
57    str->appendf(") ");
58}
59#endif
60
61void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds,
62                                      const SkMatrix& ctm,
63                                      bool embiggen,
64                                      SkIRect* cropped) const {
65    *cropped = imageBounds;
66    if (fFlags) {
67        SkRect devCropR;
68        ctm.mapRect(&devCropR, fRect);
69        SkIRect devICropR = devCropR.roundOut();
70
71        // Compute the left/top first, in case we need to modify the right/bottom for a missing edge
72        if (fFlags & kHasLeft_CropEdge) {
73            if (embiggen || devICropR.fLeft > cropped->fLeft) {
74                cropped->fLeft = devICropR.fLeft;
75            }
76        } else {
77            devICropR.fRight = cropped->fLeft + devICropR.width();
78        }
79        if (fFlags & kHasTop_CropEdge) {
80            if (embiggen || devICropR.fTop > cropped->fTop) {
81                cropped->fTop = devICropR.fTop;
82            }
83        } else {
84            devICropR.fBottom = cropped->fTop + devICropR.height();
85        }
86        if (fFlags & kHasWidth_CropEdge) {
87            if (embiggen || devICropR.fRight < cropped->fRight) {
88                cropped->fRight = devICropR.fRight;
89            }
90        }
91        if (fFlags & kHasHeight_CropEdge) {
92            if (embiggen || devICropR.fBottom < cropped->fBottom) {
93                cropped->fBottom = devICropR.fBottom;
94            }
95        }
96    }
97}
98
99///////////////////////////////////////////////////////////////////////////////////////////////////
100
101static int32_t next_image_filter_unique_id() {
102    static int32_t gImageFilterUniqueID;
103
104    // Never return 0.
105    int32_t id;
106    do {
107        id = sk_atomic_inc(&gImageFilterUniqueID) + 1;
108    } while (0 == id);
109    return id;
110}
111
112void SkImageFilter::Common::allocInputs(int count) {
113    fInputs.reset(count);
114}
115
116bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) {
117    const int count = buffer.readInt();
118    if (!buffer.validate(count >= 0)) {
119        return false;
120    }
121    if (!buffer.validate(expectedCount < 0 || count == expectedCount)) {
122        return false;
123    }
124
125    SkFUZZF(("allocInputs: %d\n", count));
126    this->allocInputs(count);
127    for (int i = 0; i < count; i++) {
128        if (buffer.readBool()) {
129            fInputs[i] = sk_sp<SkImageFilter>(buffer.readImageFilter());
130        }
131        if (!buffer.isValid()) {
132            return false;
133        }
134    }
135    SkRect rect;
136    buffer.readRect(&rect);
137    if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) {
138        return false;
139    }
140
141    uint32_t flags = buffer.readUInt();
142    fCropRect = CropRect(rect, flags);
143    return buffer.isValid();
144}
145
146///////////////////////////////////////////////////////////////////////////////////////////////////
147
148void SkImageFilter::init(sk_sp<SkImageFilter> const* inputs,
149                         int inputCount,
150                         const CropRect* cropRect) {
151    fCropRect = cropRect ? *cropRect : CropRect(SkRect(), 0x0);
152
153    fInputs.reset(inputCount);
154
155    for (int i = 0; i < inputCount; ++i) {
156        if (!inputs[i] || inputs[i]->usesSrcInput()) {
157            fUsesSrcInput = true;
158        }
159        fInputs[i] = inputs[i];
160    }
161}
162
163SkImageFilter::SkImageFilter(sk_sp<SkImageFilter> const* inputs,
164                             int inputCount,
165                             const CropRect* cropRect)
166    : fUsesSrcInput(false)
167    , fUniqueID(next_image_filter_unique_id()) {
168    this->init(inputs, inputCount, cropRect);
169}
170
171SkImageFilter::~SkImageFilter() {
172    SkImageFilterCache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count());
173}
174
175SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer)
176    : fUsesSrcInput(false)
177    , fCropRect(SkRect(), 0x0)
178    , fUniqueID(next_image_filter_unique_id()) {
179    Common common;
180    if (common.unflatten(buffer, inputCount)) {
181        this->init(common.inputs(), common.inputCount(), &common.cropRect());
182    }
183}
184
185void SkImageFilter::flatten(SkWriteBuffer& buffer) const {
186    buffer.writeInt(fInputs.count());
187    for (int i = 0; i < fInputs.count(); i++) {
188        SkImageFilter* input = this->getInput(i);
189        buffer.writeBool(input != nullptr);
190        if (input != nullptr) {
191            buffer.writeFlattenable(input);
192        }
193    }
194    buffer.writeRect(fCropRect.rect());
195    buffer.writeUInt(fCropRect.flags());
196}
197
198sk_sp<SkSpecialImage> SkImageFilter::filterImage(SkSpecialImage* src, const Context& context,
199                                                 SkIPoint* offset) const {
200    SkASSERT(src && offset);
201
202    uint32_t srcGenID = fUsesSrcInput ? src->uniqueID() : 0;
203    const SkIRect srcSubset = fUsesSrcInput ? src->subset() : SkIRect::MakeWH(0, 0);
204    SkImageFilterCacheKey key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID, srcSubset);
205    if (context.cache()) {
206        sk_sp<SkSpecialImage> result = context.cache()->get(key, offset);
207        if (result) {
208            return result;
209        }
210    }
211
212    sk_sp<SkSpecialImage> result(this->onFilterImage(src, context, offset));
213
214#if SK_SUPPORT_GPU
215    if (src->isTextureBacked() && result && !result->isTextureBacked()) {
216        // Keep the result on the GPU - this is still required for some
217        // image filters that don't support GPU in all cases
218        GrContext* context = src->getContext();
219        result = result->makeTextureImage(context);
220    }
221#endif
222
223    if (result && context.cache()) {
224        context.cache()->set(key, result.get(), *offset, this);
225        SkAutoMutexAcquire mutex(fMutex);
226        fCacheKeys.push_back(key);
227    }
228
229    return result;
230}
231
232void SkImageFilter::removeKey(const SkImageFilterCacheKey& key) const {
233    SkAutoMutexAcquire mutex(fMutex);
234    for (int i = 0; i < fCacheKeys.count(); i++) {
235        if (fCacheKeys[i] == key) {
236            fCacheKeys.removeShuffle(i);
237            break;
238        }
239    }
240#ifdef SK_DEBUG
241    for (int i = 0; i < fCacheKeys.count(); i++) {
242        if (fCacheKeys[i] == key) {
243            SkASSERT(false);
244        }
245    }
246#endif
247}
248
249SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
250                                 MapDirection direction) const {
251    if (kReverse_MapDirection == direction) {
252        SkIRect bounds = this->onFilterNodeBounds(src, ctm, direction);
253        return this->onFilterBounds(bounds, ctm, direction);
254    } else {
255        SkIRect bounds = this->onFilterBounds(src, ctm, direction);
256        bounds = this->onFilterNodeBounds(bounds, ctm, direction);
257        SkIRect dst;
258        this->getCropRect().applyTo(bounds, ctm, this->affectsTransparentBlack(), &dst);
259        return dst;
260    }
261}
262
263SkRect SkImageFilter::computeFastBounds(const SkRect& src) const {
264    if (0 == this->countInputs()) {
265        return src;
266    }
267    SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
268    for (int i = 1; i < this->countInputs(); i++) {
269        SkImageFilter* input = this->getInput(i);
270        if (input) {
271            combinedBounds.join(input->computeFastBounds(src));
272        } else {
273            combinedBounds.join(src);
274        }
275    }
276    return combinedBounds;
277}
278
279bool SkImageFilter::canComputeFastBounds() const {
280    if (this->affectsTransparentBlack()) {
281        return false;
282    }
283    for (int i = 0; i < this->countInputs(); i++) {
284        SkImageFilter* input = this->getInput(i);
285        if (input && !input->canComputeFastBounds()) {
286            return false;
287        }
288    }
289    return true;
290}
291
292#if SK_SUPPORT_GPU
293sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context,
294                                                sk_sp<GrFragmentProcessor> fp,
295                                                const SkIRect& bounds,
296                                                const OutputProperties& outputProperties) {
297    GrPaint paint;
298    paint.addColorFragmentProcessor(std::move(fp));
299    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
300
301    sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace());
302    GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get());
303    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
304        SkBackingFit::kApprox, bounds.width(), bounds.height(), config, std::move(colorSpace)));
305    if (!renderTargetContext) {
306        return nullptr;
307    }
308    paint.setGammaCorrect(renderTargetContext->isGammaCorrect());
309
310    SkIRect dstIRect = SkIRect::MakeWH(bounds.width(), bounds.height());
311    SkRect srcRect = SkRect::Make(bounds);
312    SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
313    GrFixedClip clip(dstIRect);
314    renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
315                                        srcRect);
316
317    return SkSpecialImage::MakeDeferredFromGpu(context, dstIRect,
318                                               kNeedNewImageUniqueID_SpecialImage,
319                                               renderTargetContext->asTextureProxyRef(),
320                                               renderTargetContext->refColorSpace());
321}
322#endif
323
324bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
325    SkASSERT(nullptr != filterPtr);
326    if (!this->isColorFilterNode(filterPtr)) {
327        return false;
328    }
329    if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) {
330        (*filterPtr)->unref();
331        return false;
332    }
333    return true;
334}
335
336bool SkImageFilter::canHandleComplexCTM() const {
337    if (!this->onCanHandleComplexCTM()) {
338        return false;
339    }
340    const int count = this->countInputs();
341    for (int i = 0; i < count; ++i) {
342        SkImageFilter* input = this->getInput(i);
343        if (input && !input->canHandleComplexCTM()) {
344            return false;
345        }
346    }
347    return true;
348}
349
350bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
351                                  SkIRect* dstBounds) const {
352    SkIRect temp = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
353    fCropRect.applyTo(temp, ctx.ctm(), this->affectsTransparentBlack(), dstBounds);
354    // Intersect against the clip bounds, in case the crop rect has
355    // grown the bounds beyond the original clip. This can happen for
356    // example in tiling, where the clip is much smaller than the filtered
357    // primitive. If we didn't do this, we would be processing the filter
358    // at the full crop rect size in every tile.
359    return dstBounds->intersect(ctx.clipBounds());
360}
361
362#if SK_SUPPORT_GPU
363sk_sp<SkSpecialImage> SkImageFilter::ImageToColorSpace(SkSpecialImage* src,
364                                                       const OutputProperties& outProps) {
365    // There are several conditions that determine if we actually need to convert the source to the
366    // destination's color space. Rather than duplicate that logic here, just try to make an xform
367    // object. If that produces something, then both are tagged, and the source is in a different
368    // gamut than the dest. There is some overhead to making the xform, but those are cached, and
369    // if we get one back, that means we're about to use it during the conversion anyway.
370    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(src->getColorSpace(),
371                                                                       outProps.colorSpace());
372
373    if (!colorSpaceXform) {
374        // No xform needed, just return the original image
375        return sk_ref_sp(src);
376    }
377
378    sk_sp<SkSpecialSurface> surf(src->makeSurface(outProps,
379                                                  SkISize::Make(src->width(), src->height())));
380    if (!surf) {
381        return sk_ref_sp(src);
382    }
383
384    SkCanvas* canvas = surf->getCanvas();
385    SkASSERT(canvas);
386    SkPaint p;
387    p.setBlendMode(SkBlendMode::kSrc);
388    src->draw(canvas, 0, 0, &p);
389    return surf->makeImageSnapshot();
390}
391#endif
392
393// Return a larger (newWidth x newHeight) copy of 'src' with black padding
394// around it.
395static sk_sp<SkSpecialImage> pad_image(SkSpecialImage* src,
396                                       const SkImageFilter::OutputProperties& outProps,
397                                       int newWidth, int newHeight, int offX, int offY) {
398    // We would like to operate in the source's color space (so that we return an "identical"
399    // image, other than the padding. To achieve that, we'd create new output properties:
400    //
401    // SkImageFilter::OutputProperties outProps(src->getColorSpace());
402    //
403    // That fails in at least two ways. For formats that are texturable but not renderable (like
404    // F16 on some ES implementations), we can't create a surface to do the work. For sRGB, images
405    // may be tagged with an sRGB color space (which leads to an sRGB config in makeSurface). But
406    // the actual config of that sRGB image on a device with no sRGB support is non-sRGB.
407    //
408    // Rather than try to special case these situations, we execute the image padding in the
409    // destination color space. This should not affect the output of the DAG in (almost) any case,
410    // because the result of this call is going to be used as an input, where it would have been
411    // switched to the destination space anyway. The one exception would be a filter that expected
412    // to consume unclamped F16 data, but the padded version of the image is pre-clamped to 8888.
413    // We can revisit this logic if that ever becomes an actual problem.
414    sk_sp<SkSpecialSurface> surf(src->makeSurface(outProps, SkISize::Make(newWidth, newHeight)));
415    if (!surf) {
416        return nullptr;
417    }
418
419    SkCanvas* canvas = surf->getCanvas();
420    SkASSERT(canvas);
421
422    canvas->clear(0x0);
423
424    src->draw(canvas, offX, offY, nullptr);
425
426    return surf->makeImageSnapshot();
427}
428
429sk_sp<SkSpecialImage> SkImageFilter::applyCropRect(const Context& ctx,
430                                                   SkSpecialImage* src,
431                                                   SkIPoint* srcOffset,
432                                                   SkIRect* bounds) const {
433    const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset->x(), srcOffset->y(),
434                                                src->width(), src->height());
435
436    SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
437    fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds);
438    if (!bounds->intersect(ctx.clipBounds())) {
439        return nullptr;
440    }
441
442    if (srcBounds.contains(*bounds)) {
443        return sk_sp<SkSpecialImage>(SkRef(src));
444    } else {
445        sk_sp<SkSpecialImage> img(pad_image(src, ctx.outputProperties(),
446                                            bounds->width(), bounds->height(),
447                                            srcOffset->x() - bounds->x(),
448                                            srcOffset->y() - bounds->y()));
449        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
450        return img;
451    }
452}
453
454SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
455                                      MapDirection direction) const {
456    if (this->countInputs() < 1) {
457        return src;
458    }
459
460    SkIRect totalBounds;
461    for (int i = 0; i < this->countInputs(); ++i) {
462        SkImageFilter* filter = this->getInput(i);
463        SkIRect rect = filter ? filter->filterBounds(src, ctm, direction) : src;
464        if (0 == i) {
465            totalBounds = rect;
466        } else {
467            totalBounds.join(rect);
468        }
469    }
470
471    return totalBounds;
472}
473
474SkIRect SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const {
475    return src;
476}
477
478
479SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
480    SkIRect clipBounds = this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(),
481                                                  MapDirection::kReverse_MapDirection);
482    return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.outputProperties());
483}
484
485sk_sp<SkImageFilter> SkImageFilter::MakeMatrixFilter(const SkMatrix& matrix,
486                                                     SkFilterQuality filterQuality,
487                                                     sk_sp<SkImageFilter> input) {
488    return SkMatrixImageFilter::Make(matrix, filterQuality, std::move(input));
489}
490
491sk_sp<SkImageFilter> SkImageFilter::makeWithLocalMatrix(const SkMatrix& matrix) const {
492    // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter
493    // is *always* treated as a const ptr. Hence the const-cast here.
494    //
495    SkImageFilter* nonConstThis = const_cast<SkImageFilter*>(this);
496    return SkLocalMatrixImageFilter::Make(matrix, sk_ref_sp<SkImageFilter>(nonConstThis));
497}
498
499sk_sp<SkSpecialImage> SkImageFilter::filterInput(int index,
500                                                 SkSpecialImage* src,
501                                                 const Context& ctx,
502                                                 SkIPoint* offset) const {
503    SkImageFilter* input = this->getInput(index);
504    if (!input) {
505        return sk_sp<SkSpecialImage>(SkRef(src));
506    }
507
508    sk_sp<SkSpecialImage> result(input->filterImage(src, this->mapContext(ctx), offset));
509
510    SkASSERT(!result || src->isTextureBacked() == result->isTextureBacked());
511
512    return result;
513}
514
515void SkImageFilter::PurgeCache() {
516    SkImageFilterCache::Get()->purge();
517}
518