• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "GrDrawContext.h"
18 #include "effects/GrConstColorProcessor.h"
19 #include "effects/GrTextureDomain.h"
20 #include "effects/GrSimpleTextureEffect.h"
21 #include "SkGr.h"
22 #endif
23 
24 ///////////////////////////////////////////////////////////////////////////////
25 
SkXfermodeImageFilter(SkXfermode * mode,SkImageFilter * inputs[2],const CropRect * cropRect)26 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
27                                              SkImageFilter* inputs[2],
28                                              const CropRect* cropRect)
29     : INHERITED(2, inputs, cropRect)
30     , fMode(SkSafeRef(mode)) {
31 }
32 
CreateProc(SkReadBuffer & buffer)33 SkFlattenable* SkXfermodeImageFilter::CreateProc(SkReadBuffer& buffer) {
34     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
35     SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
36     return Create(mode, common.getInput(0), common.getInput(1), &common.cropRect());
37 }
38 
flatten(SkWriteBuffer & buffer) const39 void SkXfermodeImageFilter::flatten(SkWriteBuffer& buffer) const {
40     this->INHERITED::flatten(buffer);
41     buffer.writeFlattenable(fMode);
42 }
43 
onFilterImageDeprecated(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * dst,SkIPoint * offset) const44 bool SkXfermodeImageFilter::onFilterImageDeprecated(Proxy* proxy,
45                                                     const SkBitmap& src,
46                                                     const Context& ctx,
47                                                     SkBitmap* dst,
48                                                     SkIPoint* offset) const {
49     SkBitmap background = src, foreground = src;
50     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
51     if (!this->filterInputDeprecated(0, proxy, src, ctx, &background, &backgroundOffset)) {
52         background.reset();
53     }
54     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
55     if (!this->filterInputDeprecated(1, proxy, src, ctx, &foreground, &foregroundOffset)) {
56         foreground.reset();
57     }
58 
59     SkIRect bounds, foregroundBounds;
60     SkIRect foregroundSrcBounds = foreground.bounds();
61     foregroundSrcBounds.offset(foregroundOffset);
62     if (!applyCropRect(ctx, foregroundSrcBounds, &foregroundBounds)) {
63         foregroundBounds.setEmpty();
64         foreground.reset();
65     }
66     SkIRect backgroundSrcBounds = background.bounds();
67     backgroundSrcBounds.offset(backgroundOffset);
68     if (!applyCropRect(ctx, backgroundSrcBounds, &bounds)) {
69         bounds.setEmpty();
70         background.reset();
71     }
72     bounds.join(foregroundBounds);
73     if (bounds.isEmpty()) {
74         return false;
75     }
76 
77     SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
78     if (nullptr == device.get()) {
79         return false;
80     }
81     SkCanvas canvas(device);
82     canvas.translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
83     SkPaint paint;
84     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
85     canvas.drawBitmap(background, SkIntToScalar(backgroundOffset.fX),
86                       SkIntToScalar(backgroundOffset.fY), &paint);
87     paint.setXfermode(fMode);
88     canvas.drawBitmap(foreground, SkIntToScalar(foregroundOffset.fX),
89                       SkIntToScalar(foregroundOffset.fY), &paint);
90     canvas.clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op);
91     paint.setColor(SK_ColorTRANSPARENT);
92     canvas.drawPaint(paint);
93     *dst = device->accessBitmap(false);
94     offset->fX = bounds.left();
95     offset->fY = bounds.top();
96     return true;
97 }
98 
99 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const100 void SkXfermodeImageFilter::toString(SkString* str) const {
101     str->appendf("SkXfermodeImageFilter: (");
102     str->appendf("xfermode: (");
103     if (fMode) {
104         fMode->toString(str);
105     }
106     str->append(")");
107     if (this->getInput(0)) {
108         str->appendf("foreground: (");
109         this->getInput(0)->toString(str);
110         str->appendf(")");
111     }
112     if (this->getInput(1)) {
113         str->appendf("background: (");
114         this->getInput(1)->toString(str);
115         str->appendf(")");
116     }
117     str->append(")");
118 }
119 #endif
120 
121 #if SK_SUPPORT_GPU
122 
canFilterImageGPU() const123 bool SkXfermodeImageFilter::canFilterImageGPU() const {
124     return !this->cropRectIsSet();
125 }
126 
127 #include "SkXfermode_proccoeff.h"
128 
filterImageGPUDeprecated(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const129 bool SkXfermodeImageFilter::filterImageGPUDeprecated(Proxy* proxy,
130                                                      const SkBitmap& src,
131                                                      const Context& ctx,
132                                                      SkBitmap* result,
133                                                      SkIPoint* offset) const {
134     GrContext* context = nullptr;
135     SkBitmap background = src;
136     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
137     if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &background, &backgroundOffset)) {
138         background.reset();
139     }
140     GrTexture* backgroundTex = background.getTexture();
141     if (backgroundTex) {
142         context = backgroundTex->getContext();
143     }
144 
145     SkBitmap foreground = src;
146     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
147     if (!this->filterInputGPUDeprecated(1, proxy, src, ctx, &foreground, &foregroundOffset)) {
148         foreground.reset();
149     }
150     GrTexture* foregroundTex = foreground.getTexture();
151     if (foregroundTex) {
152         context = foregroundTex->getContext();
153     }
154 
155     if (!context) {
156         return false;
157     }
158 
159     SkIRect bounds = background.bounds().makeOffset(backgroundOffset.x(), backgroundOffset.y());
160     bounds.join(foreground.bounds().makeOffset(foregroundOffset.x(), foregroundOffset.y()));
161     if (bounds.isEmpty()) {
162         return false;
163     }
164 
165     GrSurfaceDesc desc;
166     desc.fFlags = kRenderTarget_GrSurfaceFlag;
167     desc.fWidth = bounds.width();
168     desc.fHeight = bounds.height();
169     desc.fConfig = kSkia8888_GrPixelConfig;
170     SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc));
171     if (!dst) {
172         return false;
173     }
174 
175     GrPaint paint;
176     SkAutoTUnref<const GrFragmentProcessor> bgFP;
177 
178     if (backgroundTex) {
179         SkMatrix backgroundMatrix;
180         backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
181         backgroundMatrix.preTranslate(SkIntToScalar(-backgroundOffset.fX),
182                                       SkIntToScalar(-backgroundOffset.fY));
183         bgFP.reset(GrTextureDomainEffect::Create(
184                             backgroundTex, backgroundMatrix,
185                             GrTextureDomain::MakeTexelDomain(backgroundTex, background.bounds()),
186                             GrTextureDomain::kDecal_Mode,
187                             GrTextureParams::kNone_FilterMode));
188     } else {
189         bgFP.reset(GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK,
190                                                  GrConstColorProcessor::kIgnore_InputMode));
191     }
192 
193     if (foregroundTex) {
194         SkMatrix foregroundMatrix;
195         foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
196         foregroundMatrix.preTranslate(SkIntToScalar(-foregroundOffset.fX),
197                                       SkIntToScalar(-foregroundOffset.fY));
198 
199         SkAutoTUnref<const GrFragmentProcessor> foregroundFP;
200 
201         foregroundFP.reset(GrTextureDomainEffect::Create(
202                             foregroundTex, foregroundMatrix,
203                             GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
204                             GrTextureDomain::kDecal_Mode,
205                             GrTextureParams::kNone_FilterMode));
206 
207         paint.addColorFragmentProcessor(foregroundFP.get());
208 
209         // A null fMode is interpreted to mean kSrcOver_Mode (to match raster).
210         SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get()));
211         if (!mode) {
212             // It would be awesome to use SkXfermode::Create here but it knows better
213             // than us and won't return a kSrcOver_Mode SkXfermode. That means we
214             // have to get one the hard way.
215             struct ProcCoeff rec;
216             rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode);
217             SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC);
218 
219             mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode));
220         }
221 
222         SkAutoTUnref<const GrFragmentProcessor> xferFP(mode->getFragmentProcessorForImageFilter(bgFP));
223 
224         // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed
225         if (xferFP) {
226             paint.addColorFragmentProcessor(xferFP);
227         }
228     } else {
229         paint.addColorFragmentProcessor(bgFP);
230     }
231 
232     paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
233 
234     SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget()));
235     if (!drawContext) {
236         return false;
237     }
238 
239     SkMatrix matrix;
240     matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
241     drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(bounds));
242 
243     offset->fX = bounds.left();
244     offset->fY = bounds.top();
245     GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result);
246     return true;
247 }
248 
249 #endif
250 
251