1 /*
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 "include/effects/SkArithmeticImageFilter.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/effects/SkXfermodeImageFilter.h"
12 #include "include/private/SkNx.h"
13 #include "src/core/SkImageFilter_Base.h"
14 #include "src/core/SkReadBuffer.h"
15 #include "src/core/SkSpecialImage.h"
16 #include "src/core/SkSpecialSurface.h"
17 #include "src/core/SkWriteBuffer.h"
18 #if SK_SUPPORT_GPU
19 #include "include/effects/SkRuntimeEffect.h"
20 #include "include/gpu/GrRecordingContext.h"
21 #include "src/gpu/GrColorSpaceXform.h"
22 #include "src/gpu/GrRecordingContextPriv.h"
23 #include "src/gpu/GrRenderTargetContext.h"
24 #include "src/gpu/GrTextureProxy.h"
25 #include "src/gpu/SkGr.h"
26 #include "src/gpu/effects/generated/GrArithmeticProcessor.h"
27 #include "src/gpu/effects/generated/GrConstColorProcessor.h"
28 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
29 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
30 #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
31 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
32 #endif
33
34 namespace {
35
36 class ArithmeticImageFilterImpl final : public SkImageFilter_Base {
37 public:
ArithmeticImageFilterImpl(float k1,float k2,float k3,float k4,bool enforcePMColor,sk_sp<SkImageFilter> inputs[2],const CropRect * cropRect)38 ArithmeticImageFilterImpl(float k1, float k2, float k3, float k4, bool enforcePMColor,
39 sk_sp<SkImageFilter> inputs[2], const CropRect* cropRect)
40 : INHERITED(inputs, 2, cropRect), fInputs{k1, k2, k3, k4, enforcePMColor} {}
41
42 protected:
43 sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
44
45 SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
46 MapDirection, const SkIRect* inputRect) const override;
47
48 #if SK_SUPPORT_GPU
49 sk_sp<SkSpecialImage> filterImageGPU(const Context& ctx,
50 sk_sp<SkSpecialImage> background,
51 const SkIPoint& backgroundOffset,
52 sk_sp<SkSpecialImage> foreground,
53 const SkIPoint& foregroundOffset,
54 const SkIRect& bounds) const;
55 #endif
56
57 void flatten(SkWriteBuffer& buffer) const override;
58
59 void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const;
60
61 private:
62 friend void SkArithmeticImageFilter::RegisterFlattenables();
SK_FLATTENABLE_HOOKS(ArithmeticImageFilterImpl)63 SK_FLATTENABLE_HOOKS(ArithmeticImageFilterImpl)
64
65 bool affectsTransparentBlack() const override { return !SkScalarNearlyZero(fInputs.fK[3]); }
66
67 ArithmeticFPInputs fInputs;
68
69 using INHERITED = SkImageFilter_Base;
70 };
71
72 }; // end namespace
73
Make(float k1,float k2,float k3,float k4,bool enforcePMColor,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const SkImageFilter::CropRect * crop)74 sk_sp<SkImageFilter> SkArithmeticImageFilter::Make(float k1, float k2, float k3, float k4,
75 bool enforcePMColor,
76 sk_sp<SkImageFilter> background,
77 sk_sp<SkImageFilter> foreground,
78 const SkImageFilter::CropRect* crop) {
79 if (!SkScalarIsFinite(k1) || !SkScalarIsFinite(k2) || !SkScalarIsFinite(k3) ||
80 !SkScalarIsFinite(k4)) {
81 return nullptr;
82 }
83
84 // are we nearly some other "std" mode?
85 int mode = -1; // illegal mode
86 if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) && SkScalarNearlyZero(k3) &&
87 SkScalarNearlyZero(k4)) {
88 mode = (int)SkBlendMode::kSrc;
89 } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) &&
90 SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) {
91 mode = (int)SkBlendMode::kDst;
92 } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && SkScalarNearlyZero(k3) &&
93 SkScalarNearlyZero(k4)) {
94 mode = (int)SkBlendMode::kClear;
95 }
96 if (mode >= 0) {
97 return SkXfermodeImageFilter::Make((SkBlendMode)mode, std::move(background),
98 std::move(foreground), crop);
99 }
100
101 sk_sp<SkImageFilter> inputs[2] = {std::move(background), std::move(foreground)};
102 return sk_sp<SkImageFilter>(
103 new ArithmeticImageFilterImpl(k1, k2, k3, k4, enforcePMColor, inputs, crop));
104 }
105
RegisterFlattenables()106 void SkArithmeticImageFilter::RegisterFlattenables() {
107 SK_REGISTER_FLATTENABLE(ArithmeticImageFilterImpl);
108 }
109
110 ///////////////////////////////////////////////////////////////////////////////////////////////////
111
CreateProc(SkReadBuffer & buffer)112 sk_sp<SkFlattenable> ArithmeticImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
113 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
114 float k[4];
115 for (int i = 0; i < 4; ++i) {
116 k[i] = buffer.readScalar();
117 }
118 const bool enforcePMColor = buffer.readBool();
119 if (!buffer.isValid()) {
120 return nullptr;
121 }
122 return SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], enforcePMColor, common.getInput(0),
123 common.getInput(1), &common.cropRect());
124 }
125
flatten(SkWriteBuffer & buffer) const126 void ArithmeticImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
127 this->INHERITED::flatten(buffer);
128 for (int i = 0; i < 4; ++i) {
129 buffer.writeScalar(fInputs.fK[i]);
130 }
131 buffer.writeBool(fInputs.fEnforcePMColor);
132 }
133
pin(float min,const Sk4f & val,float max)134 static Sk4f pin(float min, const Sk4f& val, float max) {
135 return Sk4f::Max(min, Sk4f::Min(val, max));
136 }
137
138 template <bool EnforcePMColor>
arith_span(const float k[],SkPMColor dst[],const SkPMColor src[],int count)139 void arith_span(const float k[], SkPMColor dst[], const SkPMColor src[], int count) {
140 const Sk4f k1 = k[0] * (1/255.0f),
141 k2 = k[1],
142 k3 = k[2],
143 k4 = k[3] * 255.0f + 0.5f;
144
145 for (int i = 0; i < count; i++) {
146 Sk4f s = SkNx_cast<float>(Sk4b::Load(src+i)),
147 d = SkNx_cast<float>(Sk4b::Load(dst+i)),
148 r = pin(0, k1*s*d + k2*s + k3*d + k4, 255);
149 if (EnforcePMColor) {
150 Sk4f a = SkNx_shuffle<3,3,3,3>(r);
151 r = Sk4f::Min(a, r);
152 }
153 SkNx_cast<uint8_t>(r).store(dst+i);
154 }
155 }
156
157 // apply mode to src==transparent (0)
arith_transparent(const float k[],SkPMColor dst[],int count)158 template<bool EnforcePMColor> void arith_transparent(const float k[], SkPMColor dst[], int count) {
159 const Sk4f k3 = k[2],
160 k4 = k[3] * 255.0f + 0.5f;
161
162 for (int i = 0; i < count; i++) {
163 Sk4f d = SkNx_cast<float>(Sk4b::Load(dst+i)),
164 r = pin(0, k3*d + k4, 255);
165 if (EnforcePMColor) {
166 Sk4f a = SkNx_shuffle<3,3,3,3>(r);
167 r = Sk4f::Min(a, r);
168 }
169 SkNx_cast<uint8_t>(r).store(dst+i);
170 }
171 }
172
intersect(SkPixmap * dst,SkPixmap * src,int srcDx,int srcDy)173 static bool intersect(SkPixmap* dst, SkPixmap* src, int srcDx, int srcDy) {
174 SkIRect dstR = SkIRect::MakeWH(dst->width(), dst->height());
175 SkIRect srcR = SkIRect::MakeXYWH(srcDx, srcDy, src->width(), src->height());
176 SkIRect sect;
177 if (!sect.intersect(dstR, srcR)) {
178 return false;
179 }
180 *dst = SkPixmap(dst->info().makeDimensions(sect.size()),
181 dst->addr(sect.fLeft, sect.fTop),
182 dst->rowBytes());
183 *src = SkPixmap(src->info().makeDimensions(sect.size()),
184 src->addr(std::max(0, -srcDx), std::max(0, -srcDy)),
185 src->rowBytes());
186 return true;
187 }
188
onFilterImage(const Context & ctx,SkIPoint * offset) const189 sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::onFilterImage(const Context& ctx,
190 SkIPoint* offset) const {
191 SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
192 sk_sp<SkSpecialImage> background(this->filterInput(0, ctx, &backgroundOffset));
193
194 SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
195 sk_sp<SkSpecialImage> foreground(this->filterInput(1, ctx, &foregroundOffset));
196
197 SkIRect foregroundBounds = SkIRect::MakeEmpty();
198 if (foreground) {
199 foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(),
200 foreground->width(), foreground->height());
201 }
202
203 SkIRect srcBounds = SkIRect::MakeEmpty();
204 if (background) {
205 srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(),
206 background->width(), background->height());
207 }
208
209 srcBounds.join(foregroundBounds);
210 if (srcBounds.isEmpty()) {
211 return nullptr;
212 }
213
214 SkIRect bounds;
215 if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
216 return nullptr;
217 }
218
219 offset->fX = bounds.left();
220 offset->fY = bounds.top();
221
222 #if SK_SUPPORT_GPU
223 if (ctx.gpuBacked()) {
224 return this->filterImageGPU(ctx, background, backgroundOffset, foreground,
225 foregroundOffset, bounds);
226 }
227 #endif
228
229 sk_sp<SkSpecialSurface> surf(ctx.makeSurface(bounds.size()));
230 if (!surf) {
231 return nullptr;
232 }
233
234 SkCanvas* canvas = surf->getCanvas();
235 SkASSERT(canvas);
236
237 canvas->clear(0x0); // can't count on background to fully clear the background
238 canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
239
240 if (background) {
241 SkPaint paint;
242 paint.setBlendMode(SkBlendMode::kSrc);
243 background->draw(canvas, SkIntToScalar(backgroundOffset.fX),
244 SkIntToScalar(backgroundOffset.fY), &paint);
245 }
246
247 this->drawForeground(canvas, foreground.get(), foregroundBounds);
248
249 return surf->makeImageSnapshot();
250 }
251
onFilterBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const252 SkIRect ArithmeticImageFilterImpl::onFilterBounds(const SkIRect& src,
253 const SkMatrix& ctm,
254 MapDirection dir,
255 const SkIRect* inputRect) const {
256 if (kReverse_MapDirection == dir) {
257 return INHERITED::onFilterBounds(src, ctm, dir, inputRect);
258 }
259
260 SkASSERT(2 == this->countInputs());
261
262 // result(i1,i2) = k1*i1*i2 + k2*i1 + k3*i2 + k4
263 // Note that background (getInput(0)) is i2, and foreground (getInput(1)) is i1.
264 auto i2 = this->getInput(0) ? this->getInput(0)->filterBounds(src, ctm, dir, nullptr) : src;
265 auto i1 = this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, nullptr) : src;
266
267 // Arithmetic with non-zero k4 may influence the complete filter primitive
268 // region. [k4 > 0 => result(0,0) = k4 => result(i1,i2) >= k4]
269 if (!SkScalarNearlyZero(fInputs.fK[3])) {
270 i1.join(i2);
271 return i1;
272 }
273
274 // If both K2 or K3 are non-zero, both i1 and i2 appear.
275 if (!SkScalarNearlyZero(fInputs.fK[1]) && !SkScalarNearlyZero(fInputs.fK[2])) {
276 i1.join(i2);
277 return i1;
278 }
279
280 // If k2 is non-zero, output can be produced whenever i1 is non-transparent.
281 // [k3 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k2*i1 = (k1*i2 + k2)*i1]
282 if (!SkScalarNearlyZero(fInputs.fK[1])) {
283 return i1;
284 }
285
286 // If k3 is non-zero, output can be produced whenever i2 is non-transparent.
287 // [k2 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k3*i2 = (k1*i1 + k3)*i2]
288 if (!SkScalarNearlyZero(fInputs.fK[2])) {
289 return i2;
290 }
291
292 // If just k1 is non-zero, output will only be produce where both inputs
293 // are non-transparent. Use intersection.
294 // [k1 > 0 and k2 = k3 = k4 = 0 => result(i1,i2) = k1*i1*i2]
295 if (!SkScalarNearlyZero(fInputs.fK[0])) {
296 if (!i1.intersect(i2)) {
297 return SkIRect::MakeEmpty();
298 }
299 return i1;
300 }
301
302 // [k1 = k2 = k3 = k4 = 0 => result(i1,i2) = 0]
303 return SkIRect::MakeEmpty();
304 }
305
306 #if SK_SUPPORT_GPU
307
filterImageGPU(const Context & ctx,sk_sp<SkSpecialImage> background,const SkIPoint & backgroundOffset,sk_sp<SkSpecialImage> foreground,const SkIPoint & foregroundOffset,const SkIRect & bounds) const308 sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::filterImageGPU(
309 const Context& ctx,
310 sk_sp<SkSpecialImage> background,
311 const SkIPoint& backgroundOffset,
312 sk_sp<SkSpecialImage> foreground,
313 const SkIPoint& foregroundOffset,
314 const SkIRect& bounds) const {
315 SkASSERT(ctx.gpuBacked());
316
317 auto context = ctx.getContext();
318
319 GrSurfaceProxyView backgroundView, foregroundView;
320
321 GrProtected isProtected = GrProtected::kNo;
322 if (background) {
323 backgroundView = background->view(context);
324 SkASSERT(backgroundView.proxy());
325 isProtected = backgroundView.proxy()->isProtected();
326 }
327
328 if (foreground) {
329 foregroundView = foreground->view(context);
330 SkASSERT(foregroundView.proxy());
331 isProtected = foregroundView.proxy()->isProtected();
332 }
333
334 GrPaint paint;
335 std::unique_ptr<GrFragmentProcessor> bgFP;
336 const auto& caps = *ctx.getContext()->priv().caps();
337 GrSamplerState sampler(GrSamplerState::WrapMode::kClampToBorder,
338 GrSamplerState::Filter::kNearest);
339
340 if (background) {
341 SkRect bgSubset = SkRect::Make(background->subset());
342 SkMatrix backgroundMatrix = SkMatrix::Translate(
343 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
344 SkIntToScalar(bgSubset.top() - backgroundOffset.fY));
345 bgFP = GrTextureEffect::MakeSubset(std::move(backgroundView), background->alphaType(),
346 backgroundMatrix, sampler, bgSubset, caps);
347 bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP),
348 background->getColorSpace(), background->alphaType(),
349 ctx.colorSpace(), kPremul_SkAlphaType);
350 } else {
351 bgFP = GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT);
352 }
353
354 if (foreground) {
355 SkRect fgSubset = SkRect::Make(foreground->subset());
356 SkMatrix foregroundMatrix = SkMatrix::Translate(
357 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
358 SkIntToScalar(fgSubset.top() - foregroundOffset.fY));
359 auto fgFP = GrTextureEffect::MakeSubset(std::move(foregroundView), foreground->alphaType(),
360 foregroundMatrix, sampler, fgSubset, caps);
361 fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP),
362 foreground->getColorSpace(), foreground->alphaType(),
363 ctx.colorSpace(), kPremul_SkAlphaType);
364 paint.setColorFragmentProcessor(
365 GrArithmeticProcessor::Make(std::move(fgFP), std::move(bgFP), fInputs));
366 } else {
367 paint.setColorFragmentProcessor(std::move(bgFP));
368 }
369
370 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
371
372 auto renderTargetContext = GrRenderTargetContext::Make(
373 context, ctx.grColorType(), ctx.refColorSpace(), SkBackingFit::kApprox, bounds.size(),
374 1, GrMipmapped::kNo, isProtected, kBottomLeft_GrSurfaceOrigin);
375 if (!renderTargetContext) {
376 return nullptr;
377 }
378
379 SkMatrix matrix;
380 matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
381 renderTargetContext->drawRect(nullptr, std::move(paint), GrAA::kNo, matrix,
382 SkRect::Make(bounds));
383
384 return SkSpecialImage::MakeDeferredFromGpu(context,
385 SkIRect::MakeWH(bounds.width(), bounds.height()),
386 kNeedNewImageUniqueID_SpecialImage,
387 renderTargetContext->readSurfaceView(),
388 renderTargetContext->colorInfo().colorType(),
389 renderTargetContext->colorInfo().refColorSpace());
390 }
391 #endif
392
drawForeground(SkCanvas * canvas,SkSpecialImage * img,const SkIRect & fgBounds) const393 void ArithmeticImageFilterImpl::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
394 const SkIRect& fgBounds) const {
395 SkPixmap dst;
396 if (!canvas->peekPixels(&dst)) {
397 return;
398 }
399
400 const SkMatrix& ctm = canvas->getTotalMatrix();
401 SkASSERT(ctm.getType() <= SkMatrix::kTranslate_Mask);
402 const int dx = SkScalarRoundToInt(ctm.getTranslateX());
403 const int dy = SkScalarRoundToInt(ctm.getTranslateY());
404 // be sure to perform this offset using SkIRect, since it saturates to avoid overflows
405 const SkIRect fgoffset = fgBounds.makeOffset(dx, dy);
406
407 if (img) {
408 SkBitmap srcBM;
409 SkPixmap src;
410 if (!img->getROPixels(&srcBM)) {
411 return;
412 }
413 if (!srcBM.peekPixels(&src)) {
414 return;
415 }
416
417 auto proc = fInputs.fEnforcePMColor ? arith_span<true> : arith_span<false>;
418 SkPixmap tmpDst = dst;
419 if (intersect(&tmpDst, &src, fgoffset.fLeft, fgoffset.fTop)) {
420 for (int y = 0; y < tmpDst.height(); ++y) {
421 proc(fInputs.fK, tmpDst.writable_addr32(0, y), src.addr32(0, y), tmpDst.width());
422 }
423 }
424 }
425
426 // Now apply the mode with transparent-color to the outside of the fg image
427 SkRegion outside(SkIRect::MakeWH(dst.width(), dst.height()));
428 outside.op(fgoffset, SkRegion::kDifference_Op);
429 auto proc = fInputs.fEnforcePMColor ? arith_transparent<true> : arith_transparent<false>;
430 for (SkRegion::Iterator iter(outside); !iter.done(); iter.next()) {
431 const SkIRect r = iter.rect();
432 for (int y = r.fTop; y < r.fBottom; ++y) {
433 proc(fInputs.fK, dst.writable_addr32(r.fLeft, y), r.width());
434 }
435 }
436 }
437