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