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#include "GrYUVEffect.h"
9
10#include "GrCoordTransform.h"
11#include "GrFragmentProcessor.h"
12#include "GrInvariantOutput.h"
13#include "GrProcessor.h"
14#include "glsl/GrGLSLFragmentProcessor.h"
15#include "glsl/GrGLSLFragmentShaderBuilder.h"
16#include "glsl/GrGLSLProgramDataManager.h"
17#include "glsl/GrGLSLUniformHandler.h"
18
19namespace {
20
21static const float kJPEGConversionMatrix[16] = {
22    1.0f,  0.0f,      1.402f,  -0.701f,
23    1.0f, -0.34414f, -0.71414f, 0.529f,
24    1.0f,  1.772f,    0.0f,    -0.886f,
25    0.0f,  0.0f,      0.0f,     1.0
26};
27
28static const float kRec601ConversionMatrix[16] = {
29    1.164f,  0.0f,    1.596f, -0.87075f,
30    1.164f, -0.391f, -0.813f,  0.52925f,
31    1.164f,  2.018f,  0.0f,   -1.08175f,
32    0.0f,    0.0f,    0.0f,    1.0}
33;
34
35static const float kRec709ConversionMatrix[16] = {
36    1.164f,  0.0f,    1.793f, -0.96925f,
37    1.164f, -0.213f, -0.533f,  0.30025f,
38    1.164f,  2.112f,  0.0f,   -1.12875f,
39    0.0f,    0.0f,    0.0f,    1.0f}
40;
41
42static const float kJPEGInverseConversionMatrix[16] = {
43     0.299001f,  0.586998f,  0.114001f,  0.0000821798f,
44    -0.168736f, -0.331263f,  0.499999f,  0.499954f,
45     0.499999f, -0.418686f, -0.0813131f, 0.499941f,
46     0.f,        0.f,        0.f,        1.f
47};
48
49static const float kRec601InverseConversionMatrix[16] = {
50     0.256951f,  0.504421f,  0.0977346f, 0.0625f,
51    -0.148212f, -0.290954f,  0.439166f,  0.5f,
52     0.439166f, -0.367886f, -0.0712802f, 0.5f,
53     0.f,        0.f,        0.f,        1.f
54};
55
56static const float kRec709InverseConversionMatrix[16] = {
57     0.182663f,  0.614473f, 0.061971f, 0.0625f,
58    -0.100672f, -0.338658f, 0.43933f,  0.5f,
59     0.439142f, -0.39891f, -0.040231f, 0.5f,
60     0.f,        0.f,       0.f,       1.
61};
62
63class YUVtoRGBEffect : public GrFragmentProcessor {
64public:
65    static GrFragmentProcessor* Create(GrTexture* yTexture, GrTexture* uTexture,
66                                       GrTexture* vTexture, const SkISize sizes[3],
67                                       SkYUVColorSpace colorSpace) {
68        SkScalar w[3], h[3];
69        w[0] = SkIntToScalar(sizes[0].fWidth)  / SkIntToScalar(yTexture->width());
70        h[0] = SkIntToScalar(sizes[0].fHeight) / SkIntToScalar(yTexture->height());
71        w[1] = SkIntToScalar(sizes[1].fWidth)  / SkIntToScalar(uTexture->width());
72        h[1] = SkIntToScalar(sizes[1].fHeight) / SkIntToScalar(uTexture->height());
73        w[2] = SkIntToScalar(sizes[2].fWidth)  / SkIntToScalar(vTexture->width());
74        h[2] = SkIntToScalar(sizes[2].fHeight) / SkIntToScalar(vTexture->height());
75        SkMatrix yuvMatrix[3];
76        yuvMatrix[0] = GrCoordTransform::MakeDivByTextureWHMatrix(yTexture);
77        yuvMatrix[1] = yuvMatrix[0];
78        yuvMatrix[1].preScale(w[1] / w[0], h[1] / h[0]);
79        yuvMatrix[2] = yuvMatrix[0];
80        yuvMatrix[2].preScale(w[2] / w[0], h[2] / h[0]);
81        GrTextureParams::FilterMode uvFilterMode =
82            ((sizes[1].fWidth  != sizes[0].fWidth) ||
83             (sizes[1].fHeight != sizes[0].fHeight) ||
84             (sizes[2].fWidth  != sizes[0].fWidth) ||
85             (sizes[2].fHeight != sizes[0].fHeight)) ?
86            GrTextureParams::kBilerp_FilterMode :
87            GrTextureParams::kNone_FilterMode;
88        return new YUVtoRGBEffect(yTexture, uTexture, vTexture, yuvMatrix, uvFilterMode,
89                                  colorSpace);
90    }
91
92    const char* name() const override { return "YUV to RGB"; }
93
94    SkYUVColorSpace getColorSpace() const { return fColorSpace; }
95
96    class GLSLProcessor : public GrGLSLFragmentProcessor {
97    public:
98        // this class always generates the same code.
99        static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {}
100
101        void emitCode(EmitArgs& args) override {
102            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
103
104            const char* colorSpaceMatrix = nullptr;
105            fMatrixUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
106                                                          kMat44f_GrSLType, kDefault_GrSLPrecision,
107                                                          "ColorSpaceMatrix", &colorSpaceMatrix);
108            fragBuilder->codeAppendf("%s = vec4(", args.fOutputColor);
109            fragBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(),
110                                             args.fCoords[0].getType());
111            fragBuilder->codeAppend(".r,");
112            fragBuilder->appendTextureLookup(args.fSamplers[1], args.fCoords[1].c_str(),
113                                             args.fCoords[1].getType());
114            fragBuilder->codeAppend(".r,");
115            fragBuilder->appendTextureLookup(args.fSamplers[2], args.fCoords[2].c_str(),
116                                             args.fCoords[2].getType());
117            fragBuilder->codeAppendf(".r, 1.0) * %s;", colorSpaceMatrix);
118        }
119
120    protected:
121        void onSetData(const GrGLSLProgramDataManager& pdman,
122                       const GrProcessor& processor) override {
123            const YUVtoRGBEffect& yuvEffect = processor.cast<YUVtoRGBEffect>();
124            switch (yuvEffect.getColorSpace()) {
125                case kJPEG_SkYUVColorSpace:
126                    pdman.setMatrix4f(fMatrixUni, kJPEGConversionMatrix);
127                    break;
128                case kRec601_SkYUVColorSpace:
129                    pdman.setMatrix4f(fMatrixUni, kRec601ConversionMatrix);
130                    break;
131                case kRec709_SkYUVColorSpace:
132                    pdman.setMatrix4f(fMatrixUni, kRec709ConversionMatrix);
133                    break;
134            }
135        }
136
137    private:
138        GrGLSLProgramDataManager::UniformHandle fMatrixUni;
139
140        typedef GrGLSLFragmentProcessor INHERITED;
141    };
142
143private:
144    YUVtoRGBEffect(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
145                   const SkMatrix yuvMatrix[3], GrTextureParams::FilterMode uvFilterMode,
146                   SkYUVColorSpace colorSpace)
147    : fYTransform(kLocal_GrCoordSet, yuvMatrix[0], yTexture, GrTextureParams::kNone_FilterMode)
148    , fYAccess(yTexture)
149    , fUTransform(kLocal_GrCoordSet, yuvMatrix[1], uTexture, uvFilterMode)
150    , fUAccess(uTexture, uvFilterMode)
151    , fVTransform(kLocal_GrCoordSet, yuvMatrix[2], vTexture, uvFilterMode)
152    , fVAccess(vTexture, uvFilterMode)
153    , fColorSpace(colorSpace) {
154        this->initClassID<YUVtoRGBEffect>();
155        this->addCoordTransform(&fYTransform);
156        this->addTextureAccess(&fYAccess);
157        this->addCoordTransform(&fUTransform);
158        this->addTextureAccess(&fUAccess);
159        this->addCoordTransform(&fVTransform);
160        this->addTextureAccess(&fVAccess);
161    }
162
163    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
164        return new GLSLProcessor;
165    }
166
167    void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
168        GLSLProcessor::GenKey(*this, caps, b);
169    }
170
171    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
172        const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>();
173        return fColorSpace == s.getColorSpace();
174    }
175
176    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
177        // YUV is opaque
178        inout->setToOther(kA_GrColorComponentFlag, 0xFF << GrColor_SHIFT_A,
179                          GrInvariantOutput::kWillNot_ReadInput);
180    }
181
182    GrCoordTransform fYTransform;
183    GrTextureAccess fYAccess;
184    GrCoordTransform fUTransform;
185    GrTextureAccess fUAccess;
186    GrCoordTransform fVTransform;
187    GrTextureAccess fVAccess;
188    SkYUVColorSpace fColorSpace;
189
190    typedef GrFragmentProcessor INHERITED;
191};
192
193
194class RGBToYUVEffect : public GrFragmentProcessor {
195public:
196    enum OutputChannels {
197        // output color r = y, g = u, b = v, a = a
198        kYUV_OutputChannels,
199        // output color rgba = y
200        kY_OutputChannels,
201        // output color r = u, g = v, b = 0, a = a
202        kUV_OutputChannels,
203        // output color rgba = u
204        kU_OutputChannels,
205        // output color rgba = v
206        kV_OutputChannels
207    };
208
209    RGBToYUVEffect(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace,
210                   OutputChannels output)
211        : fColorSpace(colorSpace)
212        , fOutputChannels(output) {
213        this->initClassID<RGBToYUVEffect>();
214        this->registerChildProcessor(rgbFP);
215    }
216
217    const char* name() const override { return "RGBToYUV"; }
218
219    SkYUVColorSpace getColorSpace() const { return fColorSpace; }
220
221    OutputChannels outputChannels() const { return fOutputChannels; }
222
223    class GLSLProcessor : public GrGLSLFragmentProcessor {
224    public:
225        GLSLProcessor() : fLastColorSpace(-1), fLastOutputChannels(-1) {}
226
227        void emitCode(EmitArgs& args) override {
228            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
229            OutputChannels oc = args.fFp.cast<RGBToYUVEffect>().outputChannels();
230
231            SkString outputColor("rgbColor");
232            this->emitChild(0, args.fInputColor, &outputColor, args);
233
234            const char* uniName;
235            switch (oc) {
236                case kYUV_OutputChannels:
237                    fRGBToYUVUni = args.fUniformHandler->addUniformArray(
238                        kFragment_GrShaderFlag,
239                        kVec4f_GrSLType, kDefault_GrSLPrecision,
240                        "RGBToYUV", 3, &uniName);
241                    fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s[0].rgb) + %s[0].a,"
242                                                       "dot(rgbColor.rgb, %s[1].rgb) + %s[1].a,"
243                                                       "dot(rgbColor.rgb, %s[2].rgb) + %s[2].a,"
244                                                       "rgbColor.a);",
245                                             args.fOutputColor, uniName, uniName, uniName, uniName,
246                                             uniName, uniName);
247                    break;
248                case kUV_OutputChannels:
249                    fRGBToYUVUni = args.fUniformHandler->addUniformArray(
250                        kFragment_GrShaderFlag,
251                        kVec4f_GrSLType, kDefault_GrSLPrecision,
252                        "RGBToUV", 2, &uniName);
253                    fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s[0].rgb) + %s[0].a,"
254                                                       "dot(rgbColor.rgb, %s[1].rgb) + %s[1].a,"
255                                                       "0.0,"
256                                                       "rgbColor.a);",
257                                             args.fOutputColor, uniName, uniName, uniName, uniName);
258                    break;
259                case kY_OutputChannels:
260                case kU_OutputChannels:
261                case kV_OutputChannels:
262                    fRGBToYUVUni = args.fUniformHandler->addUniform(
263                        kFragment_GrShaderFlag,
264                        kVec4f_GrSLType, kDefault_GrSLPrecision,
265                        "RGBToYUorV", &uniName);
266                    fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s.rgb) + %s.a);\n",
267                                             args.fOutputColor, uniName, uniName);
268                    break;
269            }
270        }
271
272    private:
273        void onSetData(const GrGLSLProgramDataManager& pdman,
274                       const GrProcessor& processor) override {
275            const RGBToYUVEffect& effect = processor.cast<RGBToYUVEffect>();
276            OutputChannels oc = effect.outputChannels();
277            if (effect.getColorSpace() != fLastColorSpace || oc != fLastOutputChannels) {
278
279                const float* matrix = nullptr;
280                switch (effect.getColorSpace()) {
281                    case kJPEG_SkYUVColorSpace:
282                        matrix = kJPEGInverseConversionMatrix;
283                        break;
284                    case kRec601_SkYUVColorSpace:
285                        matrix = kRec601InverseConversionMatrix;
286                        break;
287                    case kRec709_SkYUVColorSpace:
288                        matrix = kRec709InverseConversionMatrix;
289                        break;
290                }
291                switch (oc) {
292                    case kYUV_OutputChannels:
293                        pdman.set4fv(fRGBToYUVUni, 3, matrix);
294                        break;
295                    case kUV_OutputChannels:
296                        pdman.set4fv(fRGBToYUVUni, 2, matrix + 4);
297                        break;
298                    case kY_OutputChannels:
299                        pdman.set4fv(fRGBToYUVUni, 1, matrix);
300                        break;
301                    case kU_OutputChannels:
302                        pdman.set4fv(fRGBToYUVUni, 1, matrix + 4);
303                        break;
304                    case kV_OutputChannels:
305                        pdman.set4fv(fRGBToYUVUni, 1, matrix + 8);
306                        break;
307                }
308                fLastColorSpace = effect.getColorSpace();
309            }
310        }
311        GrGLSLProgramDataManager::UniformHandle fRGBToYUVUni;
312        int                                     fLastColorSpace;
313        int                                     fLastOutputChannels;
314
315        typedef GrGLSLFragmentProcessor INHERITED;
316    };
317
318private:
319    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
320        return new GLSLProcessor;
321    }
322
323    void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
324        // kY, kU, and kV all generate the same code, just upload different coefficients.
325        if (kU_OutputChannels == fOutputChannels || kV_OutputChannels == fOutputChannels) {
326            b->add32(kY_OutputChannels);
327        } else {
328            b->add32(fOutputChannels);
329        }
330    }
331
332    bool onIsEqual(const GrFragmentProcessor& sBase) const override {
333        const RGBToYUVEffect& s = sBase.cast<RGBToYUVEffect>();
334        return fColorSpace == s.getColorSpace() && fOutputChannels == s.outputChannels();
335    }
336
337    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
338        inout->setToUnknown(GrInvariantOutput::kWillNot_ReadInput);
339    }
340
341    GrCoordTransform    fTransform;
342    GrTextureAccess     fAccess;
343    SkYUVColorSpace     fColorSpace;
344    OutputChannels      fOutputChannels;
345
346    typedef GrFragmentProcessor INHERITED;
347};
348
349}
350
351//////////////////////////////////////////////////////////////////////////////
352
353const GrFragmentProcessor*
354GrYUVEffect::CreateYUVToRGB(GrTexture* yTexture, GrTexture* uTexture, GrTexture* vTexture,
355                            const SkISize sizes[3], SkYUVColorSpace colorSpace) {
356    SkASSERT(yTexture && uTexture && vTexture && sizes);
357    return YUVtoRGBEffect::Create(yTexture, uTexture, vTexture, sizes, colorSpace);
358}
359
360const GrFragmentProcessor*
361GrYUVEffect::CreateRGBToYUV(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) {
362    SkASSERT(rgbFP);
363    return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kYUV_OutputChannels);
364}
365
366const GrFragmentProcessor*
367GrYUVEffect::CreateRGBToY(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) {
368    SkASSERT(rgbFP);
369    return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kY_OutputChannels);
370}
371
372const GrFragmentProcessor*
373GrYUVEffect::CreateRGBToUV(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) {
374    SkASSERT(rgbFP);
375    return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kUV_OutputChannels);
376}
377
378const GrFragmentProcessor*
379GrYUVEffect::CreateRGBToU(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) {
380    SkASSERT(rgbFP);
381    return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kU_OutputChannels);
382}
383
384const GrFragmentProcessor*
385GrYUVEffect::CreateRGBToV(const GrFragmentProcessor* rgbFP, SkYUVColorSpace colorSpace) {
386    SkASSERT(rgbFP);
387    return new RGBToYUVEffect(rgbFP, colorSpace, RGBToYUVEffect::kV_OutputChannels);
388}
389