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