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