• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkArithmeticImageFilter.h"
9 #include "SkCanvas.h"
10 #include "SkColorSpaceXformer.h"
11 #include "SkImageFilterPriv.h"
12 #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
19 #include "GrClip.h"
20 #include "GrColorSpaceXform.h"
21 #include "GrContext.h"
22 #include "GrRenderTargetContext.h"
23 #include "GrTextureProxy.h"
24 #include "SkGr.h"
25 #include "effects/GrConstColorProcessor.h"
26 #include "effects/GrSkSLFP.h"
27 #include "effects/GrTextureDomain.h"
28 #include "glsl/GrGLSLFragmentProcessor.h"
29 #include "glsl/GrGLSLFragmentShaderBuilder.h"
30 #include "glsl/GrGLSLProgramDataManager.h"
31 #include "glsl/GrGLSLUniformHandler.h"
32 
33 GR_FP_SRC_STRING SKSL_ARITHMETIC_SRC = R"(
34 in uniform float4 k;
35 layout(key) const in bool enforcePMColor;
36 in fragmentProcessor child;
37 
38 void 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 )";
46 #endif
47 
48 class ArithmeticImageFilterImpl : public SkImageFilter {
49 public:
ArithmeticImageFilterImpl(float k1,float k2,float k3,float k4,bool enforcePMColor,sk_sp<SkImageFilter> inputs[2],const CropRect * cropRect)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 
54 protected:
55     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
56                                         SkIPoint* offset) const override;
57 
58     SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
59                            MapDirection, const SkIRect* inputRect) const override;
60 
61 #if SK_SUPPORT_GPU
62     sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source,
63                                          sk_sp<SkSpecialImage> background,
64                                          const SkIPoint& backgroundOffset,
65                                          sk_sp<SkSpecialImage> foreground,
66                                          const SkIPoint& foregroundOffset,
67                                          const SkIRect& bounds,
68                                          const OutputProperties& outputProperties) const;
69 #endif
70 
flatten(SkWriteBuffer & buffer) const71     void flatten(SkWriteBuffer& buffer) const override {
72         this->INHERITED::flatten(buffer);
73         for (int i = 0; i < 4; ++i) {
74             buffer.writeScalar(fK[i]);
75         }
76         buffer.writeBool(fEnforcePMColor);
77     }
78 
79     void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const;
80 
81     sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
82 
83 private:
SK_FLATTENABLE_HOOKS(ArithmeticImageFilterImpl)84     SK_FLATTENABLE_HOOKS(ArithmeticImageFilterImpl)
85 
86     bool affectsTransparentBlack() const override { return !SkScalarNearlyZero(fK[3]); }
87 
88     const float fK[4];
89     const bool fEnforcePMColor;
90 
91     friend class ::SkArithmeticImageFilter;
92 
93     typedef SkImageFilter INHERITED;
94 };
95 
CreateProc(SkReadBuffer & buffer)96 sk_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();
103     if (!buffer.isValid()) {
104         return nullptr;
105     }
106     return SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], enforcePMColor, common.getInput(0),
107                                          common.getInput(1), &common.cropRect());
108 }
109 
pin(float min,const Sk4f & val,float max)110 static Sk4f pin(float min, const Sk4f& val, float max) {
111     return Sk4f::Max(min, Sk4f::Min(val, max));
112 }
113 
114 template <bool EnforcePMColor>
arith_span(const float k[],SkPMColor dst[],const SkPMColor src[],int count)115 void 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)
arith_transparent(const float k[],SkPMColor dst[],int count)134 template<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 
intersect(SkPixmap * dst,SkPixmap * src,int srcDx,int srcDy)149 static 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 
onFilterImage(SkSpecialImage * source,const Context & ctx,SkIPoint * offset) const165 sk_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 
onFilterBounds(const SkIRect & src,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect) const229 SkIRect ArithmeticImageFilterImpl::onFilterBounds(const SkIRect& src,
230                                                   const SkMatrix& ctm,
231                                                   MapDirection dir,
232                                                   const SkIRect* inputRect) const {
233     if (kReverse_MapDirection == dir) {
234         return SkImageFilter::onFilterBounds(src, ctm, dir, inputRect);
235     }
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.
241     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;
243 
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 
283 #if SK_SUPPORT_GPU
284 
filterImageGPU(SkSpecialImage * source,sk_sp<SkSpecialImage> background,const SkIPoint & backgroundOffset,sk_sp<SkSpecialImage> foreground,const SkIPoint & foregroundOffset,const SkIRect & bounds,const OutputProperties & outputProperties) const285 sk_sp<SkSpecialImage> ArithmeticImageFilterImpl::filterImageGPU(
286         SkSpecialImage* source,
287         sk_sp<SkSpecialImage> background,
288         const SkIPoint& backgroundOffset,
289         sk_sp<SkSpecialImage> foreground,
290         const SkIPoint& foregroundOffset,
291         const SkIRect& bounds,
292         const OutputProperties& outputProperties) const {
293     SkASSERT(source->isTextureBacked());
294 
295     GrContext* context = source->getContext();
296 
297     sk_sp<GrTextureProxy> backgroundProxy, foregroundProxy;
298 
299     if (background) {
300         backgroundProxy = background->asTextureProxyRef(context);
301     }
302 
303     if (foreground) {
304         foregroundProxy = foreground->asTextureProxyRef(context);
305     }
306 
307     GrPaint paint;
308     std::unique_ptr<GrFragmentProcessor> bgFP;
309 
310     if (backgroundProxy) {
311         SkIRect bgSubset = background->subset();
312         SkMatrix backgroundMatrix = SkMatrix::MakeTrans(
313                 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
314                 SkIntToScalar(bgSubset.top()  - backgroundOffset.fY));
315         bgFP = GrTextureDomainEffect::Make(
316                 std::move(backgroundProxy), backgroundMatrix,
317                 GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
318                 GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
319         bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
320                                              background->alphaType(),
321                                              outputProperties.colorSpace());
322     } else {
323         bgFP = GrConstColorProcessor::Make(SK_PMColor4fTRANSPARENT,
324                                            GrConstColorProcessor::InputMode::kIgnore);
325     }
326 
327     if (foregroundProxy) {
328         SkIRect fgSubset = foreground->subset();
329         SkMatrix foregroundMatrix = SkMatrix::MakeTrans(
330                 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
331                 SkIntToScalar(fgSubset.top()  - foregroundOffset.fY));
332         auto foregroundFP = GrTextureDomainEffect::Make(
333                 std::move(foregroundProxy), foregroundMatrix,
334                 GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
335                 GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
336         foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
337                                                      foreground->getColorSpace(),
338                                                      foreground->alphaType(),
339                                                      outputProperties.colorSpace());
340         paint.addColorFragmentProcessor(std::move(foregroundFP));
341 
342         static int arithmeticIndex = GrSkSLFP::NewIndex();
343         ArithmeticFPInputs inputs;
344         static_assert(sizeof(inputs.k) == sizeof(fK), "struct size mismatch");
345         memcpy(inputs.k, fK, sizeof(inputs.k));
346         inputs.enforcePMColor = fEnforcePMColor;
347         std::unique_ptr<GrFragmentProcessor> xferFP = GrSkSLFP::Make(context,
348                                                                      arithmeticIndex,
349                                                                      "Arithmetic",
350                                                                      SKSL_ARITHMETIC_SRC,
351                                                                      &inputs,
352                                                                      sizeof(inputs));
353         if (xferFP) {
354             ((GrSkSLFP&) *xferFP).addChild(std::move(bgFP));
355             paint.addColorFragmentProcessor(std::move(xferFP));
356         }
357     } else {
358         paint.addColorFragmentProcessor(std::move(bgFP));
359     }
360 
361     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
362 
363     SkColorType colorType = outputProperties.colorType();
364     GrBackendFormat format =
365             context->contextPriv().caps()->getBackendFormatFromColorType(colorType);
366 
367     sk_sp<GrRenderTargetContext> renderTargetContext(
368         context->contextPriv().makeDeferredRenderTargetContext(
369             format, SkBackingFit::kApprox, bounds.width(), bounds.height(),
370             SkColorType2GrPixelConfig(colorType),
371             sk_ref_sp(outputProperties.colorSpace())));
372     if (!renderTargetContext) {
373         return nullptr;
374     }
375 
376     SkMatrix matrix;
377     matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
378     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix,
379                                   SkRect::Make(bounds));
380 
381     return SkSpecialImage::MakeDeferredFromGpu(
382             context,
383             SkIRect::MakeWH(bounds.width(), bounds.height()),
384             kNeedNewImageUniqueID_SpecialImage,
385             renderTargetContext->asTextureProxyRef(),
386             renderTargetContext->colorSpaceInfo().refColorSpace());
387 }
388 #endif
389 
drawForeground(SkCanvas * canvas,SkSpecialImage * img,const SkIRect & fgBounds) const390 void ArithmeticImageFilterImpl::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
391                                                const SkIRect& fgBounds) const {
392     SkPixmap dst;
393     if (!canvas->peekPixels(&dst)) {
394         return;
395     }
396 
397     const SkMatrix& ctm = canvas->getTotalMatrix();
398     SkASSERT(ctm.getType() <= SkMatrix::kTranslate_Mask);
399     const int dx = SkScalarRoundToInt(ctm.getTranslateX());
400     const int dy = SkScalarRoundToInt(ctm.getTranslateY());
401     // be sure to perform this offset using SkIRect, since it saturates to avoid overflows
402     const SkIRect fgoffset = fgBounds.makeOffset(dx, dy);
403 
404     if (img) {
405         SkBitmap srcBM;
406         SkPixmap src;
407         if (!img->getROPixels(&srcBM)) {
408             return;
409         }
410         if (!srcBM.peekPixels(&src)) {
411             return;
412         }
413 
414         auto proc = fEnforcePMColor ? arith_span<true> : arith_span<false>;
415         SkPixmap tmpDst = dst;
416         if (intersect(&tmpDst, &src, fgoffset.fLeft, fgoffset.fTop)) {
417             for (int y = 0; y < tmpDst.height(); ++y) {
418                 proc(fK, tmpDst.writable_addr32(0, y), src.addr32(0, y), tmpDst.width());
419             }
420         }
421     }
422 
423     // Now apply the mode with transparent-color to the outside of the fg image
424     SkRegion outside(SkIRect::MakeWH(dst.width(), dst.height()));
425     outside.op(fgoffset, SkRegion::kDifference_Op);
426     auto proc = fEnforcePMColor ? arith_transparent<true> : arith_transparent<false>;
427     for (SkRegion::Iterator iter(outside); !iter.done(); iter.next()) {
428         const SkIRect r = iter.rect();
429         for (int y = r.fTop; y < r.fBottom; ++y) {
430             proc(fK, dst.writable_addr32(r.fLeft, y), r.width());
431         }
432     }
433 }
434 
onMakeColorSpace(SkColorSpaceXformer * xformer) const435 sk_sp<SkImageFilter> ArithmeticImageFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
436 const {
437     SkASSERT(2 == this->countInputs());
438     auto background = xformer->apply(this->getInput(0));
439     auto foreground = xformer->apply(this->getInput(1));
440     if (background.get() != this->getInput(0) || foreground.get() != this->getInput(1)) {
441         return SkArithmeticImageFilter::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor,
442                                              std::move(background), std::move(foreground),
443                                              getCropRectIfSet());
444     }
445     return this->refMe();
446 }
447 
Make(float k1,float k2,float k3,float k4,bool enforcePMColor,sk_sp<SkImageFilter> background,sk_sp<SkImageFilter> foreground,const SkImageFilter::CropRect * crop)448 sk_sp<SkImageFilter> SkArithmeticImageFilter::Make(float k1, float k2, float k3, float k4,
449                                                    bool enforcePMColor,
450                                                    sk_sp<SkImageFilter> background,
451                                                    sk_sp<SkImageFilter> foreground,
452                                                    const SkImageFilter::CropRect* crop) {
453     if (!SkScalarIsFinite(k1) || !SkScalarIsFinite(k2) || !SkScalarIsFinite(k3) ||
454         !SkScalarIsFinite(k4)) {
455         return nullptr;
456     }
457 
458     // are we nearly some other "std" mode?
459     int mode = -1;  // illegal mode
460     if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) && SkScalarNearlyZero(k3) &&
461         SkScalarNearlyZero(k4)) {
462         mode = (int)SkBlendMode::kSrc;
463     } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) &&
464                SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) {
465         mode = (int)SkBlendMode::kDst;
466     } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && SkScalarNearlyZero(k3) &&
467                SkScalarNearlyZero(k4)) {
468         mode = (int)SkBlendMode::kClear;
469     }
470     if (mode >= 0) {
471         return SkXfermodeImageFilter::Make((SkBlendMode)mode, std::move(background),
472                                            std::move(foreground), crop);
473     }
474 
475     sk_sp<SkImageFilter> inputs[2] = {std::move(background), std::move(foreground)};
476     return sk_sp<SkImageFilter>(
477             new ArithmeticImageFilterImpl(k1, k2, k3, k4, enforcePMColor, inputs, crop));
478 }
479 
480 ///////////////////////////////////////////////////////////////////////////////////////////////////
481 
RegisterFlattenables()482 void SkArithmeticImageFilter::RegisterFlattenables() {
483     SK_REGISTER_FLATTENABLE(ArithmeticImageFilterImpl);
484 }
485