• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 "src/gpu/effects/GrYUVtoRGBEffect.h"
9 
10 #include "include/gpu/GrTexture.h"
11 #include "src/core/SkYUVMath.h"
12 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
13 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
14 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
15 #include "src/sksl/SkSLCPP.h"
16 #include "src/sksl/SkSLUtil.h"
17 
Make(GrSurfaceProxyView views[],const SkYUVAIndex yuvaIndices[4],SkYUVColorSpace yuvColorSpace,GrSamplerState::Filter filterMode,const GrCaps & caps,const SkMatrix & localMatrix,const SkRect * domain)18 std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(GrSurfaceProxyView views[],
19                                                             const SkYUVAIndex yuvaIndices[4],
20                                                             SkYUVColorSpace yuvColorSpace,
21                                                             GrSamplerState::Filter filterMode,
22                                                             const GrCaps& caps,
23                                                             const SkMatrix& localMatrix,
24                                                             const SkRect* domain) {
25     int numPlanes;
26     SkAssertResult(SkYUVAIndex::AreValidIndices(yuvaIndices, &numPlanes));
27 
28     const SkISize YDimensions =
29         views[yuvaIndices[SkYUVAIndex::kY_Index].fIndex].proxy()->dimensions();
30 
31     // This promotion of nearest to bilinear for UV planes exists to mimic libjpeg[-turbo]'s
32     // do_fancy_upsampling option. However, skbug.com/9693.
33     GrSamplerState::Filter subsampledPlaneFilterMode = GrSamplerState::Filter::kMipMap == filterMode
34                                                                ? GrSamplerState::Filter::kMipMap
35                                                                : GrSamplerState::Filter::kBilerp;
36     std::unique_ptr<GrFragmentProcessor> planeFPs[4];
37     for (int i = 0; i < numPlanes; ++i) {
38         SkISize dimensions = views[i].proxy()->dimensions();
39         SkTCopyOnFirstWrite<SkMatrix> planeMatrix(&localMatrix);
40         GrSamplerState::Filter planeFilter = filterMode;
41         SkRect planeDomain;
42         if (dimensions != YDimensions) {
43             // JPEG chroma subsampling of odd dimensions produces U and V planes with the ceiling of
44             // the image size divided by the subsampling factor (2). Our API for creating YUVA
45             // doesn't capture the intended subsampling (and we should fix that). This fixes up 2x
46             // subsampling for images with odd widths/heights (e.g. JPEG 420 or 422).
47             float sx = (float)dimensions.width() / YDimensions.width();
48             float sy = (float)dimensions.height() / YDimensions.height();
49             if ((YDimensions.width() & 0b1) && dimensions.width() == YDimensions.width() / 2 + 1) {
50                 sx = 0.5f;
51             }
52             if ((YDimensions.height() & 0b1) &&
53                 dimensions.height() == YDimensions.height() / 2 + 1) {
54                 sy = 0.5f;
55             }
56             *planeMatrix.writable() = SkMatrix::MakeScale(sx, sy);
57             planeMatrix.writable()->preConcat(localMatrix);
58             planeFilter = subsampledPlaneFilterMode;
59             if (domain) {
60                 planeDomain = {domain->fLeft   * sx,
61                                domain->fTop    * sy,
62                                domain->fRight  * sx,
63                                domain->fBottom * sy};
64             }
65         } else if (domain) {
66             planeDomain = *domain;
67         }
68 
69         if (domain) {
70             SkASSERT(planeFilter != GrSamplerState::Filter::kMipMap);
71             planeFPs[i] = GrTextureEffect::MakeSubset(views[i], kUnknown_SkAlphaType,
72                                                       *planeMatrix, planeFilter, planeDomain, caps);
73         } else {
74             planeFPs[i] = GrTextureEffect::Make(views[i], kUnknown_SkAlphaType, *planeMatrix,
75                                                 planeFilter);
76         }
77     }
78 
79     return std::unique_ptr<GrFragmentProcessor>(
80             new GrYUVtoRGBEffect(planeFPs, numPlanes, yuvaIndices, yuvColorSpace));
81 }
82 
alpha_type(const SkYUVAIndex yuvaIndices[4])83 static SkAlphaType alpha_type(const SkYUVAIndex yuvaIndices[4]) {
84     return yuvaIndices[3].fIndex >= 0 ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
85 }
86 
GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4],int numPlanes,const SkYUVAIndex yuvaIndices[4],SkYUVColorSpace yuvColorSpace)87 GrYUVtoRGBEffect::GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4], int numPlanes,
88                                    const SkYUVAIndex yuvaIndices[4], SkYUVColorSpace yuvColorSpace)
89         : GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID,
90                               ModulateForClampedSamplerOptFlags(alpha_type(yuvaIndices)))
91         , fYUVColorSpace(yuvColorSpace) {
92     for (int i = 0; i < numPlanes; ++i) {
93         this->registerChildProcessor(std::move(planeFPs[i]));
94     }
95     std::copy_n(yuvaIndices, 4, fYUVAIndices);
96 }
97 
98 #ifdef SK_DEBUG
dumpInfo() const99 SkString GrYUVtoRGBEffect::dumpInfo() const {
100     SkString str;
101     for (int i = 0; i < this->numTextureSamplers(); ++i) {
102         str.appendf("%d: %d %d ", i,
103                     this->textureSampler(i).view().proxy()->uniqueID().asUInt(),
104                     this->textureSampler(i).view().proxy()->underlyingUniqueID().asUInt());
105     }
106     str.appendf("\n");
107 
108     return str;
109 }
110 #endif
111 
onCreateGLSLInstance() const112 GrGLSLFragmentProcessor* GrYUVtoRGBEffect::onCreateGLSLInstance() const {
113     class GrGLSLYUVtoRGBEffect : public GrGLSLFragmentProcessor {
114     public:
115         GrGLSLYUVtoRGBEffect() {}
116 
117         void emitCode(EmitArgs& args) override {
118             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
119             const GrYUVtoRGBEffect& yuvEffect = args.fFp.cast<GrYUVtoRGBEffect>();
120 
121             int numPlanes = yuvEffect.numChildProcessors();
122 
123             SkString coords[4];
124             fragBuilder->codeAppendf("half4 planes[%d];", numPlanes);
125             for (int i = 0; i < numPlanes; ++i) {
126                 SkString tempVar = this->invokeChild(i, args);
127                 fragBuilder->codeAppendf("planes[%d] = %s;", i, tempVar.c_str());
128             }
129 
130             bool hasAlpha = yuvEffect.fYUVAIndices[3].fIndex >= 0;
131             SkString rgba[4];
132             rgba[3] = "1";
133             for (int i = 0; i < (hasAlpha ? 4 : 3); ++i) {
134                 auto info = yuvEffect.fYUVAIndices[i];
135                 auto letter = "rgba"[static_cast<int>(info.fChannel)];
136                 rgba[i].printf("planes[%d].%c", info.fIndex, letter);
137             }
138 
139             fragBuilder->codeAppendf("half4 color = half4(%s, %s, %s, %s);",
140                     rgba[0].c_str(), rgba[1].c_str(), rgba[2].c_str(), rgba[3].c_str());
141 
142             if (kIdentity_SkYUVColorSpace != yuvEffect.fYUVColorSpace) {
143                 fColorSpaceMatrixVar = args.fUniformHandler->addUniform(
144                         kFragment_GrShaderFlag, kHalf3x3_GrSLType, "colorSpaceMatrix");
145                 fColorSpaceTranslateVar = args.fUniformHandler->addUniform(
146                         kFragment_GrShaderFlag, kHalf3_GrSLType, "colorSpaceTranslate");
147                 fragBuilder->codeAppendf(
148                         "color.rgb = saturate(color.rgb * %s + %s);",
149                         args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar),
150                         args.fUniformHandler->getUniformCStr(fColorSpaceTranslateVar));
151             }
152 
153             if (hasAlpha) {
154                 // premultiply alpha
155                 fragBuilder->codeAppendf("color.rgb *= color.a;");
156             }
157             fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
158         }
159 
160     private:
161         void onSetData(const GrGLSLProgramDataManager& pdman,
162                        const GrFragmentProcessor& proc) override {
163             const GrYUVtoRGBEffect& yuvEffect = proc.cast<GrYUVtoRGBEffect>();
164 
165             if (yuvEffect.fYUVColorSpace != kIdentity_SkYUVColorSpace) {
166                 SkASSERT(fColorSpaceMatrixVar.isValid());
167                 float yuvM[20];
168                 SkColorMatrix_YUV2RGB(yuvEffect.fYUVColorSpace, yuvM);
169                 // We drop the fourth column entirely since the transformation
170                 // should not depend on alpha. The fifth column is sent as a separate
171                 // vector. The fourth row is also dropped entirely because alpha should
172                 // never be modified.
173                 SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
174                 SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
175                 float mtx[9] = {
176                     yuvM[ 0], yuvM[ 1], yuvM[ 2],
177                     yuvM[ 5], yuvM[ 6], yuvM[ 7],
178                     yuvM[10], yuvM[11], yuvM[12],
179                 };
180                 float v[3] = {yuvM[4], yuvM[9], yuvM[14]};
181                 pdman.setMatrix3f(fColorSpaceMatrixVar, mtx);
182                 pdman.set3fv(fColorSpaceTranslateVar, 1, v);
183             }
184         }
185 
186         UniformHandle fColorSpaceMatrixVar;
187         UniformHandle fColorSpaceTranslateVar;
188     };
189 
190     return new GrGLSLYUVtoRGBEffect;
191 }
onGetGLSLProcessorKey(const GrShaderCaps & caps,GrProcessorKeyBuilder * b) const192 void GrYUVtoRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
193                                              GrProcessorKeyBuilder* b) const {
194     uint32_t packed = 0;
195     for (int i = 0; i < 4; ++i) {
196         if (fYUVAIndices[i].fIndex < 0) {
197             continue;
198         }
199 
200         uint8_t index = fYUVAIndices[i].fIndex;
201         uint8_t chann = static_cast<int>(fYUVAIndices[i].fChannel);
202 
203         SkASSERT(index < 4 && chann < 4);
204 
205         packed |= (index | (chann << 2)) << (i * 4);
206     }
207     if (fYUVColorSpace == kIdentity_SkYUVColorSpace) {
208         packed |= 0x1 << 16;
209     }
210     b->add32(packed);
211 }
212 
onIsEqual(const GrFragmentProcessor & other) const213 bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
214     const GrYUVtoRGBEffect& that = other.cast<GrYUVtoRGBEffect>();
215 
216     for (int i = 0; i < 4; ++i) {
217         if (fYUVAIndices[i] != that.fYUVAIndices[i]) {
218             return false;
219         }
220     }
221 
222     if (fYUVColorSpace != that.fYUVColorSpace) {
223         return false;
224     }
225 
226     return true;
227 }
228 
GrYUVtoRGBEffect(const GrYUVtoRGBEffect & src)229 GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src)
230         : GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID, src.optimizationFlags())
231         , fYUVColorSpace(src.fYUVColorSpace) {
232     int numPlanes = src.numChildProcessors();
233     for (int i = 0; i < numPlanes; ++i) {
234         this->registerChildProcessor(this->childProcessor(i).clone());
235     }
236     std::copy_n(src.fYUVAIndices, this->numChildProcessors(), fYUVAIndices);
237 }
238 
clone() const239 std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::clone() const {
240     return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(*this));
241 }
242