1 /*
2  * Copyright 2014 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 #ifndef GrMatrixConvolutionEffect_DEFINED
9 #define GrMatrixConvolutionEffect_DEFINED
10 
11 #include "src/gpu/GrFragmentProcessor.h"
12 #include <array>
13 #include <new>
14 
15 class GrMatrixConvolutionEffect : public GrFragmentProcessor {
16 public:
17     // A little bit less than the minimum # uniforms required by DX9SM2 (32).
18     // Allows for a 5x5 kernel (or 28x1, for that matter).
19     // Must be a multiple of 4, since we upload these in vec4s.
20     inline static constexpr int kMaxUniformSize = 28;
21 
22     static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext*,
23                                                      GrSurfaceProxyView srcView,
24                                                      const SkIRect& srcBounds,
25                                                      const SkISize& kernelSize,
26                                                      const SkScalar* kernel,
27                                                      SkScalar gain,
28                                                      SkScalar bias,
29                                                      const SkIPoint& kernelOffset,
30                                                      GrSamplerState::WrapMode,
31                                                      bool convolveAlpha,
32                                                      const GrCaps&);
33 
34     const char* name() const override { return "MatrixConvolution"; }
35 
36     SkString getShaderDfxInfo() const override;
37 
38     std::unique_ptr<GrFragmentProcessor> clone() const override;
39 
40 private:
41     class Impl;
42 
43     /**
44      * Small kernels are represented as float-arrays and uploaded as uniforms.
45      * Large kernels go over the uniform limit and are uploaded as textures and sampled.
46      * If Float16 textures are supported, we use those. Otherwise we use A8.
47      */
48     class KernelWrapper {
49     public:
50         struct BiasAndGain {
51             // Only used in A8 mode. Applied before any other math.
52             float fBias;
53             // Only used in A8 mode. Premultiplied in with user gain to save time.
54             float fGain;
55             bool operator==(const BiasAndGain&) const;
56         };
57         using MakeResult = std::tuple<KernelWrapper, std::unique_ptr<GrFragmentProcessor>>;
58         static MakeResult Make(GrRecordingContext*, SkISize, const GrCaps&, const float* values);
59 
60         KernelWrapper() = default;
KernelWrapper(const KernelWrapper& that)61         KernelWrapper(const KernelWrapper& that) : fSize(that.fSize) {
62             if (that.isSampled()) {
63                 fBiasAndGain = that.fBiasAndGain;
64             } else {
65                 new (&fArray) std::array<float, kMaxUniformSize>(that.fArray);
66             }
67         }
68 
isValid() const69         bool isValid() const { return !fSize.isEmpty(); }
size() const70         SkISize size() const { return fSize; }
isSampled() const71         bool isSampled() const { return fSize.area() > kMaxUniformSize; }
array() const72         const std::array<float, kMaxUniformSize>& array() const {
73             SkASSERT(!this->isSampled());
74             return fArray;
75         }
biasAndGain() const76         const BiasAndGain& biasAndGain() const {
77             SkASSERT(this->isSampled());
78             return fBiasAndGain;
79         }
80         bool operator==(const KernelWrapper&) const;
81 
82     private:
KernelWrapper(SkISize size)83         KernelWrapper(SkISize size) : fSize(size) {
84             if (this->isSampled()) {
85                 fBiasAndGain = {0.f , 1.f};
86             }
87         }
88 
89         SkISize fSize = {};
90         union {
91             std::array<float, kMaxUniformSize> fArray;
92             BiasAndGain fBiasAndGain;
93         };
94     };
95 
96     GrMatrixConvolutionEffect(std::unique_ptr<GrFragmentProcessor> child,
97                               const KernelWrapper& kernel,
98                               std::unique_ptr<GrFragmentProcessor> kernelFP,
99                               SkScalar gain,
100                               SkScalar bias,
101                               const SkIPoint& kernelOffset,
102                               bool convolveAlpha);
103 
104     explicit GrMatrixConvolutionEffect(const GrMatrixConvolutionEffect&);
105 
106     std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
107 
108     void onAddToKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
109 
110     bool onIsEqual(const GrFragmentProcessor&) const override;
111 
112     KernelWrapper    fKernel;
113     float            fGain;
114     float            fBias;
115     SkVector         fKernelOffset;
116     bool             fConvolveAlpha;
117 
118     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
119 
120     using INHERITED = GrFragmentProcessor;
121 };
122 
123 #endif
124