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 "GrProcessor.h"
13 #include "GrTextureProxy.h"
14 #include "glsl/GrGLSLFragmentProcessor.h"
15 #include "glsl/GrGLSLFragmentShaderBuilder.h"
16 #include "glsl/GrGLSLProgramDataManager.h"
17 #include "glsl/GrGLSLUniformHandler.h"
18
19 namespace {
20
21 static 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
28 static 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
35 static 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
42 static 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
49 static 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
56 static 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
63 class YUVtoRGBEffect : public GrFragmentProcessor {
64 public:
Make(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> yProxy,sk_sp<GrTextureProxy> uProxy,sk_sp<GrTextureProxy> vProxy,const SkISize sizes[3],SkYUVColorSpace colorSpace,bool nv12)65 static sk_sp<GrFragmentProcessor> Make(GrResourceProvider* resourceProvider,
66 sk_sp<GrTextureProxy> yProxy,
67 sk_sp<GrTextureProxy> uProxy,
68 sk_sp<GrTextureProxy> vProxy, const SkISize sizes[3],
69 SkYUVColorSpace colorSpace, bool nv12) {
70 SkScalar w[3], h[3];
71 w[0] = SkIntToScalar(sizes[0].fWidth);
72 h[0] = SkIntToScalar(sizes[0].fHeight);
73 w[1] = SkIntToScalar(sizes[1].fWidth);
74 h[1] = SkIntToScalar(sizes[1].fHeight);
75 w[2] = SkIntToScalar(sizes[2].fWidth);
76 h[2] = SkIntToScalar(sizes[2].fHeight);
77 const SkMatrix yuvMatrix[3] = {
78 SkMatrix::I(),
79 SkMatrix::MakeScale(w[1] / w[0], h[1] / h[0]),
80 SkMatrix::MakeScale(w[2] / w[0], h[2] / h[0])
81 };
82 GrSamplerParams::FilterMode uvFilterMode =
83 ((sizes[1].fWidth != sizes[0].fWidth) ||
84 (sizes[1].fHeight != sizes[0].fHeight) ||
85 (sizes[2].fWidth != sizes[0].fWidth) ||
86 (sizes[2].fHeight != sizes[0].fHeight)) ?
87 GrSamplerParams::kBilerp_FilterMode :
88 GrSamplerParams::kNone_FilterMode;
89 return sk_sp<GrFragmentProcessor>(new YUVtoRGBEffect(
90 resourceProvider, std::move(yProxy), std::move(uProxy), std::move(vProxy),
91 yuvMatrix, uvFilterMode, colorSpace, nv12));
92 }
93
name() const94 const char* name() const override { return "YUV to RGB"; }
95
getColorSpace() const96 SkYUVColorSpace getColorSpace() const { return fColorSpace; }
97
isNV12() const98 bool isNV12() const {
99 return fNV12;
100 }
101
102 class GLSLProcessor : public GrGLSLFragmentProcessor {
103 public:
emitCode(EmitArgs & args)104 void emitCode(EmitArgs& args) override {
105 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
106 const YUVtoRGBEffect& effect = args.fFp.cast<YUVtoRGBEffect>();
107
108 const char* colorSpaceMatrix = nullptr;
109 fMatrixUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
110 kMat44f_GrSLType, kDefault_GrSLPrecision,
111 "ColorSpaceMatrix", &colorSpaceMatrix);
112 fragBuilder->codeAppendf("%s = vec4(", args.fOutputColor);
113 fragBuilder->appendTextureLookup(args.fTexSamplers[0],
114 args.fTransformedCoords[0].c_str(),
115 args.fTransformedCoords[0].getType());
116 fragBuilder->codeAppend(".r,");
117 fragBuilder->appendTextureLookup(args.fTexSamplers[1],
118 args.fTransformedCoords[1].c_str(),
119 args.fTransformedCoords[1].getType());
120 if (effect.fNV12) {
121 fragBuilder->codeAppendf(".rg,");
122 } else {
123 fragBuilder->codeAppend(".r,");
124 fragBuilder->appendTextureLookup(args.fTexSamplers[2],
125 args.fTransformedCoords[2].c_str(),
126 args.fTransformedCoords[2].getType());
127 fragBuilder->codeAppendf(".g,");
128 }
129 fragBuilder->codeAppendf("1.0) * %s;", colorSpaceMatrix);
130 }
131
132 protected:
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)133 void onSetData(const GrGLSLProgramDataManager& pdman,
134 const GrProcessor& processor) override {
135 const YUVtoRGBEffect& yuvEffect = processor.cast<YUVtoRGBEffect>();
136 switch (yuvEffect.getColorSpace()) {
137 case kJPEG_SkYUVColorSpace:
138 pdman.setMatrix4f(fMatrixUni, kJPEGConversionMatrix);
139 break;
140 case kRec601_SkYUVColorSpace:
141 pdman.setMatrix4f(fMatrixUni, kRec601ConversionMatrix);
142 break;
143 case kRec709_SkYUVColorSpace:
144 pdman.setMatrix4f(fMatrixUni, kRec709ConversionMatrix);
145 break;
146 }
147 }
148
149 private:
150 GrGLSLProgramDataManager::UniformHandle fMatrixUni;
151
152 typedef GrGLSLFragmentProcessor INHERITED;
153 };
154
155 private:
YUVtoRGBEffect(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> yProxy,sk_sp<GrTextureProxy> uProxy,sk_sp<GrTextureProxy> vProxy,const SkMatrix yuvMatrix[3],GrSamplerParams::FilterMode uvFilterMode,SkYUVColorSpace colorSpace,bool nv12)156 YUVtoRGBEffect(GrResourceProvider* resourceProvider,
157 sk_sp<GrTextureProxy> yProxy, sk_sp<GrTextureProxy> uProxy,
158 sk_sp<GrTextureProxy> vProxy, const SkMatrix yuvMatrix[3],
159 GrSamplerParams::FilterMode uvFilterMode, SkYUVColorSpace colorSpace, bool nv12)
160 : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
161 , fYTransform(resourceProvider, yuvMatrix[0], yProxy.get(),
162 GrSamplerParams::kNone_FilterMode)
163 , fYSampler(resourceProvider, std::move(yProxy))
164 , fUTransform(resourceProvider, yuvMatrix[1], uProxy.get(), uvFilterMode)
165 , fUSampler(resourceProvider, std::move(uProxy), uvFilterMode)
166 , fVSampler(resourceProvider, vProxy, uvFilterMode)
167 , fColorSpace(colorSpace)
168 , fNV12(nv12) {
169 this->initClassID<YUVtoRGBEffect>();
170 this->addCoordTransform(&fYTransform);
171 this->addTextureSampler(&fYSampler);
172 this->addCoordTransform(&fUTransform);
173 this->addTextureSampler(&fUSampler);
174 if (!fNV12) {
175 fVTransform = GrCoordTransform(resourceProvider, yuvMatrix[2], vProxy.get(),
176 uvFilterMode);
177 this->addCoordTransform(&fVTransform);
178 this->addTextureSampler(&fVSampler);
179 }
180 }
181
onCreateGLSLInstance() const182 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
183 return new GLSLProcessor;
184 }
185
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const186 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
187 b->add32(fNV12);
188 }
189
onIsEqual(const GrFragmentProcessor & sBase) const190 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
191 const YUVtoRGBEffect& s = sBase.cast<YUVtoRGBEffect>();
192 return (fColorSpace == s.getColorSpace()) && (fNV12 == s.isNV12());
193 }
194
195 GrCoordTransform fYTransform;
196 TextureSampler fYSampler;
197 GrCoordTransform fUTransform;
198 TextureSampler fUSampler;
199 GrCoordTransform fVTransform;
200 TextureSampler fVSampler;
201 SkYUVColorSpace fColorSpace;
202 bool fNV12;
203
204 typedef GrFragmentProcessor INHERITED;
205 };
206
207
208 class RGBToYUVEffect : public GrFragmentProcessor {
209 public:
210 enum OutputChannels {
211 // output color r = y, g = u, b = v, a = a
212 kYUV_OutputChannels,
213 // output color rgba = y
214 kY_OutputChannels,
215 // output color r = u, g = v, b = 0, a = a
216 kUV_OutputChannels,
217 // output color rgba = u
218 kU_OutputChannels,
219 // output color rgba = v
220 kV_OutputChannels
221 };
222
RGBToYUVEffect(sk_sp<GrFragmentProcessor> rgbFP,SkYUVColorSpace colorSpace,OutputChannels output)223 RGBToYUVEffect(sk_sp<GrFragmentProcessor> rgbFP, SkYUVColorSpace colorSpace,
224 OutputChannels output)
225 // This could advertise kConstantOutputForConstantInput, but doesn't seem useful.
226 : INHERITED(kPreservesOpaqueInput_OptimizationFlag)
227 , fColorSpace(colorSpace)
228 , fOutputChannels(output) {
229 this->initClassID<RGBToYUVEffect>();
230 this->registerChildProcessor(std::move(rgbFP));
231 }
232
name() const233 const char* name() const override { return "RGBToYUV"; }
234
getColorSpace() const235 SkYUVColorSpace getColorSpace() const { return fColorSpace; }
236
outputChannels() const237 OutputChannels outputChannels() const { return fOutputChannels; }
238
239 class GLSLProcessor : public GrGLSLFragmentProcessor {
240 public:
GLSLProcessor()241 GLSLProcessor() : fLastColorSpace(-1), fLastOutputChannels(-1) {}
242
emitCode(EmitArgs & args)243 void emitCode(EmitArgs& args) override {
244 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
245 OutputChannels oc = args.fFp.cast<RGBToYUVEffect>().outputChannels();
246
247 SkString outputColor("rgbColor");
248 this->emitChild(0, args.fInputColor, &outputColor, args);
249
250 const char* uniName;
251 switch (oc) {
252 case kYUV_OutputChannels:
253 fRGBToYUVUni = args.fUniformHandler->addUniformArray(
254 kFragment_GrShaderFlag,
255 kVec4f_GrSLType, kDefault_GrSLPrecision,
256 "RGBToYUV", 3, &uniName);
257 fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s[0].rgb) + %s[0].a,"
258 "dot(rgbColor.rgb, %s[1].rgb) + %s[1].a,"
259 "dot(rgbColor.rgb, %s[2].rgb) + %s[2].a,"
260 "rgbColor.a);",
261 args.fOutputColor, uniName, uniName, uniName, uniName,
262 uniName, uniName);
263 break;
264 case kUV_OutputChannels:
265 fRGBToYUVUni = args.fUniformHandler->addUniformArray(
266 kFragment_GrShaderFlag,
267 kVec4f_GrSLType, kDefault_GrSLPrecision,
268 "RGBToUV", 2, &uniName);
269 fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s[0].rgb) + %s[0].a,"
270 "dot(rgbColor.rgb, %s[1].rgb) + %s[1].a,"
271 "0.0,"
272 "rgbColor.a);",
273 args.fOutputColor, uniName, uniName, uniName, uniName);
274 break;
275 case kY_OutputChannels:
276 case kU_OutputChannels:
277 case kV_OutputChannels:
278 fRGBToYUVUni = args.fUniformHandler->addUniform(
279 kFragment_GrShaderFlag,
280 kVec4f_GrSLType, kDefault_GrSLPrecision,
281 "RGBToYUorV", &uniName);
282 fragBuilder->codeAppendf("%s = vec4(dot(rgbColor.rgb, %s.rgb) + %s.a);\n",
283 args.fOutputColor, uniName, uniName);
284 break;
285 }
286 }
287
288 private:
onSetData(const GrGLSLProgramDataManager & pdman,const GrProcessor & processor)289 void onSetData(const GrGLSLProgramDataManager& pdman,
290 const GrProcessor& processor) override {
291 const RGBToYUVEffect& effect = processor.cast<RGBToYUVEffect>();
292 OutputChannels oc = effect.outputChannels();
293 if (effect.getColorSpace() != fLastColorSpace || oc != fLastOutputChannels) {
294
295 const float* matrix = nullptr;
296 switch (effect.getColorSpace()) {
297 case kJPEG_SkYUVColorSpace:
298 matrix = kJPEGInverseConversionMatrix;
299 break;
300 case kRec601_SkYUVColorSpace:
301 matrix = kRec601InverseConversionMatrix;
302 break;
303 case kRec709_SkYUVColorSpace:
304 matrix = kRec709InverseConversionMatrix;
305 break;
306 }
307 switch (oc) {
308 case kYUV_OutputChannels:
309 pdman.set4fv(fRGBToYUVUni, 3, matrix);
310 break;
311 case kUV_OutputChannels:
312 pdman.set4fv(fRGBToYUVUni, 2, matrix + 4);
313 break;
314 case kY_OutputChannels:
315 pdman.set4fv(fRGBToYUVUni, 1, matrix);
316 break;
317 case kU_OutputChannels:
318 pdman.set4fv(fRGBToYUVUni, 1, matrix + 4);
319 break;
320 case kV_OutputChannels:
321 pdman.set4fv(fRGBToYUVUni, 1, matrix + 8);
322 break;
323 }
324 fLastColorSpace = effect.getColorSpace();
325 }
326 }
327 GrGLSLProgramDataManager::UniformHandle fRGBToYUVUni;
328 int fLastColorSpace;
329 int fLastOutputChannels;
330
331 typedef GrGLSLFragmentProcessor INHERITED;
332 };
333
334 private:
onCreateGLSLInstance() const335 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
336 return new GLSLProcessor;
337 }
338
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const339 void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
340 // kY, kU, and kV all generate the same code, just upload different coefficients.
341 if (kU_OutputChannels == fOutputChannels || kV_OutputChannels == fOutputChannels) {
342 b->add32(kY_OutputChannels);
343 } else {
344 b->add32(fOutputChannels);
345 }
346 }
347
onIsEqual(const GrFragmentProcessor & sBase) const348 bool onIsEqual(const GrFragmentProcessor& sBase) const override {
349 const RGBToYUVEffect& s = sBase.cast<RGBToYUVEffect>();
350 return fColorSpace == s.getColorSpace() && fOutputChannels == s.outputChannels();
351 }
352
353 GrCoordTransform fTransform;
354 TextureSampler fTextureSampler;
355 SkYUVColorSpace fColorSpace;
356 OutputChannels fOutputChannels;
357
358 typedef GrFragmentProcessor INHERITED;
359 };
360
361 }
362
363 //////////////////////////////////////////////////////////////////////////////
364
MakeYUVToRGB(GrResourceProvider * resourceProvider,sk_sp<GrTextureProxy> yProxy,sk_sp<GrTextureProxy> uProxy,sk_sp<GrTextureProxy> vProxy,const SkISize sizes[3],SkYUVColorSpace colorSpace,bool nv12)365 sk_sp<GrFragmentProcessor> GrYUVEffect::MakeYUVToRGB(GrResourceProvider* resourceProvider,
366 sk_sp<GrTextureProxy> yProxy,
367 sk_sp<GrTextureProxy> uProxy,
368 sk_sp<GrTextureProxy> vProxy,
369 const SkISize sizes[3],
370 SkYUVColorSpace colorSpace, bool nv12) {
371 SkASSERT(yProxy && uProxy && vProxy && sizes);
372 return YUVtoRGBEffect::Make(resourceProvider,
373 std::move(yProxy), std::move(uProxy), std::move(vProxy),
374 sizes, colorSpace, nv12);
375 }
376
377 sk_sp<GrFragmentProcessor>
MakeRGBToYUV(sk_sp<GrFragmentProcessor> rgbFP,SkYUVColorSpace colorSpace)378 GrYUVEffect::MakeRGBToYUV(sk_sp<GrFragmentProcessor> rgbFP, SkYUVColorSpace colorSpace) {
379 SkASSERT(rgbFP);
380 return sk_sp<GrFragmentProcessor>(
381 new RGBToYUVEffect(std::move(rgbFP), colorSpace, RGBToYUVEffect::kYUV_OutputChannels));
382 }
383
384 sk_sp<GrFragmentProcessor>
MakeRGBToY(sk_sp<GrFragmentProcessor> rgbFP,SkYUVColorSpace colorSpace)385 GrYUVEffect::MakeRGBToY(sk_sp<GrFragmentProcessor> rgbFP, SkYUVColorSpace colorSpace) {
386 SkASSERT(rgbFP);
387 return sk_sp<GrFragmentProcessor>(
388 new RGBToYUVEffect(std::move(rgbFP), colorSpace, RGBToYUVEffect::kY_OutputChannels));
389 }
390
391 sk_sp<GrFragmentProcessor>
MakeRGBToUV(sk_sp<GrFragmentProcessor> rgbFP,SkYUVColorSpace colorSpace)392 GrYUVEffect::MakeRGBToUV(sk_sp<GrFragmentProcessor> rgbFP, SkYUVColorSpace colorSpace) {
393 SkASSERT(rgbFP);
394 return sk_sp<GrFragmentProcessor>(
395 new RGBToYUVEffect(std::move(rgbFP), colorSpace, RGBToYUVEffect::kUV_OutputChannels));
396 }
397
398 sk_sp<GrFragmentProcessor>
MakeRGBToU(sk_sp<GrFragmentProcessor> rgbFP,SkYUVColorSpace colorSpace)399 GrYUVEffect::MakeRGBToU(sk_sp<GrFragmentProcessor> rgbFP, SkYUVColorSpace colorSpace) {
400 SkASSERT(rgbFP);
401 return sk_sp<GrFragmentProcessor>(
402 new RGBToYUVEffect(std::move(rgbFP), colorSpace, RGBToYUVEffect::kU_OutputChannels));
403 }
404
405 sk_sp<GrFragmentProcessor>
MakeRGBToV(sk_sp<GrFragmentProcessor> rgbFP,SkYUVColorSpace colorSpace)406 GrYUVEffect::MakeRGBToV(sk_sp<GrFragmentProcessor> rgbFP, SkYUVColorSpace colorSpace) {
407 SkASSERT(rgbFP);
408 return sk_sp<GrFragmentProcessor>(
409 new RGBToYUVEffect(std::move(rgbFP), colorSpace, RGBToYUVEffect::kV_OutputChannels));
410 }
411