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 "SkColorFilterImageFilter.h"
9
10#include "SkCanvas.h"
11#include "SkColorFilter.h"
12#include "SkReadBuffer.h"
13#include "SkSpecialImage.h"
14#include "SkSpecialSurface.h"
15#include "SkWriteBuffer.h"
16
17sk_sp<SkImageFilter> SkColorFilterImageFilter::Make(sk_sp<SkColorFilter> cf,
18                                                    sk_sp<SkImageFilter> input,
19                                                    const CropRect* cropRect) {
20    if (!cf) {
21        return nullptr;
22    }
23
24    SkColorFilter* inputCF;
25    if (input && input->isColorFilterNode(&inputCF)) {
26        // This is an optimization, as it collapses the hierarchy by just combining the two
27        // colorfilters into a single one, which the new imagefilter will wrap.
28        sk_sp<SkColorFilter> newCF(SkColorFilter::MakeComposeFilter(cf,// can't move bc of fallthru
29                                                                    sk_sp<SkColorFilter>(inputCF)));
30        if (newCF) {
31            return sk_sp<SkImageFilter>(new SkColorFilterImageFilter(std::move(newCF),
32                                                                     sk_ref_sp(input->getInput(0)),
33                                                                     cropRect));
34        }
35    }
36
37    return sk_sp<SkImageFilter>(new SkColorFilterImageFilter(std::move(cf),
38                                                             std::move(input),
39                                                             cropRect));
40}
41
42SkColorFilterImageFilter::SkColorFilterImageFilter(sk_sp<SkColorFilter> cf,
43                                                   sk_sp<SkImageFilter> input,
44                                                   const CropRect* cropRect)
45    : INHERITED(&input, 1, cropRect)
46    , fColorFilter(std::move(cf)) {
47}
48
49sk_sp<SkFlattenable> SkColorFilterImageFilter::CreateProc(SkReadBuffer& buffer) {
50    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
51    sk_sp<SkColorFilter> cf(buffer.readColorFilter());
52    return Make(std::move(cf), common.getInput(0), &common.cropRect());
53}
54
55void SkColorFilterImageFilter::flatten(SkWriteBuffer& buffer) const {
56    this->INHERITED::flatten(buffer);
57    buffer.writeFlattenable(fColorFilter.get());
58}
59
60sk_sp<SkSpecialImage> SkColorFilterImageFilter::onFilterImage(SkSpecialImage* source,
61                                                              const Context& ctx,
62                                                              SkIPoint* offset) const {
63    SkIPoint inputOffset = SkIPoint::Make(0, 0);
64    sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
65
66    SkIRect inputBounds;
67    if (fColorFilter->affectsTransparentBlack()) {
68        // If the color filter affects transparent black, the bounds are the entire clip.
69        inputBounds = ctx.clipBounds();
70    } else if (!input) {
71        return nullptr;
72    } else {
73        inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
74                                        input->width(), input->height());
75    }
76
77    SkIRect bounds;
78    if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
79        return nullptr;
80    }
81
82    sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
83    if (!surf) {
84        return nullptr;
85    }
86
87    SkCanvas* canvas = surf->getCanvas();
88    SkASSERT(canvas);
89
90    SkPaint paint;
91
92    paint.setBlendMode(SkBlendMode::kSrc);
93    paint.setColorFilter(fColorFilter);
94
95    // TODO: it may not be necessary to clear or drawPaint inside the input bounds
96    // (see skbug.com/5075)
97    if (fColorFilter->affectsTransparentBlack()) {
98        // The subsequent input->draw() call may not fill the entire canvas. For filters which
99        // affect transparent black, ensure that the filter is applied everywhere.
100        paint.setColor(SK_ColorTRANSPARENT);
101        canvas->drawPaint(paint);
102        paint.setColor(SK_ColorBLACK);
103    } else {
104        canvas->clear(0x0);
105    }
106
107    if (input) {
108        input->draw(canvas,
109                    SkIntToScalar(inputOffset.fX - bounds.fLeft),
110                    SkIntToScalar(inputOffset.fY - bounds.fTop),
111                    &paint);
112    }
113
114    offset->fX = bounds.fLeft;
115    offset->fY = bounds.fTop;
116    return surf->makeImageSnapshot();
117}
118
119bool SkColorFilterImageFilter::onIsColorFilterNode(SkColorFilter** filter) const {
120    SkASSERT(1 == this->countInputs());
121    if (!this->cropRectIsSet()) {
122        if (filter) {
123            *filter = SkRef(fColorFilter.get());
124        }
125        return true;
126    }
127    return false;
128}
129
130bool SkColorFilterImageFilter::affectsTransparentBlack() const {
131    return fColorFilter->affectsTransparentBlack();
132}
133
134#ifndef SK_IGNORE_TO_STRING
135void SkColorFilterImageFilter::toString(SkString* str) const {
136    str->appendf("SkColorFilterImageFilter: (");
137
138    str->appendf("input: (");
139
140    if (this->getInput(0)) {
141        this->getInput(0)->toString(str);
142    }
143
144    str->appendf(") color filter: ");
145    fColorFilter->toString(str);
146
147    str->append(")");
148}
149#endif
150