blob: c1663c032819157435d4d8afaec18e0f22f01334 [file] [log] [blame]
Brian Salomon89cb8212017-01-09 10:48:23 -05001/*
2 * Copyright 2016 Google Inc.
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 "SkArithmeticImageFilter.h"
Brian Salomon89cb8212017-01-09 10:48:23 -05009#include "SkCanvas.h"
Mike Reed6d9f4292017-07-06 12:32:55 -040010#include "SkColorSpaceXformer.h"
Cary Clark60aaeb22017-11-03 08:06:09 -040011#include "SkImageFilterPriv.h"
Brian Salomon89cb8212017-01-09 10:48:23 -050012#include "SkNx.h"
13#include "SkReadBuffer.h"
14#include "SkSpecialImage.h"
15#include "SkSpecialSurface.h"
16#include "SkWriteBuffer.h"
17#include "SkXfermodeImageFilter.h"
18#if SK_SUPPORT_GPU
Brian Salomonc65aec92017-03-09 09:03:58 -050019#include "GrClip.h"
Brian Osman1cb41712017-10-19 12:54:52 -040020#include "GrColorSpaceXform.h"
Brian Salomon89cb8212017-01-09 10:48:23 -050021#include "GrContext.h"
22#include "GrRenderTargetContext.h"
23#include "GrTextureProxy.h"
24#include "SkGr.h"
Brian Salomon89cb8212017-01-09 10:48:23 -050025#include "effects/GrConstColorProcessor.h"
Ethan Nicholasce008112018-08-30 09:19:50 -040026#include "effects/GrSkSLFP.h"
Brian Salomon89cb8212017-01-09 10:48:23 -050027#include "effects/GrTextureDomain.h"
28#include "glsl/GrGLSLFragmentProcessor.h"
29#include "glsl/GrGLSLFragmentShaderBuilder.h"
30#include "glsl/GrGLSLProgramDataManager.h"
31#include "glsl/GrGLSLUniformHandler.h"
Brian Salomon89cb8212017-01-09 10:48:23 -050032
Ethan Nicholas78aceb22018-08-31 16:13:58 -040033GR_FP_SRC_STRING SKSL_ARITHMETIC_SRC = R"(
Ethan Nicholasce008112018-08-30 09:19:50 -040034in uniform float4 k;
35layout(key) const in bool enforcePMColor;
36in fragmentProcessor child;
37
38void main(int x, int y, inout half4 color) {
39 half4 dst = process(child);
40 color = saturate(k.x * color * dst + k.y * color + k.z * dst + k.w);
41 if (enforcePMColor) {
42 color.rgb = min(color.rgb, color.a);
43 }
44}
45)";
Ethan Nicholas78aceb22018-08-31 16:13:58 -040046#endif
Ethan Nicholasce008112018-08-30 09:19:50 -040047
Brian Salomon89cb8212017-01-09 10:48:23 -050048class ArithmeticImageFilterImpl : public SkImageFilter {
49public:
50 ArithmeticImageFilterImpl(float k1, float k2, float k3, float k4, bool enforcePMColor,
51 sk_sp<SkImageFilter> inputs[2], const CropRect* cropRect)
52 : INHERITED(inputs, 2, cropRect), fK{k1, k2, k3, k4}, fEnforcePMColor(enforcePMColor) {}
53
Brian Salomon89cb8212017-01-09 10:48:23 -050054 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(ArithmeticImageFilterImpl)
55
56protected:
57 sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
58 SkIPoint* offset) const override;
59
Robert Phillips12078432018-05-17 11:17:39 -040060 SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
61 MapDirection, const SkIRect* inputRect) const override;
Xianzhu Wang0fa353c2017-08-25 16:27:04 -070062
Brian Salomon89cb8212017-01-09 10:48:23 -050063#if SK_SUPPORT_GPU
64 sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
65 sk_sp<SkSpecialImage> background,
66 const SkIPoint& backgroundOffset,
67 sk_sp<SkSpecialImage> foreground,
68 const SkIPoint& foregroundOffset,
69 const SkIRect& bounds,
70 const OutputProperties& outputProperties) const;
71#endif
72
73 void flatten(SkWriteBuffer& buffer) const override {
74 this->INHERITED::flatten(buffer);
75 for (int i = 0; i < 4; ++i) {
76 buffer.writeScalar(fK[i]);
77 }
78 buffer.writeBool(fEnforcePMColor);
79 }
80
81 void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const;
82
Matt Sarett31abf1f2017-04-07 16:54:04 -040083 sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
84
Brian Salomon89cb8212017-01-09 10:48:23 -050085private:
Xianzhu Wang0fa353c2017-08-25 16:27:04 -070086 bool affectsTransparentBlack() const override { return !SkScalarNearlyZero(fK[3]); }
87
Brian Salomon89cb8212017-01-09 10:48:23 -050088 const float fK[4];
89 const bool fEnforcePMColor;
90
91 friend class ::SkArithmeticImageFilter;
92
93 typedef SkImageFilter INHERITED;
94};
Brian Salomon89cb8212017-01-09 10:48:23 -050095
96sk_sp<SkFlattenable> ArithmeticImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
97 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
98 float k[4];
99 for (int i = 0; i < 4; ++i) {
100 k[i] = buffer.readScalar();
101 }
102 const bool enforcePMColor = buffer.readBool();
Kevin Lubickdaebae92018-05-17 11:29:10 -0400103 if (!buffer.isValid()) {
104 return nullptr;
105 }
Brian Salomon89cb8212017-01-09 10:48:23 -0500106 return SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], enforcePMColor, common.getInput(0),
107 common.getInput(1), &common.cropRect());
108}
109
110static Sk4f pin(float min, const Sk4f& val, float max) {
111 return Sk4f::Max(min, Sk4f::Min(val, max));
112}
113
114template <bool EnforcePMColor>
115void arith_span(const float k[], SkPMColor dst[], const SkPMColor src[], int count) {
116 const Sk4f k1 = k[0] * (1/255.0f),
117 k2 = k[1],
118 k3 = k[2],
119 k4 = k[3] * 255.0f + 0.5f;
120
121 for (int i = 0; i < count; i++) {
122 Sk4f s = SkNx_cast<float>(Sk4b::Load(src+i)),
123 d = SkNx_cast<float>(Sk4b::Load(dst+i)),
124 r = pin(0, k1*s*d + k2*s + k3*d + k4, 255);
125 if (EnforcePMColor) {
126 Sk4f a = SkNx_shuffle<3,3,3,3>(r);
127 r = Sk4f::Min(a, r);
128 }
129 SkNx_cast<uint8_t>(r).store(dst+i);
130 }
131}
132
133// apply mode to src==transparent (0)
134template<bool EnforcePMColor> void arith_transparent(const float k[], SkPMColor dst[], int count) {
135 const Sk4f k3 = k[2],
136 k4 = k[3] * 255.0f + 0.5f;
137
138 for (int i = 0; i < count; i++) {
139 Sk4f d = SkNx_cast<float>(Sk4b::Load(dst+i)),
140 r = pin(0, k3*d + k4, 255);
141 if (EnforcePMColor) {
142 Sk4f a = SkNx_shuffle<3,3,3,3>(r);
143 r = Sk4f::Min(a, r);
144 }
145 SkNx_cast<uint8_t>(r).store(dst+i);
146 }
147}
148
149static bool intersect(SkPixmap* dst, SkPixmap* src, int srcDx, int srcDy) {
150 SkIRect dstR = SkIRect::MakeWH(dst->width(), dst->height());
151 SkIRect srcR = SkIRect::MakeXYWH(srcDx, srcDy, src->width(), src->height());
152 SkIRect sect;
153 if (!sect.intersect(dstR, srcR)) {
154 return false;
155 }
156 *dst = SkPixmap(dst->info().makeWH(sect.width(), sect.height()),
157 dst->addr(sect.fLeft, sect.fTop),
158 dst->rowBytes());
159 *src = SkPixmap(src->info().makeWH(sect.width(), sect.height()),
160 src->addr(SkTMax(0, -srcDx), SkTMax(0, -srcDy)),
161 src->rowBytes());
162 return true;
163}
164
165sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::onFilterImage(SkSpecialImage* source,
166 const Context& ctx,
167 SkIPoint* offset) const {
168 SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
169 sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset));
170
171 SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
172 sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset));
173
174 SkIRect foregroundBounds = SkIRect::EmptyIRect();
175 if (foreground) {
176 foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(),
177 foreground->width(), foreground->height());
178 }
179
180 SkIRect srcBounds = SkIRect::EmptyIRect();
181 if (background) {
182 srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(),
183 background->width(), background->height());
184 }
185
186 srcBounds.join(foregroundBounds);
187 if (srcBounds.isEmpty()) {
188 return nullptr;
189 }
190
191 SkIRect bounds;
192 if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
193 return nullptr;
194 }
195
196 offset->fX = bounds.left();
197 offset->fY = bounds.top();
198
199#if SK_SUPPORT_GPU
200 if (source->isTextureBacked()) {
201 return this->filterImageGPU(source, background, backgroundOffset, foreground,
202 foregroundOffset, bounds, ctx.outputProperties());
203 }
204#endif
205
206 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size()));
207 if (!surf) {
208 return nullptr;
209 }
210
211 SkCanvas* canvas = surf->getCanvas();
212 SkASSERT(canvas);
213
214 canvas->clear(0x0); // can't count on background to fully clear the background
215 canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
216
217 if (background) {
218 SkPaint paint;
219 paint.setBlendMode(SkBlendMode::kSrc);
220 background->draw(canvas, SkIntToScalar(backgroundOffset.fX),
221 SkIntToScalar(backgroundOffset.fY), &paint);
222 }
223
224 this->drawForeground(canvas, foreground.get(), foregroundBounds);
225
226 return surf->makeImageSnapshot();
227}
228
Xianzhu Wang0fa353c2017-08-25 16:27:04 -0700229SkIRect ArithmeticImageFilterImpl::onFilterBounds(const SkIRect& src,
230 const SkMatrix& ctm,
Robert Phillips12078432018-05-17 11:17:39 -0400231 MapDirection dir,
232 const SkIRect* inputRect) const {
233 if (kReverse_MapDirection == dir) {
234 return SkImageFilter::onFilterBounds(src, ctm, dir, inputRect);
Xianzhu Wang0fa353c2017-08-25 16:27:04 -0700235 }
236
237 SkASSERT(2 == this->countInputs());
238
239 // result(i1,i2) = k1*i1*i2 + k2*i1 + k3*i2 + k4
240 // Note that background (getInput(0)) is i2, and foreground (getInput(1)) is i1.
Robert Phillips12078432018-05-17 11:17:39 -0400241 auto i2 = this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, dir, nullptr) : src;
242 auto i1 = this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, nullptr) : src;
Xianzhu Wang0fa353c2017-08-25 16:27:04 -0700243
244 // Arithmetic with non-zero k4 may influence the complete filter primitive
245 // region. [k4 > 0 => result(0,0) = k4 => result(i1,i2) >= k4]
246 if (!SkScalarNearlyZero(fK[3])) {
247 i1.join(i2);
248 return i1;
249 }
250
251 // If both K2 or K3 are non-zero, both i1 and i2 appear.
252 if (!SkScalarNearlyZero(fK[1]) && !SkScalarNearlyZero(fK[2])) {
253 i1.join(i2);
254 return i1;
255 }
256
257 // If k2 is non-zero, output can be produced whenever i1 is non-transparent.
258 // [k3 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k2*i1 = (k1*i2 + k2)*i1]
259 if (!SkScalarNearlyZero(fK[1])) {
260 return i1;
261 }
262
263 // If k3 is non-zero, output can be produced whenever i2 is non-transparent.
264 // [k2 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k3*i2 = (k1*i1 + k3)*i2]
265 if (!SkScalarNearlyZero(fK[2])) {
266 return i2;
267 }
268
269 // If just k1 is non-zero, output will only be produce where both inputs
270 // are non-transparent. Use intersection.
271 // [k1 > 0 and k2 = k3 = k4 = 0 => result(i1,i2) = k1*i1*i2]
272 if (!SkScalarNearlyZero(fK[0])) {
273 if (!i1.intersect(i2)) {
274 return SkIRect::MakeEmpty();
275 }
276 return i1;
277 }
278
279 // [k1 = k2 = k3 = k4 = 0 => result(i1,i2) = 0]
280 return SkIRect::MakeEmpty();
281}
282
Brian Salomon89cb8212017-01-09 10:48:23 -0500283#if SK_SUPPORT_GPU
284
Brian Salomon89cb8212017-01-09 10:48:23 -0500285sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::filterImageGPU(
286 SkSpecialImage* source,
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400287 sk_sp<SkSpecialImage> background,
Brian Salomon89cb8212017-01-09 10:48:23 -0500288 const SkIPoint& backgroundOffset,
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400289 sk_sp<SkSpecialImage> foreground,
Brian Salomon89cb8212017-01-09 10:48:23 -0500290 const SkIPoint& foregroundOffset,
291 const SkIRect& bounds,
292 const OutputProperties& outputProperties) const {
293 SkASSERT(source->isTextureBacked());
294
295 GrContext* context = source->getContext();
296
Robert Phillips8e1c4e62017-02-19 12:27:01 -0500297 sk_sp<GrTextureProxy> backgroundProxy, foregroundProxy;
Brian Salomon89cb8212017-01-09 10:48:23 -0500298
299 if (background) {
Robert Phillips8e1c4e62017-02-19 12:27:01 -0500300 backgroundProxy = background->asTextureProxyRef(context);
Brian Salomon89cb8212017-01-09 10:48:23 -0500301 }
302
303 if (foreground) {
Robert Phillips8e1c4e62017-02-19 12:27:01 -0500304 foregroundProxy = foreground->asTextureProxyRef(context);
Brian Salomon89cb8212017-01-09 10:48:23 -0500305 }
306
307 GrPaint paint;
Brian Salomonaff329b2017-08-11 09:40:37 -0400308 std::unique_ptr<GrFragmentProcessor> bgFP;
Brian Salomon89cb8212017-01-09 10:48:23 -0500309
Robert Phillips8e1c4e62017-02-19 12:27:01 -0500310 if (backgroundProxy) {
Robert Phillips67c18d62017-01-20 12:44:06 -0500311 SkMatrix backgroundMatrix = SkMatrix::MakeTrans(-SkIntToScalar(backgroundOffset.fX),
312 -SkIntToScalar(backgroundOffset.fY));
Brian Salomon89cb8212017-01-09 10:48:23 -0500313 bgFP = GrTextureDomainEffect::Make(
Brian Osman2240be92017-10-18 13:15:13 -0400314 std::move(backgroundProxy), backgroundMatrix,
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400315 GrTextureDomain::MakeTexelDomain(background->subset()),
316 GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
Brian Osmanbd2c653e2017-10-18 09:57:33 -0400317 bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
Brian Osman21fc5ce2018-08-27 20:36:19 +0000318 background->alphaType(),
Brian Osman653f34d2018-06-14 11:44:02 -0400319 outputProperties.colorSpace());
Brian Salomon89cb8212017-01-09 10:48:23 -0500320 } else {
Brian Osmanf28e55d2018-10-03 16:35:54 -0400321 bgFP = GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT,
Ethan Nicholase9d172a2017-11-20 12:12:24 -0500322 GrConstColorProcessor::InputMode::kIgnore);
Brian Salomon89cb8212017-01-09 10:48:23 -0500323 }
324
Robert Phillips8e1c4e62017-02-19 12:27:01 -0500325 if (foregroundProxy) {
Robert Phillips67c18d62017-01-20 12:44:06 -0500326 SkMatrix foregroundMatrix = SkMatrix::MakeTrans(-SkIntToScalar(foregroundOffset.fX),
327 -SkIntToScalar(foregroundOffset.fY));
Brian Salomonaff329b2017-08-11 09:40:37 -0400328 auto foregroundFP = GrTextureDomainEffect::Make(
Brian Osman2240be92017-10-18 13:15:13 -0400329 std::move(foregroundProxy), foregroundMatrix,
Brian Salomonaff329b2017-08-11 09:40:37 -0400330 GrTextureDomain::MakeTexelDomain(foreground->subset()),
Brian Salomon2bbdcc42017-09-07 12:36:34 -0400331 GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
Brian Osmanbd2c653e2017-10-18 09:57:33 -0400332 foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
Brian Osman653f34d2018-06-14 11:44:02 -0400333 foreground->getColorSpace(),
Brian Osman21fc5ce2018-08-27 20:36:19 +0000334 foreground->alphaType(),
Brian Osmanbd2c653e2017-10-18 09:57:33 -0400335 outputProperties.colorSpace());
Brian Salomon89cb8212017-01-09 10:48:23 -0500336 paint.addColorFragmentProcessor(std::move(foregroundFP));
337
Ethan Nicholasce008112018-08-30 09:19:50 -0400338 static int arithmeticIndex = GrSkSLFP::NewIndex();
339 ArithmeticFPInputs inputs;
340 static_assert(sizeof(inputs.k) == sizeof(fK), "struct size mismatch");
341 memcpy(inputs.k, fK, sizeof(inputs.k));
342 inputs.enforcePMColor = fEnforcePMColor;
343 std::unique_ptr<GrFragmentProcessor> xferFP = GrSkSLFP::Make(context,
344 arithmeticIndex,
345 "Arithmetic",
346 SKSL_ARITHMETIC_SRC,
Ethan Nicholas222e2752018-10-11 11:21:34 -0400347 inputs);
Brian Salomon89cb8212017-01-09 10:48:23 -0500348 if (xferFP) {
Ethan Nicholasce008112018-08-30 09:19:50 -0400349 ((GrSkSLFP&) *xferFP).addChild(std::move(bgFP));
Brian Salomon89cb8212017-01-09 10:48:23 -0500350 paint.addColorFragmentProcessor(std::move(xferFP));
351 }
352 } else {
353 paint.addColorFragmentProcessor(std::move(bgFP));
354 }
355
356 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
357
Robert Phillips0c4b7b12018-03-06 08:20:37 -0500358 sk_sp<GrRenderTargetContext> renderTargetContext(
359 context->contextPriv().makeDeferredRenderTargetContext(
Brian Salomon89cb8212017-01-09 10:48:23 -0500360 SkBackingFit::kApprox, bounds.width(), bounds.height(),
Brian Osmana50205f2018-07-06 13:57:01 -0400361 SkColorType2GrPixelConfig(outputProperties.colorType()),
Brian Salomon89cb8212017-01-09 10:48:23 -0500362 sk_ref_sp(outputProperties.colorSpace())));
363 if (!renderTargetContext) {
364 return nullptr;
365 }
Brian Salomon89cb8212017-01-09 10:48:23 -0500366
367 SkMatrix matrix;
368 matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
Brian Salomon82f44312017-01-11 13:42:54 -0500369 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix,
370 SkRect::Make(bounds));
Brian Salomon89cb8212017-01-09 10:48:23 -0500371
Brian Salomonf3569f02017-10-24 12:52:33 -0400372 return SkSpecialImage::MakeDeferredFromGpu(
373 context,
374 SkIRect::MakeWH(bounds.width(), bounds.height()),
375 kNeedNewImageUniqueID_SpecialImage,
376 renderTargetContext->asTextureProxyRef(),
377 renderTargetContext->colorSpaceInfo().refColorSpace());
Brian Salomon89cb8212017-01-09 10:48:23 -0500378}
379#endif
380
381void ArithmeticImageFilterImpl::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
382 const SkIRect& fgBounds) const {
383 SkPixmap dst;
384 if (!canvas->peekPixels(&dst)) {
385 return;
386 }
387
388 const SkMatrix& ctm = canvas->getTotalMatrix();
389 SkASSERT(ctm.getType() <= SkMatrix::kTranslate_Mask);
390 const int dx = SkScalarRoundToInt(ctm.getTranslateX());
391 const int dy = SkScalarRoundToInt(ctm.getTranslateY());
Mike Reed339650c2018-02-26 13:13:52 -0500392 // be sure to perform this offset using SkIRect, since it saturates to avoid overflows
393 const SkIRect fgoffset = fgBounds.makeOffset(dx, dy);
Brian Salomon89cb8212017-01-09 10:48:23 -0500394
395 if (img) {
396 SkBitmap srcBM;
397 SkPixmap src;
398 if (!img->getROPixels(&srcBM)) {
399 return;
400 }
Brian Salomon89cb8212017-01-09 10:48:23 -0500401 if (!srcBM.peekPixels(&src)) {
402 return;
403 }
404
405 auto proc = fEnforcePMColor ? arith_span<true> : arith_span<false>;
406 SkPixmap tmpDst = dst;
Mike Reed339650c2018-02-26 13:13:52 -0500407 if (intersect(&tmpDst, &src, fgoffset.fLeft, fgoffset.fTop)) {
Brian Salomon89cb8212017-01-09 10:48:23 -0500408 for (int y = 0; y < tmpDst.height(); ++y) {
409 proc(fK, tmpDst.writable_addr32(0, y), src.addr32(0, y), tmpDst.width());
410 }
411 }
412 }
413
414 // Now apply the mode with transparent-color to the outside of the fg image
415 SkRegion outside(SkIRect::MakeWH(dst.width(), dst.height()));
Mike Reed339650c2018-02-26 13:13:52 -0500416 outside.op(fgoffset, SkRegion::kDifference_Op);
Brian Salomon89cb8212017-01-09 10:48:23 -0500417 auto proc = fEnforcePMColor ? arith_transparent<true> : arith_transparent<false>;
418 for (SkRegion::Iterator iter(outside); !iter.done(); iter.next()) {
419 const SkIRect r = iter.rect();
420 for (int y = r.fTop; y < r.fBottom; ++y) {
421 proc(fK, dst.writable_addr32(r.fLeft, y), r.width());
422 }
423 }
424}
425
Matt Sarett31abf1f2017-04-07 16:54:04 -0400426sk_sp<SkImageFilter> ArithmeticImageFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
427const {
428 SkASSERT(2 == this->countInputs());
Mike Reed6d9f4292017-07-06 12:32:55 -0400429 auto background = xformer->apply(this->getInput(0));
430 auto foreground = xformer->apply(this->getInput(1));
431 if (background.get() != this->getInput(0) || foreground.get() != this->getInput(1)) {
432 return SkArithmeticImageFilter::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor,
433 std::move(background), std::move(foreground),
434 getCropRectIfSet());
Matt Sarett31abf1f2017-04-07 16:54:04 -0400435 }
Mike Reed6d9f4292017-07-06 12:32:55 -0400436 return this->refMe();
Matt Sarett31abf1f2017-04-07 16:54:04 -0400437}
438
Brian Salomon89cb8212017-01-09 10:48:23 -0500439sk_sp<SkImageFilter> SkArithmeticImageFilter::Make(float k1, float k2, float k3, float k4,
440 bool enforcePMColor,
441 sk_sp<SkImageFilter> background,
442 sk_sp<SkImageFilter> foreground,
443 const SkImageFilter::CropRect* crop) {
444 if (!SkScalarIsFinite(k1) || !SkScalarIsFinite(k2) || !SkScalarIsFinite(k3) ||
445 !SkScalarIsFinite(k4)) {
446 return nullptr;
447 }
448
449 // are we nearly some other "std" mode?
450 int mode = -1; // illegal mode
451 if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) && SkScalarNearlyZero(k3) &&
452 SkScalarNearlyZero(k4)) {
453 mode = (int)SkBlendMode::kSrc;
454 } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) &&
455 SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) {
456 mode = (int)SkBlendMode::kDst;
457 } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && SkScalarNearlyZero(k3) &&
458 SkScalarNearlyZero(k4)) {
459 mode = (int)SkBlendMode::kClear;
460 }
461 if (mode >= 0) {
462 return SkXfermodeImageFilter::Make((SkBlendMode)mode, std::move(background),
463 std::move(foreground), crop);
464 }
465
466 sk_sp<SkImageFilter> inputs[2] = {std::move(background), std::move(foreground)};
467 return sk_sp<SkImageFilter>(
468 new ArithmeticImageFilterImpl(k1, k2, k3, k4, enforcePMColor, inputs, crop));
469}
470
471///////////////////////////////////////////////////////////////////////////////////////////////////
472
473SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkArithmeticImageFilter)
474 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(ArithmeticImageFilterImpl)
475SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END