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     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 
bounds()34     const SkIRect& bounds() const { return fBounds; }
kernelSize()35     SkISize kernelSize() const { return fKernel.size(); }
kernelOffset()36     SkVector kernelOffset() const { return fKernelOffset; }
kernelIsSampled()37     bool kernelIsSampled() const { return fKernel.isSampled(); }
kernel()38     const float *kernel() const { return fKernel.array().data(); }
kernelSampleGain()39     float kernelSampleGain() const { return fKernel.biasAndGain().fGain; }
kernelSampleBias()40     float kernelSampleBias() const { return fKernel.biasAndGain().fBias; }
gain()41     float gain() const { return fGain; }
bias()42     float bias() const { return fBias; }
convolveAlpha()43     bool convolveAlpha() const { return fConvolveAlpha; }
44 
name()45     const char* name() const override { return "MatrixConvolution"; }
46 
47     std::unique_ptr<GrFragmentProcessor> clone() const override;
48 
49 private:
50     /**
51      * Small kernels are represented as float-arrays and uploaded as uniforms.
52      * Large kernels go over the uniform limit and are uploaded as textures and sampled.
53      * If Float16 textures are supported, we use those. Otherwise we use A8.
54      */
55     class KernelWrapper {
56     public:
57         struct BiasAndGain {
58             // Only used in A8 mode. Applied before any other math.
59             float fBias;
60             // Only used in A8 mode. Premultiplied in with user gain to save time.
61             float fGain;
62             bool operator==(const BiasAndGain&) const;
63         };
64         using MakeResult = std::tuple<KernelWrapper, std::unique_ptr<GrFragmentProcessor>>;
65         static MakeResult Make(GrRecordingContext*, SkISize, const GrCaps&, const float* values);
66 
67         KernelWrapper() = default;
KernelWrapper(const KernelWrapper & that)68         KernelWrapper(const KernelWrapper& that) : fSize(that.fSize) {
69             if (that.isSampled()) {
70                 fBiasAndGain = that.fBiasAndGain;
71             } else {
72                 new (&fArray) std::array<float, kMaxUniformSize>(that.fArray);
73             }
74         }
75 
isValid()76         bool isValid() const { return !fSize.isEmpty(); }
size()77         SkISize size() const { return fSize; }
isSampled()78         bool isSampled() const { return fSize.area() > kMaxUniformSize; }
array()79         const std::array<float, kMaxUniformSize>& array() const {
80             SkASSERT(!this->isSampled());
81             return fArray;
82         }
biasAndGain()83         const BiasAndGain& biasAndGain() const {
84             SkASSERT(this->isSampled());
85             return fBiasAndGain;
86         }
87         bool operator==(const KernelWrapper&) const;
88 
89     private:
KernelWrapper(SkISize size)90         KernelWrapper(SkISize size) : fSize(size) {
91             if (this->isSampled()) {
92                 fBiasAndGain = {0.f , 1.f};
93             }
94         }
95 
96         SkISize fSize = {};
97         union {
98             std::array<float, kMaxUniformSize> fArray;
99             BiasAndGain fBiasAndGain;
100         };
101     };
102 
103     GrMatrixConvolutionEffect(std::unique_ptr<GrFragmentProcessor> child,
104                               const KernelWrapper& kernel,
105                               std::unique_ptr<GrFragmentProcessor> kernelFP,
106                               SkScalar gain,
107                               SkScalar bias,
108                               const SkIPoint& kernelOffset,
109                               bool convolveAlpha);
110 
111     explicit GrMatrixConvolutionEffect(const GrMatrixConvolutionEffect&);
112 
113     std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
114 
115     void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
116 
117     bool onIsEqual(const GrFragmentProcessor&) const override;
118 
119     SkIRect          fBounds;
120     KernelWrapper    fKernel;
121     float            fGain;
122     float            fBias;
123     SkVector         fKernelOffset;
124     bool             fConvolveAlpha;
125 
126     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
127 
128     using INHERITED = GrFragmentProcessor;
129 };
130 
131 #endif
132