blob: 8721dd756eee0ffbf0bbacc28bc66970db6f1d61 [file] [log] [blame]
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +00001/*
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"
robertphillipsf230c6d2016-04-15 12:47:42 -07009
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000010#include "SkCanvas.h"
11#include "SkColorPriv.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000012#include "SkReadBuffer.h"
robertphillipsf230c6d2016-04-15 12:47:42 -070013#include "SkSpecialImage.h"
14#include "SkSpecialSurface.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000015#include "SkWriteBuffer.h"
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000016#include "SkXfermode.h"
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
robertphillipsea461502015-05-26 11:38:03 -070019#include "GrDrawContext.h"
robertphillips4f037942016-02-09 05:09:27 -080020#include "effects/GrConstColorProcessor.h"
senorblancod762ca22015-04-07 12:16:55 -070021#include "effects/GrTextureDomain.h"
bsalomonae4738f2015-09-15 15:33:27 -070022#include "effects/GrSimpleTextureEffect.h"
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000023#include "SkGr.h"
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000024#endif
25
26///////////////////////////////////////////////////////////////////////////////
27
robertphillips8c0326d2016-04-05 12:48:34 -070028sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(sk_sp<SkXfermode> mode,
29 sk_sp<SkImageFilter> background,
30 sk_sp<SkImageFilter> foreground,
reedcfb6bdf2016-03-29 11:32:50 -070031 const CropRect* cropRect) {
robertphillips8c0326d2016-04-05 12:48:34 -070032 sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
reedcfb6bdf2016-03-29 11:32:50 -070033 return sk_sp<SkImageFilter>(new SkXfermodeImageFilter(mode, inputs, cropRect));
34}
35
36SkXfermodeImageFilter::SkXfermodeImageFilter(sk_sp<SkXfermode> mode,
robertphillips8c0326d2016-04-05 12:48:34 -070037 sk_sp<SkImageFilter> inputs[2],
senorblanco24e06d52015-03-18 12:11:33 -070038 const CropRect* cropRect)
robertphillips8c0326d2016-04-05 12:48:34 -070039 : INHERITED(inputs, 2, cropRect)
robertphillipsf230c6d2016-04-15 12:47:42 -070040 , fMode(std::move(mode)) {
41}
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000042
reed60c9b582016-04-03 09:11:13 -070043sk_sp<SkFlattenable> SkXfermodeImageFilter::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -070044 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
reedcfb6bdf2016-03-29 11:32:50 -070045 sk_sp<SkXfermode> mode(buffer.readXfermode());
robertphillipsf230c6d2016-04-15 12:47:42 -070046 return Make(std::move(mode), common.getInput(0), common.getInput(1), &common.cropRect());
reed9fa60da2014-08-21 07:59:51 -070047}
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000048
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000049void SkXfermodeImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000050 this->INHERITED::flatten(buffer);
reedcfb6bdf2016-03-29 11:32:50 -070051 buffer.writeFlattenable(fMode.get());
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000052}
53
robertphillipsf230c6d2016-04-15 12:47:42 -070054sk_sp<SkSpecialImage> SkXfermodeImageFilter::onFilterImage(SkSpecialImage* source,
55 const Context& ctx,
56 SkIPoint* offset) const {
commit-bot@chromium.org7b320702013-07-10 21:22:18 +000057 SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
robertphillipsf230c6d2016-04-15 12:47:42 -070058 sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset));
59
commit-bot@chromium.org7b320702013-07-10 21:22:18 +000060 SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
robertphillipsf230c6d2016-04-15 12:47:42 -070061 sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset));
62
63 SkIRect foregroundBounds = SkIRect::EmptyIRect();
64 if (foreground) {
65 foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(),
66 foreground->width(), foreground->height());
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +000067 }
commit-bot@chromium.org1a4fb702013-09-26 16:09:28 +000068
robertphillipsf230c6d2016-04-15 12:47:42 -070069 SkIRect srcBounds = SkIRect::EmptyIRect();
70 if (background) {
71 srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(),
72 background->width(), background->height());
73 }
74
senorblanco9db04272016-03-31 08:24:29 -070075 srcBounds.join(foregroundBounds);
robertphillipsf230c6d2016-04-15 12:47:42 -070076 if (srcBounds.isEmpty()) {
77 return nullptr;
78 }
79
senorblanco9db04272016-03-31 08:24:29 -070080 SkIRect bounds;
81 if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
robertphillipsf230c6d2016-04-15 12:47:42 -070082 return nullptr;
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +000083 }
commit-bot@chromium.org1a4fb702013-09-26 16:09:28 +000084
senorblanco@chromium.org6776b822014-01-03 21:48:22 +000085 offset->fX = bounds.left();
86 offset->fY = bounds.top();
robertphillipsf230c6d2016-04-15 12:47:42 -070087
88#if SK_SUPPORT_GPU
89 if (source->isTextureBacked()) {
90 return this->filterImageGPU(source,
91 background, backgroundOffset,
92 foreground, foregroundOffset,
93 bounds);
94 }
95#endif
96
97 const SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
98 kPremul_SkAlphaType);
99 sk_sp<SkSpecialSurface> surf(source->makeSurface(info));
100 if (!surf) {
101 return nullptr;
102 }
103
104 SkCanvas* canvas = surf->getCanvas();
105 SkASSERT(canvas);
106
robertphillips3806b8f2016-05-12 10:46:39 -0700107 canvas->clear(0x0); // can't count on background to fully clear the background
108
robertphillipsf230c6d2016-04-15 12:47:42 -0700109 canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
110
111 SkPaint paint;
112 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
113
114 if (background) {
115 background->draw(canvas,
116 SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY),
117 &paint);
118 }
119
120 paint.setXfermode(fMode);
121
122 if (foreground) {
123 foreground->draw(canvas,
124 SkIntToScalar(foregroundOffset.fX), SkIntToScalar(foregroundOffset.fY),
125 &paint);
126 }
127
128 canvas->clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op);
129 paint.setColor(SK_ColorTRANSPARENT);
130 canvas->drawPaint(paint);
131
132 return surf->makeImageSnapshot();
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000133}
134
robertphillipsf3f5bad2014-12-19 13:49:15 -0800135#ifndef SK_IGNORE_TO_STRING
136void SkXfermodeImageFilter::toString(SkString* str) const {
137 str->appendf("SkXfermodeImageFilter: (");
138 str->appendf("xfermode: (");
139 if (fMode) {
140 fMode->toString(str);
141 }
robertphillips63195182015-06-08 06:21:14 -0700142 str->append(")");
143 if (this->getInput(0)) {
144 str->appendf("foreground: (");
145 this->getInput(0)->toString(str);
146 str->appendf(")");
147 }
148 if (this->getInput(1)) {
149 str->appendf("background: (");
150 this->getInput(1)->toString(str);
151 str->appendf(")");
152 }
153 str->append(")");
robertphillipsf3f5bad2014-12-19 13:49:15 -0800154}
155#endif
156
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000157#if SK_SUPPORT_GPU
158
robertphillips4f037942016-02-09 05:09:27 -0800159#include "SkXfermode_proccoeff.h"
160
robertphillipsf230c6d2016-04-15 12:47:42 -0700161sk_sp<SkSpecialImage> SkXfermodeImageFilter::filterImageGPU(SkSpecialImage* source,
162 sk_sp<SkSpecialImage> background,
163 const SkIPoint& backgroundOffset,
164 sk_sp<SkSpecialImage> foreground,
165 const SkIPoint& foregroundOffset,
166 const SkIRect& bounds) const {
167 SkASSERT(source->isTextureBacked());
168
169 GrContext* context = source->getContext();
170
171 sk_sp<GrTexture> backgroundTex, foregroundTex;
172
173 if (background) {
robertphillipsc91fd342016-04-25 12:32:54 -0700174 backgroundTex = background->asTextureRef(context);
egdaniel38cd0552015-01-14 08:05:11 -0800175 }
176
robertphillipsf230c6d2016-04-15 12:47:42 -0700177 if (foreground) {
robertphillipsc91fd342016-04-25 12:32:54 -0700178 foregroundTex = foreground->asTextureRef(context);
senorblanco06d54ad2016-01-13 13:48:54 -0800179 }
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000180
joshualittb2456052015-07-08 09:36:59 -0700181 GrPaint paint;
brianosman898235c2016-04-06 07:38:23 -0700182 // SRGBTODO: AllowSRGBInputs?
bungeman06ca8ec2016-06-09 08:01:03 -0700183 sk_sp<GrFragmentProcessor> bgFP;
robertphillips4f037942016-02-09 05:09:27 -0800184
185 if (backgroundTex) {
186 SkMatrix backgroundMatrix;
187 backgroundMatrix.setIDiv(backgroundTex->width(), backgroundTex->height());
robertphillipsd092ffd2016-08-17 09:28:59 -0700188 backgroundMatrix.preTranslate(-SkIntToScalar(backgroundOffset.fX),
189 -SkIntToScalar(backgroundOffset.fY));
bungeman06ca8ec2016-06-09 08:01:03 -0700190 bgFP = GrTextureDomainEffect::Make(
brianosman54f30c12016-07-18 10:53:52 -0700191 backgroundTex.get(), nullptr, backgroundMatrix,
robertphillipsf230c6d2016-04-15 12:47:42 -0700192 GrTextureDomain::MakeTexelDomain(backgroundTex.get(),
193 background->subset()),
robertphillips4f037942016-02-09 05:09:27 -0800194 GrTextureDomain::kDecal_Mode,
bungeman06ca8ec2016-06-09 08:01:03 -0700195 GrTextureParams::kNone_FilterMode);
robertphillips4f037942016-02-09 05:09:27 -0800196 } else {
bungeman06ca8ec2016-06-09 08:01:03 -0700197 bgFP = GrConstColorProcessor::Make(GrColor_TRANSPARENT_BLACK,
198 GrConstColorProcessor::kIgnore_InputMode);
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000199 }
200
robertphillips4f037942016-02-09 05:09:27 -0800201 if (foregroundTex) {
202 SkMatrix foregroundMatrix;
203 foregroundMatrix.setIDiv(foregroundTex->width(), foregroundTex->height());
robertphillipsd092ffd2016-08-17 09:28:59 -0700204 foregroundMatrix.preTranslate(-SkIntToScalar(foregroundOffset.fX),
205 -SkIntToScalar(foregroundOffset.fY));
commit-bot@chromium.org7b320702013-07-10 21:22:18 +0000206
bungeman06ca8ec2016-06-09 08:01:03 -0700207 sk_sp<GrFragmentProcessor> foregroundFP;
commit-bot@chromium.org7b320702013-07-10 21:22:18 +0000208
bungeman06ca8ec2016-06-09 08:01:03 -0700209 foregroundFP = GrTextureDomainEffect::Make(
brianosman54f30c12016-07-18 10:53:52 -0700210 foregroundTex.get(), nullptr, foregroundMatrix,
robertphillipsf230c6d2016-04-15 12:47:42 -0700211 GrTextureDomain::MakeTexelDomain(foregroundTex.get(),
212 foreground->subset()),
robertphillips4f037942016-02-09 05:09:27 -0800213 GrTextureDomain::kDecal_Mode,
bungeman06ca8ec2016-06-09 08:01:03 -0700214 GrTextureParams::kNone_FilterMode);
senorblancod762ca22015-04-07 12:16:55 -0700215
bungeman06ca8ec2016-06-09 08:01:03 -0700216 paint.addColorFragmentProcessor(std::move(foregroundFP));
robertphillips4f037942016-02-09 05:09:27 -0800217
218 // A null fMode is interpreted to mean kSrcOver_Mode (to match raster).
219 SkAutoTUnref<SkXfermode> mode(SkSafeRef(fMode.get()));
220 if (!mode) {
221 // It would be awesome to use SkXfermode::Create here but it knows better
222 // than us and won't return a kSrcOver_Mode SkXfermode. That means we
223 // have to get one the hard way.
224 struct ProcCoeff rec;
225 rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode);
226 SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC);
227
228 mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode));
229 }
230
bungeman06ca8ec2016-06-09 08:01:03 -0700231 sk_sp<GrFragmentProcessor> xferFP(
232 mode->makeFragmentProcessorForImageFilter(std::move(bgFP)));
robertphillips4f037942016-02-09 05:09:27 -0800233
234 // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed
235 if (xferFP) {
bungeman06ca8ec2016-06-09 08:01:03 -0700236 paint.addColorFragmentProcessor(std::move(xferFP));
robertphillips4f037942016-02-09 05:09:27 -0800237 }
238 } else {
bungeman06ca8ec2016-06-09 08:01:03 -0700239 paint.addColorFragmentProcessor(std::move(bgFP));
bsalomonae4738f2015-09-15 15:33:27 -0700240 }
robertphillips4f037942016-02-09 05:09:27 -0800241
egdanielc4b72722015-11-23 13:20:41 -0800242 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
robertphillipsea461502015-05-26 11:38:03 -0700243
robertphillips6738c702016-07-27 12:13:51 -0700244 sk_sp<GrDrawContext> drawContext(context->makeDrawContext(SkBackingFit::kApprox,
245 bounds.width(), bounds.height(),
246 kSkia8888_GrPixelConfig,
247 sk_ref_sp(source->getColorSpace())));
robertphillipsea461502015-05-26 11:38:03 -0700248 if (!drawContext) {
robertphillipsf230c6d2016-04-15 12:47:42 -0700249 return nullptr;
robertphillipsea461502015-05-26 11:38:03 -0700250 }
251
senorblanco06d54ad2016-01-13 13:48:54 -0800252 SkMatrix matrix;
253 matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
cdalton846c0512016-05-13 10:25:00 -0700254 drawContext->drawRect(GrNoClip(), paint, matrix, SkRect::Make(bounds));
senorblanco@chromium.orgee845ae2014-04-01 19:15:23 +0000255
robertphillips3e302272016-04-20 11:48:36 -0700256 return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()),
robertphillipsf230c6d2016-04-15 12:47:42 -0700257 kNeedNewImageUniqueID_SpecialImage,
brianosmanafbf71d2016-07-21 07:15:37 -0700258 drawContext->asTexture(),
brianosmandfe4f2e2016-07-21 13:28:36 -0700259 sk_ref_sp(drawContext->getColorSpace()));
senorblanco@chromium.org86fc2662013-05-31 17:49:12 +0000260}
261
262#endif