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 "SkCanvas.h"
10 #include "SkDevice.h"
11 #include "SkColorPriv.h"
12 #include "SkReadBuffer.h"
13 #include "SkWriteBuffer.h"
14 #include "SkXfermode.h"
15 #if SK_SUPPORT_GPU
16 #include "GrContext.h"
17 #include "effects/GrTextureDomain.h"
18 #include "SkGr.h"
19 #endif
20 
21 ///////////////////////////////////////////////////////////////////////////////
22 
SkXfermodeImageFilter(SkXfermode * mode,SkImageFilter * inputs[2],const CropRect * cropRect)23 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
24                                              SkImageFilter* inputs[2],
25                                              const CropRect* cropRect)
26   : INHERITED(2, inputs, cropRect), fMode(mode) {
27     SkSafeRef(fMode);
28 }
29 
~SkXfermodeImageFilter()30 SkXfermodeImageFilter::~SkXfermodeImageFilter() {
31     SkSafeUnref(fMode);
32 }
33 
CreateProc(SkReadBuffer & buffer)34 SkFlattenable* SkXfermodeImageFilter::CreateProc(SkReadBuffer& buffer) {
35     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
36     SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
37     return Create(mode, common.getInput(0), common.getInput(1), &common.cropRect());
38 }
39 
flatten(SkWriteBuffer & buffer) const40 void SkXfermodeImageFilter::flatten(SkWriteBuffer& buffer) const {
41     this->INHERITED::flatten(buffer);
42     buffer.writeFlattenable(fMode);
43 }
44 
onFilterImage(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * dst,SkIPoint * offset) const45 bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy,
46                                             const SkBitmap& src,
47                                             const Context& ctx,
48                                             SkBitmap* dst,
49                                             SkIPoint* offset) const {
50     SkBitmap background = src, foreground = src;
51     SkImageFilter* backgroundInput = getInput(0);
52     SkImageFilter* foregroundInput = getInput(1);
53     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
54     if (backgroundInput &&
55         !backgroundInput->filterImage(proxy, src, ctx, &background, &backgroundOffset)) {
56         background.reset();
57     }
58     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
59     if (foregroundInput &&
60         !foregroundInput->filterImage(proxy, src, ctx, &foreground, &foregroundOffset)) {
61         foreground.reset();
62     }
63 
64     SkIRect bounds, foregroundBounds;
65     if (!applyCropRect(ctx, foreground, foregroundOffset, &foregroundBounds)) {
66         foregroundBounds.setEmpty();
67         foreground.reset();
68     }
69     if (!applyCropRect(ctx, background, backgroundOffset, &bounds)) {
70         bounds.setEmpty();
71         background.reset();
72     }
73     bounds.join(foregroundBounds);
74     if (bounds.isEmpty()) {
75         return false;
76     }
77 
78     SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
79     if (NULL == device.get()) {
80         return false;
81     }
82     SkCanvas canvas(device);
83     canvas.translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
84     SkPaint paint;
85     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
86     canvas.drawBitmap(background, SkIntToScalar(backgroundOffset.fX),
87                       SkIntToScalar(backgroundOffset.fY), &paint);
88     paint.setXfermode(fMode);
89     canvas.drawBitmap(foreground, SkIntToScalar(foregroundOffset.fX),
90                       SkIntToScalar(foregroundOffset.fY), &paint);
91     canvas.clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op);
92     paint.setColor(SK_ColorTRANSPARENT);
93     canvas.drawPaint(paint);
94     *dst = device->accessBitmap(false);
95     offset->fX = bounds.left();
96     offset->fY = bounds.top();
97     return true;
98 }
99 
100 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const101 void SkXfermodeImageFilter::toString(SkString* str) const {
102     str->appendf("SkXfermodeImageFilter: (");
103     str->appendf("xfermode: (");
104     if (fMode) {
105         fMode->toString(str);
106     }
107     str->append("))");
108 }
109 #endif
110 
111 #if SK_SUPPORT_GPU
112 
canFilterImageGPU() const113 bool SkXfermodeImageFilter::canFilterImageGPU() const {
114     return fMode && fMode->asFragmentProcessor(NULL, NULL) && !cropRectIsSet();
115 }
116 
filterImageGPU(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const117 bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
118                                            const SkBitmap& src,
119                                            const Context& ctx,
120                                            SkBitmap* result,
121                                            SkIPoint* offset) const {
122     SkBitmap background = src;
123     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
124     if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &background,
125                                                        &backgroundOffset)) {
126         return onFilterImage(proxy, src, ctx, result, offset);
127     }
128     GrTexture* backgroundTex = background.getTexture();
129 
130     if (NULL == backgroundTex) {
131         SkASSERT(false);
132         return false;
133     }
134 
135     SkBitmap foreground = src;
136     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
137     if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground,
138                                                        &foregroundOffset)) {
139         return onFilterImage(proxy, src, ctx, result, offset);
140     }
141     GrTexture* foregroundTex = foreground.getTexture();
142     GrContext* context = foregroundTex->getContext();
143 
144     GrFragmentProcessor* xferProcessor = NULL;
145 
146     GrSurfaceDesc desc;
147     desc.fFlags = kRenderTarget_GrSurfaceFlag;
148     desc.fWidth = src.width();
149     desc.fHeight = src.height();
150     desc.fConfig = kSkia8888_GrPixelConfig;
151     SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(
152         desc, GrTextureProvider::kApprox_ScratchTexMatch));
153     if (!dst) {
154         return false;
155     }
156 
157     if (!fMode || !fMode->asFragmentProcessor(&xferProcessor, backgroundTex)) {
158         // canFilterImageGPU() should've taken care of this
159         SkASSERT(false);
160         return false;
161     }
162 
163     SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex);
164     foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX),
165                                   SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY));
166 
167 
168     SkRect srcRect;
169     src.getBounds(&srcRect);
170 
171     GrPaint paint;
172     SkAutoTUnref<GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create(
173         foregroundTex, foregroundMatrix,
174         GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
175         GrTextureDomain::kDecal_Mode,
176         GrTextureParams::kNone_FilterMode)
177     );
178 
179     paint.addColorProcessor(foregroundDomain.get());
180     paint.addColorProcessor(xferProcessor)->unref();
181     context->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), srcRect);
182 
183     offset->fX = backgroundOffset.fX;
184     offset->fY = backgroundOffset.fY;
185     WrapTexture(dst, src.width(), src.height(), result);
186     return true;
187 }
188 
189 #endif
190 
191