1/*
2 * Copyright 2013 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/SkXfermodeImageFilter.h"
9
10#include "include/core/SkCanvas.h"
11#include "include/private/SkColorData.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#if SK_SUPPORT_GPU
18#include "include/private/GrRecordingContext.h"
19#include "src/gpu/GrCaps.h"
20#include "src/gpu/GrClip.h"
21#include "src/gpu/GrColorSpaceXform.h"
22#include "src/gpu/GrRecordingContextPriv.h"
23#include "src/gpu/GrRenderTargetContext.h"
24#include "src/gpu/GrTextureProxy.h"
25#include "src/gpu/SkGr.h"
26#include "src/gpu/effects/GrTextureEffect.h"
27#include "src/gpu/effects/generated/GrConstColorProcessor.h"
28#endif
29#include "src/core/SkClipOpPriv.h"
30
31namespace {
32
33class SkXfermodeImageFilterImpl : public SkImageFilter_Base {
34public:
35 SkXfermodeImageFilterImpl(SkBlendMode mode, sk_sp<SkImageFilter> inputs[2],
36 const CropRect* cropRect)
37 : INHERITED(inputs, 2, cropRect)
38 , fMode(mode) {}
39
40protected:
41 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
42
43 SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
44 MapDirection, const SkIRect* inputRect) const override;
45
46#if SK_SUPPORT_GPU
47 sk_sp<SkSpecialImage> filterImageGPU(const Context& ctx,
48 sk_sp<SkSpecialImage> background,
49 const SkIPoint& backgroundOffset,
50 sk_sp<SkSpecialImage> foreground,
51 const SkIPoint& foregroundOffset,
52 const SkIRect& bounds) const;
53#endif
54
55 void flatten(SkWriteBuffer&) const override;
56
57 void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const;
58#if SK_SUPPORT_GPU
59 std::unique_ptr<GrFragmentProcessor> makeFGFrag(
60 std::unique_ptr<GrFragmentProcessor> bgFP) const;
61#endif
62
63private:
64 friend void SkXfermodeImageFilter::RegisterFlattenables();
65 SK_FLATTENABLE_HOOKS(SkXfermodeImageFilterImpl)
66
67 SkBlendMode fMode;
68
69 typedef SkImageFilter_Base INHERITED;
70};
71
72} // end namespace
73
74sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(SkBlendMode mode,
75 sk_sp<SkImageFilter> background,
76 sk_sp<SkImageFilter> foreground,
77 const SkImageFilter::CropRect* cropRect) {
78 sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
79 return sk_sp<SkImageFilter>(new SkXfermodeImageFilterImpl(mode, inputs, cropRect));
80}
81
82void SkXfermodeImageFilter::RegisterFlattenables() {
83 SK_REGISTER_FLATTENABLE(SkXfermodeImageFilterImpl);
84 // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
85 SkFlattenable::Register("SkXfermodeImageFilter_Base", SkXfermodeImageFilterImpl::CreateProc);
86}
87
88///////////////////////////////////////////////////////////////////////////////////////////////////
89
90static unsigned unflatten_blendmode(SkReadBuffer& buffer) {
91 unsigned mode = buffer.read32();
92 (void)buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode);
93 return mode;
94}
95
96sk_sp<SkFlattenable> SkXfermodeImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
97 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
98 unsigned mode = unflatten_blendmode(buffer);
99 if (!buffer.isValid()) {
100 return nullptr;
101 }
102 return SkXfermodeImageFilter::Make((SkBlendMode)mode, common.getInput(0),
103 common.getInput(1), &common.cropRect());
104}
105
106void SkXfermodeImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
107 this->INHERITED::flatten(buffer);
108 buffer.write32((unsigned)fMode);
109}
110
111sk_sp<SkSpecialImage> SkXfermodeImageFilterImpl::onFilterImage(const Context& ctx,
112 SkIPoint* offset) const {
113 SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
114 sk_sp<SkSpecialImage> background(this->filterInput(0, ctx, &backgroundOffset));
115
116 SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
117 sk_sp<SkSpecialImage> foreground(this->filterInput(1, ctx, &foregroundOffset));
118
119 SkIRect foregroundBounds = SkIRect::MakeEmpty();
120 if (foreground) {
121 foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(),
122 foreground->width(), foreground->height());
123 }
124
125 SkIRect srcBounds = SkIRect::MakeEmpty();
126 if (background) {
127 srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(),
128 background->width(), background->height());
129 }
130
131 srcBounds.join(foregroundBounds);
132 if (srcBounds.isEmpty()) {
133 return nullptr;
134 }
135
136 SkIRect bounds;
137 if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
138 return nullptr;
139 }
140
141 offset->fX = bounds.left();
142 offset->fY = bounds.top();
143
144#if SK_SUPPORT_GPU
145 if (ctx.gpuBacked()) {
146 return this->filterImageGPU(ctx, background, backgroundOffset,
147 foreground, foregroundOffset, bounds);
148 }
149#endif
150
151 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(bounds.size()));
152 if (!surf) {
153 return nullptr;
154 }
155
156 SkCanvas* canvas = surf->getCanvas();
157 SkASSERT(canvas);
158
159 canvas->clear(0x0); // can't count on background to fully clear the background
160 canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
161
162 if (background) {
163 SkPaint paint;
164 paint.setBlendMode(SkBlendMode::kSrc);
165 background->draw(canvas,
166 SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY),
167 &paint);
168 }
169
170 this->drawForeground(canvas, foreground.get(), foregroundBounds);
171
172 return surf->makeImageSnapshot();
173}
174
175SkIRect SkXfermodeImageFilterImpl::onFilterBounds(const SkIRect& src,
176 const SkMatrix& ctm,
177 MapDirection dir,
178 const SkIRect* inputRect) const {
179 if (kReverse_MapDirection == dir) {
180 return INHERITED::onFilterBounds(src, ctm, dir, inputRect);
181 }
182
183 SkASSERT(!inputRect);
184 SkASSERT(2 == this->countInputs());
185 auto getBackground = [&]() {
186 return this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, dir, inputRect) : src;
187 };
188 auto getForeground = [&]() {
189 return this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, inputRect) : src;
190 };
191 switch (fMode) {
192 case SkBlendMode::kClear:
193 return SkIRect::MakeEmpty();
194
195 case SkBlendMode::kSrc:
196 case SkBlendMode::kDstATop:
197 return getForeground();
198
199 case SkBlendMode::kDst:
200 case SkBlendMode::kSrcATop:
201 return getBackground();
202
203 case SkBlendMode::kSrcIn:
204 case SkBlendMode::kDstIn: {
205 auto result = getBackground();
206 if (!result.intersect(getForeground())) {
207 return SkIRect::MakeEmpty();
208 }
209 return result;
210 }
211
212 default: {
213 auto result = getBackground();
214 result.join(getForeground());
215 return result;
216 }
217 }
218}
219
220void SkXfermodeImageFilterImpl::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
221 const SkIRect& fgBounds) const {
222 SkPaint paint;
223 paint.setBlendMode(fMode);
224 if (img) {
225 img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop), &paint);
226 }
227
228 SkAutoCanvasRestore acr(canvas, true);
229 canvas->clipRect(SkRect::Make(fgBounds), kDifference_SkClipOp);
230 paint.setColor(0);
231 canvas->drawPaint(paint);
232}
233
234#if SK_SUPPORT_GPU
235
236#include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
237
238sk_sp<SkSpecialImage> SkXfermodeImageFilterImpl::filterImageGPU(
239 const Context& ctx,
240 sk_sp<SkSpecialImage> background,
241 const SkIPoint& backgroundOffset,
242 sk_sp<SkSpecialImage> foreground,
243 const SkIPoint& foregroundOffset,
244 const SkIRect& bounds) const {
245 SkASSERT(ctx.gpuBacked());
246
247 auto context = ctx.getContext();
248
249 GrSurfaceProxyView backgroundView, foregroundView;
250
251 if (background) {
252 backgroundView = background->view(context);
253 }
254
255 if (foreground) {
256 foregroundView = foreground->view(context);
257 }
258
259 GrPaint paint;
260 std::unique_ptr<GrFragmentProcessor> bgFP;
261 const auto& caps = *ctx.getContext()->priv().caps();
262 GrSamplerState sampler(GrSamplerState::WrapMode::kClampToBorder,
263 GrSamplerState::Filter::kNearest);
264
265 if (backgroundView.asTextureProxy()) {
266 SkRect bgSubset = SkRect::Make(background->subset());
267 SkMatrix bgMatrix = SkMatrix::MakeTrans(
268 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
269 SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
270 bgFP = GrTextureEffect::MakeSubset(std::move(backgroundView), background->alphaType(),
271 bgMatrix, sampler, bgSubset, caps);
272 bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
273 background->alphaType(),
274 ctx.colorSpace());
275 } else {
276 bgFP = GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT,
277 GrConstColorProcessor::InputMode::kIgnore);
278 }
279
280 if (foregroundView.asTextureProxy()) {
281 SkRect fgSubset = SkRect::Make(foreground->subset());
282 SkMatrix fgMatrix = SkMatrix::MakeTrans(
283 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
284 SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
285 auto fgFP = GrTextureEffect::MakeSubset(std::move(foregroundView), foreground->alphaType(),
286 fgMatrix, sampler, fgSubset, caps);
287 fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP),
288 foreground->getColorSpace(),
289 foreground->alphaType(),
290 ctx.colorSpace());
291 paint.addColorFragmentProcessor(std::move(fgFP));
292
293 std::unique_ptr<GrFragmentProcessor> xferFP = this->makeFGFrag(std::move(bgFP));
294
295 // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed
296 if (xferFP) {
297 paint.addColorFragmentProcessor(std::move(xferFP));
298 }
299 } else {
300 paint.addColorFragmentProcessor(std::move(bgFP));
301 }
302
303 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
304
305 auto renderTargetContext = GrRenderTargetContext::Make(
306 context, ctx.grColorType(), ctx.refColorSpace(), SkBackingFit::kApprox, bounds.size());
307 if (!renderTargetContext) {
308 return nullptr;
309 }
310
311 SkMatrix matrix;
312 matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
313 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix,
314 SkRect::Make(bounds));
315
316 return SkSpecialImage::MakeDeferredFromGpu(context,
317 SkIRect::MakeWH(bounds.width(), bounds.height()),
318 kNeedNewImageUniqueID_SpecialImage,
319 renderTargetContext->readSurfaceView(),
320 renderTargetContext->colorInfo().colorType(),
321 renderTargetContext->colorInfo().refColorSpace());
322}
323
324std::unique_ptr<GrFragmentProcessor> SkXfermodeImageFilterImpl::makeFGFrag(
325 std::unique_ptr<GrFragmentProcessor> bgFP) const {
326 return GrXfermodeFragmentProcessor::MakeFromDstProcessor(std::move(bgFP), fMode);
327}
328
329#endif
330