xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrYUVtoRGBEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrYUVtoRGBEffect.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkYUVAInfo.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkSLSampleUsage.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkSLTypeShared.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkYUVAInfoLocation.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkYUVMath.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/KeyBuilder.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxyView.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrYUVATextureProxies.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrTextureEffect.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
32*c8dee2aaSAndroid Build Coastguard Worker #include <array>
33*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
34*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
35*c8dee2aaSAndroid Build Coastguard Worker #include <string>
36*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
37*c8dee2aaSAndroid Build Coastguard Worker 
38*c8dee2aaSAndroid Build Coastguard Worker struct GrShaderCaps;
39*c8dee2aaSAndroid Build Coastguard Worker 
border_colors(const GrYUVATextureProxies & yuvaProxies,float planeBorders[4][4])40*c8dee2aaSAndroid Build Coastguard Worker static void border_colors(const GrYUVATextureProxies& yuvaProxies, float planeBorders[4][4]) {
41*c8dee2aaSAndroid Build Coastguard Worker     float m[20];
42*c8dee2aaSAndroid Build Coastguard Worker     SkColorMatrix_RGB2YUV(yuvaProxies.yuvaInfo().yuvColorSpace(), m);
43*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
44*c8dee2aaSAndroid Build Coastguard Worker         auto [plane, channel] = yuvaProxies.yuvaLocations()[i];
45*c8dee2aaSAndroid Build Coastguard Worker         if (plane == -1) {
46*c8dee2aaSAndroid Build Coastguard Worker             return;
47*c8dee2aaSAndroid Build Coastguard Worker         }
48*c8dee2aaSAndroid Build Coastguard Worker         auto c = static_cast<int>(channel);
49*c8dee2aaSAndroid Build Coastguard Worker         planeBorders[plane][c] = m[i*5 + 4];
50*c8dee2aaSAndroid Build Coastguard Worker     }
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker 
Make(const GrYUVATextureProxies & yuvaProxies,GrSamplerState samplerState,const GrCaps & caps,const SkMatrix & localMatrix,const SkRect * subset,const SkRect * domain)53*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const GrYUVATextureProxies& yuvaProxies,
54*c8dee2aaSAndroid Build Coastguard Worker                                                             GrSamplerState samplerState,
55*c8dee2aaSAndroid Build Coastguard Worker                                                             const GrCaps& caps,
56*c8dee2aaSAndroid Build Coastguard Worker                                                             const SkMatrix& localMatrix,
57*c8dee2aaSAndroid Build Coastguard Worker                                                             const SkRect* subset,
58*c8dee2aaSAndroid Build Coastguard Worker                                                             const SkRect* domain) {
59*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!subset || SkRect::Make(yuvaProxies.yuvaInfo().dimensions()).contains(*subset));
60*c8dee2aaSAndroid Build Coastguard Worker 
61*c8dee2aaSAndroid Build Coastguard Worker     int numPlanes = yuvaProxies.yuvaInfo().numPlanes();
62*c8dee2aaSAndroid Build Coastguard Worker     if (!yuvaProxies.isValid()) {
63*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
64*c8dee2aaSAndroid Build Coastguard Worker     }
65*c8dee2aaSAndroid Build Coastguard Worker 
66*c8dee2aaSAndroid Build Coastguard Worker     bool usesBorder = samplerState.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
67*c8dee2aaSAndroid Build Coastguard Worker                       samplerState.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder;
68*c8dee2aaSAndroid Build Coastguard Worker     float planeBorders[4][4] = {};
69*c8dee2aaSAndroid Build Coastguard Worker     if (usesBorder) {
70*c8dee2aaSAndroid Build Coastguard Worker         border_colors(yuvaProxies, planeBorders);
71*c8dee2aaSAndroid Build Coastguard Worker     }
72*c8dee2aaSAndroid Build Coastguard Worker 
73*c8dee2aaSAndroid Build Coastguard Worker     bool snap[2] = {false, false};
74*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> planeFPs[SkYUVAInfo::kMaxPlanes];
75*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < numPlanes; ++i) {
76*c8dee2aaSAndroid Build Coastguard Worker         bool useSubset = SkToBool(subset);
77*c8dee2aaSAndroid Build Coastguard Worker         GrSurfaceProxyView view = yuvaProxies.makeView(i);
78*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix planeMatrix = yuvaProxies.yuvaInfo().originMatrix();
79*c8dee2aaSAndroid Build Coastguard Worker         // The returned matrix is a view matrix but we need a local matrix.
80*c8dee2aaSAndroid Build Coastguard Worker         SkAssertResult(planeMatrix.invert(&planeMatrix));
81*c8dee2aaSAndroid Build Coastguard Worker         SkRect planeSubset;
82*c8dee2aaSAndroid Build Coastguard Worker         SkRect planeDomain;
83*c8dee2aaSAndroid Build Coastguard Worker         bool makeLinearWithSnap = false;
84*c8dee2aaSAndroid Build Coastguard Worker         auto [ssx, ssy] = yuvaProxies.yuvaInfo().planeSubsamplingFactors(i);
85*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(ssx > 0 && ssx <= 4);
86*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(ssy > 0 && ssy <= 2);
87*c8dee2aaSAndroid Build Coastguard Worker         float scaleX = 1.f;
88*c8dee2aaSAndroid Build Coastguard Worker         float scaleY = 1.f;
89*c8dee2aaSAndroid Build Coastguard Worker         if (ssx > 1 || ssy > 1) {
90*c8dee2aaSAndroid Build Coastguard Worker             scaleX = 1.f/ssx;
91*c8dee2aaSAndroid Build Coastguard Worker             scaleY = 1.f/ssy;
92*c8dee2aaSAndroid Build Coastguard Worker             // We would want to add a translation to this matrix to handle other sitings.
93*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(yuvaProxies.yuvaInfo().sitingX() == SkYUVAInfo::Siting::kCentered);
94*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(yuvaProxies.yuvaInfo().sitingY() == SkYUVAInfo::Siting::kCentered);
95*c8dee2aaSAndroid Build Coastguard Worker             planeMatrix.postConcat(SkMatrix::Scale(scaleX, scaleY));
96*c8dee2aaSAndroid Build Coastguard Worker             if (subset) {
97*c8dee2aaSAndroid Build Coastguard Worker                 planeSubset = {subset->fLeft  *scaleX,
98*c8dee2aaSAndroid Build Coastguard Worker                                subset->fTop   *scaleY,
99*c8dee2aaSAndroid Build Coastguard Worker                                subset->fRight *scaleX,
100*c8dee2aaSAndroid Build Coastguard Worker                                subset->fBottom*scaleY};
101*c8dee2aaSAndroid Build Coastguard Worker             } else {
102*c8dee2aaSAndroid Build Coastguard Worker                 planeSubset = SkRect::Make(view.dimensions());
103*c8dee2aaSAndroid Build Coastguard Worker             }
104*c8dee2aaSAndroid Build Coastguard Worker             if (domain) {
105*c8dee2aaSAndroid Build Coastguard Worker                 planeDomain = {domain->fLeft  *scaleX,
106*c8dee2aaSAndroid Build Coastguard Worker                                domain->fTop   *scaleY,
107*c8dee2aaSAndroid Build Coastguard Worker                                domain->fRight *scaleX,
108*c8dee2aaSAndroid Build Coastguard Worker                                domain->fBottom*scaleY};
109*c8dee2aaSAndroid Build Coastguard Worker             }
110*c8dee2aaSAndroid Build Coastguard Worker             // If the image is not a multiple of the subsampling then the subsampled plane needs to
111*c8dee2aaSAndroid Build Coastguard Worker             // be tiled at less than its full width/height. This only matters when the mode is not
112*c8dee2aaSAndroid Build Coastguard Worker             // clamp.
113*c8dee2aaSAndroid Build Coastguard Worker             if (samplerState.wrapModeX() != GrSamplerState::WrapMode::kClamp) {
114*c8dee2aaSAndroid Build Coastguard Worker                 int dx = (ssx*view.width() - yuvaProxies.yuvaInfo().width());
115*c8dee2aaSAndroid Build Coastguard Worker                 float maxRight = view.width() - dx*scaleX;
116*c8dee2aaSAndroid Build Coastguard Worker                 if (planeSubset.fRight > maxRight) {
117*c8dee2aaSAndroid Build Coastguard Worker                     planeSubset.fRight = maxRight;
118*c8dee2aaSAndroid Build Coastguard Worker                     useSubset = true;
119*c8dee2aaSAndroid Build Coastguard Worker                 }
120*c8dee2aaSAndroid Build Coastguard Worker             }
121*c8dee2aaSAndroid Build Coastguard Worker             if (samplerState.wrapModeY() != GrSamplerState::WrapMode::kClamp) {
122*c8dee2aaSAndroid Build Coastguard Worker                 int dy = (ssy*view.height() - yuvaProxies.yuvaInfo().height());
123*c8dee2aaSAndroid Build Coastguard Worker                 float maxBottom = view.height() - dy*scaleY;
124*c8dee2aaSAndroid Build Coastguard Worker                 if (planeSubset.fBottom > maxBottom) {
125*c8dee2aaSAndroid Build Coastguard Worker                     planeSubset.fBottom = maxBottom;
126*c8dee2aaSAndroid Build Coastguard Worker                     useSubset = true;
127*c8dee2aaSAndroid Build Coastguard Worker                 }
128*c8dee2aaSAndroid Build Coastguard Worker             }
129*c8dee2aaSAndroid Build Coastguard Worker             // This promotion of nearest to linear filtering for UV planes exists to mimic
130*c8dee2aaSAndroid Build Coastguard Worker             // libjpeg[-turbo]'s do_fancy_upsampling option. We will filter the subsampled plane,
131*c8dee2aaSAndroid Build Coastguard Worker             // however we want to filter at a fixed point for each logical image pixel to simulate
132*c8dee2aaSAndroid Build Coastguard Worker             // nearest neighbor.
133*c8dee2aaSAndroid Build Coastguard Worker             if (samplerState.filter() == GrSamplerState::Filter::kNearest) {
134*c8dee2aaSAndroid Build Coastguard Worker                 bool snapX = (ssx != 1),
135*c8dee2aaSAndroid Build Coastguard Worker                      snapY = (ssy != 1);
136*c8dee2aaSAndroid Build Coastguard Worker                 makeLinearWithSnap = snapX || snapY;
137*c8dee2aaSAndroid Build Coastguard Worker                 snap[0] |= snapX;
138*c8dee2aaSAndroid Build Coastguard Worker                 snap[1] |= snapY;
139*c8dee2aaSAndroid Build Coastguard Worker                 if (domain) {
140*c8dee2aaSAndroid Build Coastguard Worker                     // The outer YUVToRGB effect will ensure sampling happens at pixel centers
141*c8dee2aaSAndroid Build Coastguard Worker                     // within this plane.
142*c8dee2aaSAndroid Build Coastguard Worker                     planeDomain = {std::floor(planeDomain.fLeft)   + 0.5f,
143*c8dee2aaSAndroid Build Coastguard Worker                                    std::floor(planeDomain.fTop)    + 0.5f,
144*c8dee2aaSAndroid Build Coastguard Worker                                    std::floor(planeDomain.fRight)  + 0.5f,
145*c8dee2aaSAndroid Build Coastguard Worker                                    std::floor(planeDomain.fBottom) + 0.5f};
146*c8dee2aaSAndroid Build Coastguard Worker                 }
147*c8dee2aaSAndroid Build Coastguard Worker             }
148*c8dee2aaSAndroid Build Coastguard Worker         } else {
149*c8dee2aaSAndroid Build Coastguard Worker             if (subset) {
150*c8dee2aaSAndroid Build Coastguard Worker                 planeSubset = *subset;
151*c8dee2aaSAndroid Build Coastguard Worker             }
152*c8dee2aaSAndroid Build Coastguard Worker             if (domain) {
153*c8dee2aaSAndroid Build Coastguard Worker                 planeDomain = *domain;
154*c8dee2aaSAndroid Build Coastguard Worker             }
155*c8dee2aaSAndroid Build Coastguard Worker         }
156*c8dee2aaSAndroid Build Coastguard Worker         if (useSubset) {
157*c8dee2aaSAndroid Build Coastguard Worker             if (makeLinearWithSnap) {
158*c8dee2aaSAndroid Build Coastguard Worker                 // The plane is subsampled and we have an overall subset on the image. We're
159*c8dee2aaSAndroid Build Coastguard Worker                 // emulating do_fancy_upsampling using linear filtering but snapping look ups to the
160*c8dee2aaSAndroid Build Coastguard Worker                 // y-plane pixel centers. Consider a logical image pixel at the edge of the subset.
161*c8dee2aaSAndroid Build Coastguard Worker                 // When computing the logical pixel color value we should use a 50/50 blend of two
162*c8dee2aaSAndroid Build Coastguard Worker                 // values from the subsampled plane. Depending on where the subset edge falls in
163*c8dee2aaSAndroid Build Coastguard Worker                 // actual subsampled plane, one of those values may come from outside the subset.
164*c8dee2aaSAndroid Build Coastguard Worker                 // Hence, we use this custom inset factory which applies the wrap mode to
165*c8dee2aaSAndroid Build Coastguard Worker                 // planeSubset but allows linear filtering to read pixels from the plane that are
166*c8dee2aaSAndroid Build Coastguard Worker                 // just outside planeSubset.
167*c8dee2aaSAndroid Build Coastguard Worker                 SkRect* domainRect = domain ? &planeDomain : nullptr;
168*c8dee2aaSAndroid Build Coastguard Worker                 planeFPs[i] = GrTextureEffect::MakeCustomLinearFilterInset(std::move(view),
169*c8dee2aaSAndroid Build Coastguard Worker                                                                            kUnknown_SkAlphaType,
170*c8dee2aaSAndroid Build Coastguard Worker                                                                            planeMatrix,
171*c8dee2aaSAndroid Build Coastguard Worker                                                                            samplerState.wrapModeX(),
172*c8dee2aaSAndroid Build Coastguard Worker                                                                            samplerState.wrapModeY(),
173*c8dee2aaSAndroid Build Coastguard Worker                                                                            planeSubset,
174*c8dee2aaSAndroid Build Coastguard Worker                                                                            domainRect,
175*c8dee2aaSAndroid Build Coastguard Worker                                                                            {scaleX/2.f, scaleY/2.f},
176*c8dee2aaSAndroid Build Coastguard Worker                                                                            caps,
177*c8dee2aaSAndroid Build Coastguard Worker                                                                            planeBorders[i]);
178*c8dee2aaSAndroid Build Coastguard Worker             } else if (domain) {
179*c8dee2aaSAndroid Build Coastguard Worker                 planeFPs[i] = GrTextureEffect::MakeSubset(std::move(view),
180*c8dee2aaSAndroid Build Coastguard Worker                                                           kUnknown_SkAlphaType,
181*c8dee2aaSAndroid Build Coastguard Worker                                                           planeMatrix,
182*c8dee2aaSAndroid Build Coastguard Worker                                                           samplerState,
183*c8dee2aaSAndroid Build Coastguard Worker                                                           planeSubset,
184*c8dee2aaSAndroid Build Coastguard Worker                                                           planeDomain,
185*c8dee2aaSAndroid Build Coastguard Worker                                                           caps,
186*c8dee2aaSAndroid Build Coastguard Worker                                                           planeBorders[i]);
187*c8dee2aaSAndroid Build Coastguard Worker             } else {
188*c8dee2aaSAndroid Build Coastguard Worker                 planeFPs[i] = GrTextureEffect::MakeSubset(std::move(view),
189*c8dee2aaSAndroid Build Coastguard Worker                                                           kUnknown_SkAlphaType,
190*c8dee2aaSAndroid Build Coastguard Worker                                                           planeMatrix,
191*c8dee2aaSAndroid Build Coastguard Worker                                                           samplerState,
192*c8dee2aaSAndroid Build Coastguard Worker                                                           planeSubset,
193*c8dee2aaSAndroid Build Coastguard Worker                                                           caps,
194*c8dee2aaSAndroid Build Coastguard Worker                                                           planeBorders[i]);
195*c8dee2aaSAndroid Build Coastguard Worker             }
196*c8dee2aaSAndroid Build Coastguard Worker         } else {
197*c8dee2aaSAndroid Build Coastguard Worker             GrSamplerState planeSampler = samplerState;
198*c8dee2aaSAndroid Build Coastguard Worker             if (makeLinearWithSnap) {
199*c8dee2aaSAndroid Build Coastguard Worker                 planeSampler = GrSamplerState(samplerState.wrapModeX(),
200*c8dee2aaSAndroid Build Coastguard Worker                                               samplerState.wrapModeY(),
201*c8dee2aaSAndroid Build Coastguard Worker                                               GrSamplerState::Filter::kLinear,
202*c8dee2aaSAndroid Build Coastguard Worker                                               samplerState.mipmapMode());
203*c8dee2aaSAndroid Build Coastguard Worker             }
204*c8dee2aaSAndroid Build Coastguard Worker             planeFPs[i] = GrTextureEffect::Make(std::move(view),
205*c8dee2aaSAndroid Build Coastguard Worker                                                 kUnknown_SkAlphaType,
206*c8dee2aaSAndroid Build Coastguard Worker                                                 planeMatrix,
207*c8dee2aaSAndroid Build Coastguard Worker                                                 planeSampler,
208*c8dee2aaSAndroid Build Coastguard Worker                                                 caps,
209*c8dee2aaSAndroid Build Coastguard Worker                                                 planeBorders[i]);
210*c8dee2aaSAndroid Build Coastguard Worker         }
211*c8dee2aaSAndroid Build Coastguard Worker     }
212*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<GrFragmentProcessor> fp(
213*c8dee2aaSAndroid Build Coastguard Worker             new GrYUVtoRGBEffect(planeFPs,
214*c8dee2aaSAndroid Build Coastguard Worker                                  numPlanes,
215*c8dee2aaSAndroid Build Coastguard Worker                                  yuvaProxies.yuvaLocations(),
216*c8dee2aaSAndroid Build Coastguard Worker                                  snap,
217*c8dee2aaSAndroid Build Coastguard Worker                                  yuvaProxies.yuvaInfo().yuvColorSpace()));
218*c8dee2aaSAndroid Build Coastguard Worker     return GrMatrixEffect::Make(localMatrix, std::move(fp));
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker 
alpha_type(const SkYUVAInfo::YUVALocations locations)221*c8dee2aaSAndroid Build Coastguard Worker static SkAlphaType alpha_type(const SkYUVAInfo::YUVALocations locations) {
222*c8dee2aaSAndroid Build Coastguard Worker     return locations[SkYUVAInfo::YUVAChannels::kA].fPlane >= 0 ? kPremul_SkAlphaType
223*c8dee2aaSAndroid Build Coastguard Worker                                                                : kOpaque_SkAlphaType;
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker 
GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4],int numPlanes,const SkYUVAInfo::YUVALocations & locations,const bool snap[2],SkYUVColorSpace yuvColorSpace)226*c8dee2aaSAndroid Build Coastguard Worker GrYUVtoRGBEffect::GrYUVtoRGBEffect(std::unique_ptr<GrFragmentProcessor> planeFPs[4],
227*c8dee2aaSAndroid Build Coastguard Worker                                    int numPlanes,
228*c8dee2aaSAndroid Build Coastguard Worker                                    const SkYUVAInfo::YUVALocations& locations,
229*c8dee2aaSAndroid Build Coastguard Worker                                    const bool snap[2],
230*c8dee2aaSAndroid Build Coastguard Worker                                    SkYUVColorSpace yuvColorSpace)
231*c8dee2aaSAndroid Build Coastguard Worker         : GrFragmentProcessor(kGrYUVtoRGBEffect_ClassID,
232*c8dee2aaSAndroid Build Coastguard Worker                               ModulateForClampedSamplerOptFlags(alpha_type(locations)))
233*c8dee2aaSAndroid Build Coastguard Worker         , fLocations(locations)
234*c8dee2aaSAndroid Build Coastguard Worker         , fYUVColorSpace(yuvColorSpace) {
235*c8dee2aaSAndroid Build Coastguard Worker     std::copy_n(snap, 2, fSnap);
236*c8dee2aaSAndroid Build Coastguard Worker 
237*c8dee2aaSAndroid Build Coastguard Worker     if (fSnap[0] || fSnap[1]) {
238*c8dee2aaSAndroid Build Coastguard Worker         // Need this so that we can access coords in SKSL to perform snapping.
239*c8dee2aaSAndroid Build Coastguard Worker         this->setUsesSampleCoordsDirectly();
240*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < numPlanes; ++i) {
241*c8dee2aaSAndroid Build Coastguard Worker             this->registerChild(std::move(planeFPs[i]), SkSL::SampleUsage::Explicit());
242*c8dee2aaSAndroid Build Coastguard Worker         }
243*c8dee2aaSAndroid Build Coastguard Worker     } else {
244*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < numPlanes; ++i) {
245*c8dee2aaSAndroid Build Coastguard Worker             this->registerChild(std::move(planeFPs[i]));
246*c8dee2aaSAndroid Build Coastguard Worker         }
247*c8dee2aaSAndroid Build Coastguard Worker     }
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker 
250*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
onDumpInfo() const251*c8dee2aaSAndroid Build Coastguard Worker SkString GrYUVtoRGBEffect::onDumpInfo() const {
252*c8dee2aaSAndroid Build Coastguard Worker     SkString str("(");
253*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
254*c8dee2aaSAndroid Build Coastguard Worker         str.appendf("Locations[%d]=%d %d, ",
255*c8dee2aaSAndroid Build Coastguard Worker                     i, fLocations[i].fPlane, static_cast<int>(fLocations[i].fChannel));
256*c8dee2aaSAndroid Build Coastguard Worker     }
257*c8dee2aaSAndroid Build Coastguard Worker     str.appendf("YUVColorSpace=%d, snap=(%d, %d))",
258*c8dee2aaSAndroid Build Coastguard Worker                 static_cast<int>(fYUVColorSpace), fSnap[0], fSnap[1]);
259*c8dee2aaSAndroid Build Coastguard Worker     return str;
260*c8dee2aaSAndroid Build Coastguard Worker }
261*c8dee2aaSAndroid Build Coastguard Worker #endif
262*c8dee2aaSAndroid Build Coastguard Worker 
onMakeProgramImpl() const263*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrYUVtoRGBEffect::onMakeProgramImpl() const {
264*c8dee2aaSAndroid Build Coastguard Worker     class Impl : public ProgramImpl {
265*c8dee2aaSAndroid Build Coastguard Worker     public:
266*c8dee2aaSAndroid Build Coastguard Worker         void emitCode(EmitArgs& args) override {
267*c8dee2aaSAndroid Build Coastguard Worker             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
268*c8dee2aaSAndroid Build Coastguard Worker             const GrYUVtoRGBEffect& yuvEffect = args.fFp.cast<GrYUVtoRGBEffect>();
269*c8dee2aaSAndroid Build Coastguard Worker 
270*c8dee2aaSAndroid Build Coastguard Worker             int numPlanes = yuvEffect.numChildProcessors();
271*c8dee2aaSAndroid Build Coastguard Worker 
272*c8dee2aaSAndroid Build Coastguard Worker             const char* sampleCoords = "";
273*c8dee2aaSAndroid Build Coastguard Worker             if (yuvEffect.fSnap[0] || yuvEffect.fSnap[1]) {
274*c8dee2aaSAndroid Build Coastguard Worker                 fragBuilder->codeAppendf("float2 snappedCoords = %s;", args.fSampleCoord);
275*c8dee2aaSAndroid Build Coastguard Worker                 if (yuvEffect.fSnap[0]) {
276*c8dee2aaSAndroid Build Coastguard Worker                     fragBuilder->codeAppend("snappedCoords.x = floor(snappedCoords.x) + 0.5;");
277*c8dee2aaSAndroid Build Coastguard Worker                 }
278*c8dee2aaSAndroid Build Coastguard Worker                 if (yuvEffect.fSnap[1]) {
279*c8dee2aaSAndroid Build Coastguard Worker                     fragBuilder->codeAppend("snappedCoords.y = floor(snappedCoords.y) + 0.5;");
280*c8dee2aaSAndroid Build Coastguard Worker                 }
281*c8dee2aaSAndroid Build Coastguard Worker                 sampleCoords = "snappedCoords";
282*c8dee2aaSAndroid Build Coastguard Worker             }
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker             fragBuilder->codeAppendf("half4 color;");
285*c8dee2aaSAndroid Build Coastguard Worker             const bool hasAlpha = yuvEffect.fLocations[SkYUVAInfo::YUVAChannels::kA].fPlane >= 0;
286*c8dee2aaSAndroid Build Coastguard Worker 
287*c8dee2aaSAndroid Build Coastguard Worker             for (int planeIdx = 0; planeIdx < numPlanes; ++planeIdx) {
288*c8dee2aaSAndroid Build Coastguard Worker                 std::string colorChannel;
289*c8dee2aaSAndroid Build Coastguard Worker                 std::string planeChannel;
290*c8dee2aaSAndroid Build Coastguard Worker                 for (int locIdx = 0; locIdx < (hasAlpha ? 4 : 3); ++locIdx) {
291*c8dee2aaSAndroid Build Coastguard Worker                     auto [yuvPlane, yuvChannel] = yuvEffect.fLocations[locIdx];
292*c8dee2aaSAndroid Build Coastguard Worker                     if (yuvPlane == planeIdx) {
293*c8dee2aaSAndroid Build Coastguard Worker                         colorChannel.push_back("rgba"[locIdx]);
294*c8dee2aaSAndroid Build Coastguard Worker                         planeChannel.push_back("rgba"[static_cast<int>(yuvChannel)]);
295*c8dee2aaSAndroid Build Coastguard Worker                     }
296*c8dee2aaSAndroid Build Coastguard Worker                 }
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(colorChannel.size() == planeChannel.size());
299*c8dee2aaSAndroid Build Coastguard Worker                 if (!colorChannel.empty()) {
300*c8dee2aaSAndroid Build Coastguard Worker                     fragBuilder->codeAppendf(
301*c8dee2aaSAndroid Build Coastguard Worker                             "color.%s = (%s).%s;",
302*c8dee2aaSAndroid Build Coastguard Worker                             colorChannel.c_str(),
303*c8dee2aaSAndroid Build Coastguard Worker                             this->invokeChild(planeIdx, args, sampleCoords).c_str(),
304*c8dee2aaSAndroid Build Coastguard Worker                             planeChannel.c_str());
305*c8dee2aaSAndroid Build Coastguard Worker                 }
306*c8dee2aaSAndroid Build Coastguard Worker             }
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker             if (!hasAlpha) {
309*c8dee2aaSAndroid Build Coastguard Worker                 fragBuilder->codeAppendf("color.a = 1;");
310*c8dee2aaSAndroid Build Coastguard Worker             }
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker             if (kIdentity_SkYUVColorSpace != yuvEffect.fYUVColorSpace) {
313*c8dee2aaSAndroid Build Coastguard Worker                 fColorSpaceMatrixVar = args.fUniformHandler->addUniform(&yuvEffect,
314*c8dee2aaSAndroid Build Coastguard Worker                         kFragment_GrShaderFlag, SkSLType::kHalf3x3, "colorSpaceMatrix");
315*c8dee2aaSAndroid Build Coastguard Worker                 fColorSpaceTranslateVar = args.fUniformHandler->addUniform(&yuvEffect,
316*c8dee2aaSAndroid Build Coastguard Worker                         kFragment_GrShaderFlag, SkSLType::kHalf3, "colorSpaceTranslate");
317*c8dee2aaSAndroid Build Coastguard Worker                 fragBuilder->codeAppendf(
318*c8dee2aaSAndroid Build Coastguard Worker                         "color.rgb = saturate(color.rgb * %s + %s);",
319*c8dee2aaSAndroid Build Coastguard Worker                         args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar),
320*c8dee2aaSAndroid Build Coastguard Worker                         args.fUniformHandler->getUniformCStr(fColorSpaceTranslateVar));
321*c8dee2aaSAndroid Build Coastguard Worker             }
322*c8dee2aaSAndroid Build Coastguard Worker             if (hasAlpha) {
323*c8dee2aaSAndroid Build Coastguard Worker                 // premultiply alpha
324*c8dee2aaSAndroid Build Coastguard Worker                 fragBuilder->codeAppendf("color.rgb *= color.a;");
325*c8dee2aaSAndroid Build Coastguard Worker             }
326*c8dee2aaSAndroid Build Coastguard Worker             fragBuilder->codeAppendf("return color;");
327*c8dee2aaSAndroid Build Coastguard Worker         }
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker     private:
330*c8dee2aaSAndroid Build Coastguard Worker         void onSetData(const GrGLSLProgramDataManager& pdman,
331*c8dee2aaSAndroid Build Coastguard Worker                        const GrFragmentProcessor& proc) override {
332*c8dee2aaSAndroid Build Coastguard Worker             const GrYUVtoRGBEffect& yuvEffect = proc.cast<GrYUVtoRGBEffect>();
333*c8dee2aaSAndroid Build Coastguard Worker 
334*c8dee2aaSAndroid Build Coastguard Worker             if (yuvEffect.fYUVColorSpace != kIdentity_SkYUVColorSpace) {
335*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(fColorSpaceMatrixVar.isValid());
336*c8dee2aaSAndroid Build Coastguard Worker                 float yuvM[20];
337*c8dee2aaSAndroid Build Coastguard Worker                 SkColorMatrix_YUV2RGB(yuvEffect.fYUVColorSpace, yuvM);
338*c8dee2aaSAndroid Build Coastguard Worker                 // We drop the fourth column entirely since the transformation
339*c8dee2aaSAndroid Build Coastguard Worker                 // should not depend on alpha. The fifth column is sent as a separate
340*c8dee2aaSAndroid Build Coastguard Worker                 // vector. The fourth row is also dropped entirely because alpha should
341*c8dee2aaSAndroid Build Coastguard Worker                 // never be modified.
342*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(yuvM[3] == 0 && yuvM[8] == 0 && yuvM[13] == 0 && yuvM[18] == 1);
343*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(yuvM[15] == 0 && yuvM[16] == 0 && yuvM[17] == 0 && yuvM[19] == 0);
344*c8dee2aaSAndroid Build Coastguard Worker                 float mtx[9] = {
345*c8dee2aaSAndroid Build Coastguard Worker                     yuvM[ 0], yuvM[ 1], yuvM[ 2],
346*c8dee2aaSAndroid Build Coastguard Worker                     yuvM[ 5], yuvM[ 6], yuvM[ 7],
347*c8dee2aaSAndroid Build Coastguard Worker                     yuvM[10], yuvM[11], yuvM[12],
348*c8dee2aaSAndroid Build Coastguard Worker                 };
349*c8dee2aaSAndroid Build Coastguard Worker                 float v[3] = {yuvM[4], yuvM[9], yuvM[14]};
350*c8dee2aaSAndroid Build Coastguard Worker                 pdman.setMatrix3f(fColorSpaceMatrixVar, mtx);
351*c8dee2aaSAndroid Build Coastguard Worker                 pdman.set3fv(fColorSpaceTranslateVar, 1, v);
352*c8dee2aaSAndroid Build Coastguard Worker             }
353*c8dee2aaSAndroid Build Coastguard Worker         }
354*c8dee2aaSAndroid Build Coastguard Worker 
355*c8dee2aaSAndroid Build Coastguard Worker         UniformHandle fColorSpaceMatrixVar;
356*c8dee2aaSAndroid Build Coastguard Worker         UniformHandle fColorSpaceTranslateVar;
357*c8dee2aaSAndroid Build Coastguard Worker     };
358*c8dee2aaSAndroid Build Coastguard Worker 
359*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<Impl>();
360*c8dee2aaSAndroid Build Coastguard Worker }
361*c8dee2aaSAndroid Build Coastguard Worker 
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const362*c8dee2aaSAndroid Build Coastguard Worker void GrYUVtoRGBEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
363*c8dee2aaSAndroid Build Coastguard Worker     uint32_t packed = 0;
364*c8dee2aaSAndroid Build Coastguard Worker     int i = 0;
365*c8dee2aaSAndroid Build Coastguard Worker     for (auto [plane, channel] : fLocations) {
366*c8dee2aaSAndroid Build Coastguard Worker         if (plane < 0) {
367*c8dee2aaSAndroid Build Coastguard Worker             continue;
368*c8dee2aaSAndroid Build Coastguard Worker         }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker         uint8_t chann = static_cast<int>(channel);
371*c8dee2aaSAndroid Build Coastguard Worker 
372*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(plane < 4 && chann < 4);
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker         packed |= (plane | (chann << 2)) << (i++ * 4);
375*c8dee2aaSAndroid Build Coastguard Worker     }
376*c8dee2aaSAndroid Build Coastguard Worker     if (fYUVColorSpace == kIdentity_SkYUVColorSpace) {
377*c8dee2aaSAndroid Build Coastguard Worker         packed |= 1 << 16;
378*c8dee2aaSAndroid Build Coastguard Worker     }
379*c8dee2aaSAndroid Build Coastguard Worker     if (fSnap[0]) {
380*c8dee2aaSAndroid Build Coastguard Worker         packed |= 1 << 17;
381*c8dee2aaSAndroid Build Coastguard Worker     }
382*c8dee2aaSAndroid Build Coastguard Worker     if (fSnap[1]) {
383*c8dee2aaSAndroid Build Coastguard Worker         packed |= 1 << 18;
384*c8dee2aaSAndroid Build Coastguard Worker     }
385*c8dee2aaSAndroid Build Coastguard Worker     b->add32(packed);
386*c8dee2aaSAndroid Build Coastguard Worker }
387*c8dee2aaSAndroid Build Coastguard Worker 
onIsEqual(const GrFragmentProcessor & other) const388*c8dee2aaSAndroid Build Coastguard Worker bool GrYUVtoRGBEffect::onIsEqual(const GrFragmentProcessor& other) const {
389*c8dee2aaSAndroid Build Coastguard Worker     const GrYUVtoRGBEffect& that = other.cast<GrYUVtoRGBEffect>();
390*c8dee2aaSAndroid Build Coastguard Worker 
391*c8dee2aaSAndroid Build Coastguard Worker     return fLocations == that.fLocations            &&
392*c8dee2aaSAndroid Build Coastguard Worker            std::equal(fSnap, fSnap + 2, that.fSnap) &&
393*c8dee2aaSAndroid Build Coastguard Worker            fYUVColorSpace == that.fYUVColorSpace;
394*c8dee2aaSAndroid Build Coastguard Worker }
395*c8dee2aaSAndroid Build Coastguard Worker 
GrYUVtoRGBEffect(const GrYUVtoRGBEffect & src)396*c8dee2aaSAndroid Build Coastguard Worker GrYUVtoRGBEffect::GrYUVtoRGBEffect(const GrYUVtoRGBEffect& src)
397*c8dee2aaSAndroid Build Coastguard Worker         : GrFragmentProcessor(src)
398*c8dee2aaSAndroid Build Coastguard Worker         , fLocations((src.fLocations))
399*c8dee2aaSAndroid Build Coastguard Worker         , fYUVColorSpace(src.fYUVColorSpace) {
400*c8dee2aaSAndroid Build Coastguard Worker     std::copy_n(src.fSnap, 2, fSnap);
401*c8dee2aaSAndroid Build Coastguard Worker }
402*c8dee2aaSAndroid Build Coastguard Worker 
clone() const403*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::clone() const {
404*c8dee2aaSAndroid Build Coastguard Worker     return std::unique_ptr<GrFragmentProcessor>(new GrYUVtoRGBEffect(*this));
405*c8dee2aaSAndroid Build Coastguard Worker }
406