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