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/GrSimpleTextureEffect.h"
18 #include "SkGr.h"
19 #endif
20
21 ///////////////////////////////////////////////////////////////////////////////
22
SkXfermodeImageFilter(SkXfermode * mode,SkImageFilter * background,SkImageFilter * foreground,const CropRect * cropRect)23 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
24 SkImageFilter* background,
25 SkImageFilter* foreground,
26 const CropRect* cropRect)
27 : INHERITED(background, foreground, cropRect), fMode(mode) {
28 SkSafeRef(fMode);
29 }
30
~SkXfermodeImageFilter()31 SkXfermodeImageFilter::~SkXfermodeImageFilter() {
32 SkSafeUnref(fMode);
33 }
34
SkXfermodeImageFilter(SkReadBuffer & buffer)35 SkXfermodeImageFilter::SkXfermodeImageFilter(SkReadBuffer& buffer)
36 : INHERITED(2, buffer) {
37 fMode = buffer.readXfermode();
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 #if SK_SUPPORT_GPU
101
canFilterImageGPU() const102 bool SkXfermodeImageFilter::canFilterImageGPU() const {
103 return fMode && fMode->asNewEffect(NULL, NULL) && !cropRectIsSet();
104 }
105
filterImageGPU(Proxy * proxy,const SkBitmap & src,const Context & ctx,SkBitmap * result,SkIPoint * offset) const106 bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
107 const SkBitmap& src,
108 const Context& ctx,
109 SkBitmap* result,
110 SkIPoint* offset) const {
111 SkBitmap background = src;
112 SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
113 if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &background,
114 &backgroundOffset)) {
115 return onFilterImage(proxy, src, ctx, result, offset);
116 }
117 GrTexture* backgroundTex = background.getTexture();
118 SkBitmap foreground = src;
119 SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
120 if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground,
121 &foregroundOffset)) {
122 return onFilterImage(proxy, src, ctx, result, offset);
123 }
124 GrTexture* foregroundTex = foreground.getTexture();
125 GrContext* context = foregroundTex->getContext();
126
127 GrEffectRef* xferEffect = NULL;
128
129 GrTextureDesc desc;
130 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
131 desc.fWidth = src.width();
132 desc.fHeight = src.height();
133 desc.fConfig = kSkia8888_GrPixelConfig;
134
135 GrAutoScratchTexture ast(context, desc);
136 SkAutoTUnref<GrTexture> dst(ast.detach());
137
138 GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
139
140 if (!fMode || !fMode->asNewEffect(&xferEffect, backgroundTex)) {
141 // canFilterImageGPU() should've taken care of this
142 SkASSERT(false);
143 return false;
144 }
145
146 SkMatrix foregroundMatrix = GrEffect::MakeDivByTextureWHMatrix(foregroundTex);
147 foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX),
148 SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY));
149
150
151 SkRect srcRect;
152 src.getBounds(&srcRect);
153
154 GrPaint paint;
155 paint.addColorTextureEffect(foregroundTex, foregroundMatrix);
156 paint.addColorEffect(xferEffect)->unref();
157 context->drawRect(paint, srcRect);
158
159 offset->fX = backgroundOffset.fX;
160 offset->fY = backgroundOffset.fY;
161 WrapTexture(dst, src.width(), src.height(), result);
162 return true;
163 }
164
165 #endif
166