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 "SkXfermodeImageFilter.h"
9 #include "SkArithmeticImageFilter.h"
10 #include "SkCanvas.h"
11 #include "SkColorData.h"
12 #include "SkColorSpaceXformer.h"
13 #include "SkImageFilterPriv.h"
14 #include "SkReadBuffer.h"
15 #include "SkSpecialImage.h"
16 #include "SkSpecialSurface.h"
17 #include "SkWriteBuffer.h"
18 #if SK_SUPPORT_GPU
19 #include "GrClip.h"
20 #include "GrColorSpaceXform.h"
21 #include "GrContext.h"
22 #include "GrRenderTargetContext.h"
23 #include "GrTextureProxy.h"
24
25 #include "effects/GrConstColorProcessor.h"
26 #include "effects/GrTextureDomain.h"
27 #include "effects/GrSimpleTextureEffect.h"
28 #include "SkGr.h"
29 #endif
30 #include "SkClipOpPriv.h"
31
32 class SkXfermodeImageFilter_Base : public SkImageFilter {
33 public:
34 SkXfermodeImageFilter_Base(SkBlendMode mode, sk_sp<SkImageFilter> inputs[2],
35 const CropRect* cropRect);
36
37 protected:
38 sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
39 SkIPoint* offset) const override;
40 sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
41
42 SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
43 MapDirection, const SkIRect* inputRect) const override;
44
45 #if SK_SUPPORT_GPU
46 sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
47 sk_sp<SkSpecialImage> background,
48 const SkIPoint& backgroundOffset,
49 sk_sp<SkSpecialImage> foreground,
50 const SkIPoint& foregroundOffset,
51 const SkIRect& bounds,
52 const OutputProperties& outputProperties) 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
63 private:
64 SK_FLATTENABLE_HOOKS(SkXfermodeImageFilter_Base)
65
66 SkBlendMode fMode;
67
68 friend class SkXfermodeImageFilter;
69
70 typedef SkImageFilter INHERITED;
71 };
72
73 ///////////////////////////////////////////////////////////////////////////////
74
Make(SkBlendMode mode,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const SkImageFilter::CropRect * cropRect)75 sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(SkBlendMode mode,
76 sk_sp<SkImageFilter> background,
77 sk_sp<SkImageFilter> foreground,
78 const SkImageFilter::CropRect* cropRect) {
79 sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
80 return sk_sp<SkImageFilter>(new SkXfermodeImageFilter_Base(mode, inputs, cropRect));
81 }
82
SkXfermodeImageFilter_Base(SkBlendMode mode,sk_sp<SkImageFilter> inputs[2],const CropRect * cropRect)83 SkXfermodeImageFilter_Base::SkXfermodeImageFilter_Base(SkBlendMode mode,
84 sk_sp<SkImageFilter> inputs[2],
85 const CropRect* cropRect)
86 : INHERITED(inputs, 2, cropRect)
87 , fMode(mode)
88 {}
89
unflatten_blendmode(SkReadBuffer & buffer)90 static unsigned unflatten_blendmode(SkReadBuffer& buffer) {
91 unsigned mode = buffer.read32();
92 (void)buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode);
93 return mode;
94 }
95
CreateProc(SkReadBuffer & buffer)96 sk_sp<SkFlattenable> SkXfermodeImageFilter_Base::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
flatten(SkWriteBuffer & buffer) const106 void SkXfermodeImageFilter_Base::flatten(SkWriteBuffer& buffer) const {
107 this->INHERITED::flatten(buffer);
108 buffer.write32((unsigned)fMode);
109 }
110
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const111 sk_sp<SkSpecialImage> SkXfermodeImageFilter_Base::onFilterImage(SkSpecialImage* source,
112 const Context& ctx,
113 SkIPoint* offset) const {
114 SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
115 sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset));
116
117 SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
118 sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset));
119
120 SkIRect foregroundBounds = SkIRect::EmptyIRect();
121 if (foreground) {
122 foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(),
123 foreground->width(), foreground->height());
124 }
125
126 SkIRect srcBounds = SkIRect::EmptyIRect();
127 if (background) {
128 srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(),
129 background->width(), background->height());
130 }
131
132 srcBounds.join(foregroundBounds);
133 if (srcBounds.isEmpty()) {
134 return nullptr;
135 }
136
137 SkIRect bounds;
138 if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
139 return nullptr;
140 }
141
142 offset->fX = bounds.left();
143 offset->fY = bounds.top();
144
145 #if SK_SUPPORT_GPU
146 if (source->isTextureBacked()) {
147 return this->filterImageGPU(source,
148 background, backgroundOffset,
149 foreground, foregroundOffset,
150 bounds, ctx.outputProperties());
151 }
152 #endif
153
154 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
155 if (!surf) {
156 return nullptr;
157 }
158
159 SkCanvas* canvas = surf->getCanvas();
160 SkASSERT(canvas);
161
162 canvas->clear(0x0); // can't count on background to fully clear the background
163 canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
164
165 if (background) {
166 SkPaint paint;
167 paint.setBlendMode(SkBlendMode::kSrc);
168 background->draw(canvas,
169 SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY),
170 &paint);
171 }
172
173 this->drawForeground(canvas, foreground.get(), foregroundBounds);
174
175 return surf->makeImageSnapshot();
176 }
177
onFilterBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const178 SkIRect SkXfermodeImageFilter_Base::onFilterBounds(const SkIRect& src,
179 const SkMatrix& ctm,
180 MapDirection dir,
181 const SkIRect* inputRect) const {
182 if (kReverse_MapDirection == dir) {
183 return SkImageFilter::onFilterBounds(src, ctm, dir, inputRect);
184 }
185
186 SkASSERT(!inputRect);
187 SkASSERT(2 == this->countInputs());
188 auto getBackground = [&]() {
189 return this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, dir, inputRect) : src;
190 };
191 auto getForeground = [&]() {
192 return this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, inputRect) : src;
193 };
194 switch (fMode) {
195 case SkBlendMode::kClear:
196 return SkIRect::MakeEmpty();
197
198 case SkBlendMode::kSrc:
199 case SkBlendMode::kDstATop:
200 return getForeground();
201
202 case SkBlendMode::kDst:
203 case SkBlendMode::kSrcATop:
204 return getBackground();
205
206 case SkBlendMode::kSrcIn:
207 case SkBlendMode::kDstIn: {
208 auto result = getBackground();
209 if (!result.intersect(getForeground())) {
210 return SkIRect::MakeEmpty();
211 }
212 return result;
213 }
214
215 default: {
216 auto result = getBackground();
217 result.join(getForeground());
218 return result;
219 }
220 }
221 }
222
onMakeColorSpace(SkColorSpaceXformer * xformer) const223 sk_sp<SkImageFilter> SkXfermodeImageFilter_Base::onMakeColorSpace(SkColorSpaceXformer* xformer)
224 const {
225 SkASSERT(2 == this->countInputs());
226 auto background = xformer->apply(this->getInput(0));
227 auto foreground = xformer->apply(this->getInput(1));
228 if (background.get() != this->getInput(0) || foreground.get() != this->getInput(1)) {
229 return SkXfermodeImageFilter::Make(fMode, std::move(background), std::move(foreground),
230 this->getCropRectIfSet());
231 }
232 return this->refMe();
233 }
234
drawForeground(SkCanvas * canvas,SkSpecialImage * img,const SkIRect & fgBounds) const235 void SkXfermodeImageFilter_Base::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
236 const SkIRect& fgBounds) const {
237 SkPaint paint;
238 paint.setBlendMode(fMode);
239 if (img) {
240 img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop), &paint);
241 }
242
243 SkAutoCanvasRestore acr(canvas, true);
244 canvas->clipRect(SkRect::Make(fgBounds), kDifference_SkClipOp);
245 paint.setColor(0);
246 canvas->drawPaint(paint);
247 }
248
249 #if SK_SUPPORT_GPU
250
251 #include "effects/GrXfermodeFragmentProcessor.h"
252
filterImageGPU(SkSpecialImage * source,sk_sp<SkSpecialImage> background,const SkIPoint & backgroundOffset,sk_sp<SkSpecialImage> foreground,const SkIPoint & foregroundOffset,const SkIRect & bounds,const OutputProperties & outputProperties) const253 sk_sp<SkSpecialImage> SkXfermodeImageFilter_Base::filterImageGPU(
254 SkSpecialImage* source,
255 sk_sp<SkSpecialImage> background,
256 const SkIPoint& backgroundOffset,
257 sk_sp<SkSpecialImage> foreground,
258 const SkIPoint& foregroundOffset,
259 const SkIRect& bounds,
260 const OutputProperties& outputProperties) const {
261 SkASSERT(source->isTextureBacked());
262
263 GrContext* context = source->getContext();
264
265 sk_sp<GrTextureProxy> backgroundProxy, foregroundProxy;
266
267 if (background) {
268 backgroundProxy = background->asTextureProxyRef(context);
269 }
270
271 if (foreground) {
272 foregroundProxy = foreground->asTextureProxyRef(context);
273 }
274
275 GrPaint paint;
276 std::unique_ptr<GrFragmentProcessor> bgFP;
277
278 if (backgroundProxy) {
279 SkIRect bgSubset = background->subset();
280 SkMatrix bgMatrix = SkMatrix::MakeTrans(
281 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
282 SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
283 bgFP = GrTextureDomainEffect::Make(
284 std::move(backgroundProxy), bgMatrix,
285 GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
286 GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
287 bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
288 background->alphaType(),
289 outputProperties.colorSpace());
290 } else {
291 bgFP = GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT,
292 GrConstColorProcessor::InputMode::kIgnore);
293 }
294
295 if (foregroundProxy) {
296 SkIRect fgSubset = foreground->subset();
297 SkMatrix fgMatrix = SkMatrix::MakeTrans(
298 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
299 SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
300 auto foregroundFP = GrTextureDomainEffect::Make(
301 std::move(foregroundProxy), fgMatrix,
302 GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
303 GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
304 foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
305 foreground->getColorSpace(),
306 foreground->alphaType(),
307 outputProperties.colorSpace());
308 paint.addColorFragmentProcessor(std::move(foregroundFP));
309
310 std::unique_ptr<GrFragmentProcessor> xferFP = this->makeFGFrag(std::move(bgFP));
311
312 // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed
313 if (xferFP) {
314 paint.addColorFragmentProcessor(std::move(xferFP));
315 }
316 } else {
317 paint.addColorFragmentProcessor(std::move(bgFP));
318 }
319
320 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
321
322 SkColorType colorType = outputProperties.colorType();
323 GrBackendFormat format =
324 context->contextPriv().caps()->getBackendFormatFromColorType(colorType);
325
326 sk_sp<GrRenderTargetContext> renderTargetContext(
327 context->contextPriv().makeDeferredRenderTargetContext(
328 format, SkBackingFit::kApprox, bounds.width(), bounds.height(),
329 SkColorType2GrPixelConfig(colorType),
330 sk_ref_sp(outputProperties.colorSpace())));
331 if (!renderTargetContext) {
332 return nullptr;
333 }
334
335 SkMatrix matrix;
336 matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
337 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix,
338 SkRect::Make(bounds));
339
340 return SkSpecialImage::MakeDeferredFromGpu(
341 context,
342 SkIRect::MakeWH(bounds.width(), bounds.height()),
343 kNeedNewImageUniqueID_SpecialImage,
344 renderTargetContext->asTextureProxyRef(),
345 renderTargetContext->colorSpaceInfo().refColorSpace());
346 }
347
makeFGFrag(std::unique_ptr<GrFragmentProcessor> bgFP) const348 std::unique_ptr<GrFragmentProcessor> SkXfermodeImageFilter_Base::makeFGFrag(
349 std::unique_ptr<GrFragmentProcessor> bgFP) const {
350 return GrXfermodeFragmentProcessor::MakeFromDstProcessor(std::move(bgFP), fMode);
351 }
352
353 #endif
354 ///////////////////////////////////////////////////////////////////////////////////////////////////
355
RegisterFlattenables()356 void SkXfermodeImageFilter::RegisterFlattenables() {
357 SK_REGISTER_FLATTENABLE(SkXfermodeImageFilter_Base);
358 }
359