1/*
2 * Copyright 2015 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 "SkArithmeticMode_gpu.h"
9
10#if SK_SUPPORT_GPU
11#include "GrContext.h"
12#include "GrFragmentProcessor.h"
13#include "GrInvariantOutput.h"
14#include "GrProcessor.h"
15#include "GrTexture.h"
16#include "gl/GrGLCaps.h"
17#include "gl/GrGLProcessor.h"
18#include "gl/GrGLProgramDataManager.h"
19#include "gl/builders/GrGLProgramBuilder.h"
20
21static const bool gUseUnpremul = false;
22
23static void add_arithmetic_code(GrGLFragmentBuilder* fsBuilder,
24                                const char* inputColor,
25                                const char* dstColor,
26                                const char* outputColor,
27                                const char* kUni,
28                                bool enforcePMColor) {
29    // We don't try to optimize for this case at all
30    if (NULL == inputColor) {
31        fsBuilder->codeAppend("const vec4 src = vec4(1);");
32    } else {
33        fsBuilder->codeAppendf("vec4 src = %s;", inputColor);
34        if (gUseUnpremul) {
35            fsBuilder->codeAppend("src.rgb = clamp(src.rgb / src.a, 0.0, 1.0);");
36        }
37    }
38
39    fsBuilder->codeAppendf("vec4 dst = %s;", dstColor);
40    if (gUseUnpremul) {
41        fsBuilder->codeAppend("dst.rgb = clamp(dst.rgb / dst.a, 0.0, 1.0);");
42    }
43
44    fsBuilder->codeAppendf("%s = %s.x * src * dst + %s.y * src + %s.z * dst + %s.w;",
45                           outputColor, kUni, kUni, kUni, kUni);
46    fsBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor);
47    if (gUseUnpremul) {
48        fsBuilder->codeAppendf("%s.rgb *= %s.a;", outputColor, outputColor);
49    } else if (enforcePMColor) {
50        fsBuilder->codeAppendf("%s.rgb = min(%s.rgb, %s.a);",
51                               outputColor, outputColor, outputColor);
52    }
53}
54
55class GLArithmeticFP : public GrGLFragmentProcessor {
56public:
57    GLArithmeticFP(const GrProcessor&)
58        : fEnforcePMColor(true) {
59    }
60
61    ~GLArithmeticFP() override {}
62
63    void emitCode(GrGLFPBuilder* builder,
64                  const GrFragmentProcessor& fp,
65                  const char* outputColor,
66                  const char* inputColor,
67                  const TransformedCoordsArray& coords,
68                  const TextureSamplerArray& samplers) override {
69        GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder();
70        fsBuilder->codeAppend("vec4 bgColor = ");
71        fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType());
72        fsBuilder->codeAppendf(";");
73        const char* dstColor = "bgColor";
74
75        fKUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
76                                    kVec4f_GrSLType, kDefault_GrSLPrecision,
77                                    "k");
78        const char* kUni = builder->getUniformCStr(fKUni);
79
80        add_arithmetic_code(fsBuilder, inputColor, dstColor, outputColor, kUni, fEnforcePMColor);
81    }
82
83    void setData(const GrGLProgramDataManager& pdman, const GrProcessor& proc) override {
84        const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>();
85        pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
86        fEnforcePMColor = arith.enforcePMColor();
87    }
88
89    static void GenKey(const GrProcessor& proc, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {
90        const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>();
91        uint32_t key = arith.enforcePMColor() ? 1 : 0;
92        b->add32(key);
93    }
94
95private:
96    GrGLProgramDataManager::UniformHandle fKUni;
97    bool fEnforcePMColor;
98
99    typedef GrGLFragmentProcessor INHERITED;
100};
101
102///////////////////////////////////////////////////////////////////////////////
103
104GrArithmeticFP::GrArithmeticFP(float k1, float k2, float k3, float k4,
105                               bool enforcePMColor, GrTexture* background)
106  : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
107    this->initClassID<GrArithmeticFP>();
108
109    SkASSERT(background);
110
111    fBackgroundTransform.reset(kLocal_GrCoordSet, background,
112                               GrTextureParams::kNone_FilterMode);
113    this->addCoordTransform(&fBackgroundTransform);
114    fBackgroundAccess.reset(background);
115    this->addTextureAccess(&fBackgroundAccess);
116}
117
118void GrArithmeticFP::getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
119    GLArithmeticFP::GenKey(*this, caps, b);
120}
121
122GrGLFragmentProcessor* GrArithmeticFP::createGLInstance() const {
123    return SkNEW_ARGS(GLArithmeticFP, (*this));
124}
125
126bool GrArithmeticFP::onIsEqual(const GrFragmentProcessor& fpBase) const {
127    const GrArithmeticFP& fp = fpBase.cast<GrArithmeticFP>();
128    return fK1 == fp.fK1 &&
129           fK2 == fp.fK2 &&
130           fK3 == fp.fK3 &&
131           fK4 == fp.fK4 &&
132           fEnforcePMColor == fp.fEnforcePMColor;
133}
134
135void GrArithmeticFP::onComputeInvariantOutput(GrInvariantOutput* inout) const {
136    // TODO: optimize this
137    inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
138}
139
140///////////////////////////////////////////////////////////////////////////////
141
142GrFragmentProcessor* GrArithmeticFP::TestCreate(SkRandom* rand,
143                                                GrContext*,
144                                                const GrDrawTargetCaps&,
145                                                GrTexture* textures[]) {
146    float k1 = rand->nextF();
147    float k2 = rand->nextF();
148    float k3 = rand->nextF();
149    float k4 = rand->nextF();
150    bool enforcePMColor = rand->nextBool();
151
152    return SkNEW_ARGS(GrArithmeticFP, (k1, k2, k3, k4, enforcePMColor, textures[0]));
153}
154
155GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrArithmeticFP);
156
157///////////////////////////////////////////////////////////////////////////////
158// Xfer Processor
159///////////////////////////////////////////////////////////////////////////////
160
161class ArithmeticXP : public GrXferProcessor {
162public:
163    static GrXferProcessor* Create(float k1, float k2, float k3, float k4, bool enforcePMColor,
164                                   const GrDeviceCoordTexture* dstCopy,
165                                   bool willReadDstColor) {
166        return SkNEW_ARGS(ArithmeticXP, (k1, k2, k3, k4, enforcePMColor, dstCopy,
167                                         willReadDstColor));
168    }
169
170    ~ArithmeticXP() override {};
171
172    const char* name() const override { return "Arithmetic"; }
173
174    GrGLXferProcessor* createGLInstance() const override;
175
176    bool hasSecondaryOutput() const override { return false; }
177
178    float k1() const { return fK1; }
179    float k2() const { return fK2; }
180    float k3() const { return fK3; }
181    float k4() const { return fK4; }
182    bool enforcePMColor() const { return fEnforcePMColor; }
183
184private:
185    ArithmeticXP(float k1, float k2, float k3, float k4, bool enforcePMColor,
186                   const GrDeviceCoordTexture* dstCopy, bool willReadDstColor);
187
188    GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
189                                                 const GrProcOptInfo& coveragePOI,
190                                                 bool doesStencilWrite,
191                                                 GrColor* overrideColor,
192                                                 const GrDrawTargetCaps& caps) override;
193
194    void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
195
196    bool onIsEqual(const GrXferProcessor& xpBase) const override {
197        const ArithmeticXP& xp = xpBase.cast<ArithmeticXP>();
198        if (fK1 != xp.fK1 ||
199            fK2 != xp.fK2 ||
200            fK3 != xp.fK3 ||
201            fK4 != xp.fK4 ||
202            fEnforcePMColor != xp.fEnforcePMColor) {
203            return false;
204        }
205        return true;
206    }
207
208    float                       fK1, fK2, fK3, fK4;
209    bool                        fEnforcePMColor;
210
211    typedef GrXferProcessor INHERITED;
212};
213
214///////////////////////////////////////////////////////////////////////////////
215
216class GLArithmeticXP : public GrGLXferProcessor {
217public:
218    GLArithmeticXP(const GrProcessor&)
219        : fEnforcePMColor(true) {
220    }
221
222    ~GLArithmeticXP() override {}
223
224    static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
225                       GrProcessorKeyBuilder* b) {
226        const ArithmeticXP& arith = processor.cast<ArithmeticXP>();
227        uint32_t key = arith.enforcePMColor() ? 1 : 0;
228        b->add32(key);
229    }
230
231private:
232    void onEmitCode(const EmitArgs& args) override {
233        GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
234
235        const char* dstColor = fsBuilder->dstColor();
236
237        fKUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility,
238                                     kVec4f_GrSLType, kDefault_GrSLPrecision,
239                                     "k");
240        const char* kUni = args.fPB->getUniformCStr(fKUni);
241
242        add_arithmetic_code(fsBuilder, args.fInputColor, dstColor, args.fOutputPrimary, kUni,
243                            fEnforcePMColor);
244
245        fsBuilder->codeAppendf("%s = %s * %s + (vec4(1.0) - %s) * %s;",
246                               args.fOutputPrimary, args.fOutputPrimary, args.fInputCoverage,
247                               args.fInputCoverage, dstColor);
248    }
249
250    void onSetData(const GrGLProgramDataManager& pdman,
251                   const GrXferProcessor& processor) override {
252        const ArithmeticXP& arith = processor.cast<ArithmeticXP>();
253        pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4());
254        fEnforcePMColor = arith.enforcePMColor();
255    };
256
257    GrGLProgramDataManager::UniformHandle fKUni;
258    bool fEnforcePMColor;
259
260    typedef GrGLXferProcessor INHERITED;
261};
262
263///////////////////////////////////////////////////////////////////////////////
264
265ArithmeticXP::ArithmeticXP(float k1, float k2, float k3, float k4, bool enforcePMColor,
266                           const GrDeviceCoordTexture* dstCopy, bool willReadDstColor)
267    : INHERITED(dstCopy, willReadDstColor)
268    , fK1(k1)
269    , fK2(k2)
270    , fK3(k3)
271    , fK4(k4)
272    , fEnforcePMColor(enforcePMColor) {
273    this->initClassID<ArithmeticXP>();
274}
275
276void ArithmeticXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
277    GLArithmeticXP::GenKey(*this, caps, b);
278}
279
280GrGLXferProcessor* ArithmeticXP::createGLInstance() const {
281    return SkNEW_ARGS(GLArithmeticXP, (*this));
282}
283
284GrXferProcessor::OptFlags ArithmeticXP::onGetOptimizations(const GrProcOptInfo& colorPOI,
285                                                           const GrProcOptInfo& coveragePOI,
286                                                           bool doesStencilWrite,
287                                                           GrColor* overrideColor,
288                                                           const GrDrawTargetCaps& caps) {
289   return GrXferProcessor::kNone_Opt;
290}
291
292///////////////////////////////////////////////////////////////////////////////
293
294GrArithmeticXPFactory::GrArithmeticXPFactory(float k1, float k2, float k3, float k4,
295                                             bool enforcePMColor)
296    : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) {
297    this->initClassID<GrArithmeticXPFactory>();
298}
299
300GrXferProcessor*
301GrArithmeticXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps,
302                                             const GrProcOptInfo& colorPOI,
303                                             const GrProcOptInfo& coveragePOI,
304                                             const GrDeviceCoordTexture* dstCopy) const {
305    return ArithmeticXP::Create(fK1, fK2, fK3, fK4, fEnforcePMColor, dstCopy,
306                                this->willReadDstColor(caps, colorPOI, coveragePOI));
307}
308
309
310void GrArithmeticXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
311                                               const GrProcOptInfo& coveragePOI,
312                                               GrXPFactory::InvariantOutput* output) const {
313    output->fWillBlendWithDst = true;
314
315    // TODO: We could try to optimize this more. For example if we have solid coverage and fK1 and
316    // fK3 are zero, then we won't be blending the color with dst at all so we can know what the
317    // output color is (up to the valid color components passed in).
318    output->fBlendedColorFlags = 0;
319}
320
321GR_DEFINE_XP_FACTORY_TEST(GrArithmeticXPFactory);
322
323GrXPFactory* GrArithmeticXPFactory::TestCreate(SkRandom* random,
324                                               GrContext*,
325                                               const GrDrawTargetCaps&,
326                                               GrTexture*[]) {
327    float k1 = random->nextF();
328    float k2 = random->nextF();
329    float k3 = random->nextF();
330    float k4 = random->nextF();
331    bool enforcePMColor = random->nextBool();
332
333    return GrArithmeticXPFactory::Create(k1, k2, k3, k4, enforcePMColor);
334}
335
336#endif
337