1*89a0ef05SAndroid Build Coastguard Worker /*
2*89a0ef05SAndroid Build Coastguard Worker * Copyright 2024 The Android Open Source Project
3*89a0ef05SAndroid Build Coastguard Worker *
4*89a0ef05SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*89a0ef05SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*89a0ef05SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*89a0ef05SAndroid Build Coastguard Worker *
8*89a0ef05SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*89a0ef05SAndroid Build Coastguard Worker *
10*89a0ef05SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*89a0ef05SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*89a0ef05SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*89a0ef05SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*89a0ef05SAndroid Build Coastguard Worker * limitations under the License.
15*89a0ef05SAndroid Build Coastguard Worker */
16*89a0ef05SAndroid Build Coastguard Worker
17*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/ultrahdrcommon.h"
18*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/gainmapmath.h"
19*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/jpegr.h"
20*89a0ef05SAndroid Build Coastguard Worker
21*89a0ef05SAndroid Build Coastguard Worker namespace ultrahdr {
22*89a0ef05SAndroid Build Coastguard Worker
23*89a0ef05SAndroid Build Coastguard Worker extern const std::string vertex_shader = R"__SHADER__(#version 300 es
24*89a0ef05SAndroid Build Coastguard Worker precision highp float;
25*89a0ef05SAndroid Build Coastguard Worker
26*89a0ef05SAndroid Build Coastguard Worker layout(location = 0) in vec4 aPos;
27*89a0ef05SAndroid Build Coastguard Worker layout(location = 1) in vec2 aTexCoord;
28*89a0ef05SAndroid Build Coastguard Worker
29*89a0ef05SAndroid Build Coastguard Worker out vec2 TexCoord;
30*89a0ef05SAndroid Build Coastguard Worker
31*89a0ef05SAndroid Build Coastguard Worker void main() {
32*89a0ef05SAndroid Build Coastguard Worker gl_Position = aPos;
33*89a0ef05SAndroid Build Coastguard Worker TexCoord = aTexCoord;
34*89a0ef05SAndroid Build Coastguard Worker }
35*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
36*89a0ef05SAndroid Build Coastguard Worker
37*89a0ef05SAndroid Build Coastguard Worker static const std::string getYuv444PixelShader = R"__SHADER__(
38*89a0ef05SAndroid Build Coastguard Worker uniform sampler2D yuvTexture;
39*89a0ef05SAndroid Build Coastguard Worker uniform int pWidth, pHeight;
40*89a0ef05SAndroid Build Coastguard Worker
41*89a0ef05SAndroid Build Coastguard Worker vec3 getYUVPixel() {
42*89a0ef05SAndroid Build Coastguard Worker // Convert texCoord to pixel coordinates
43*89a0ef05SAndroid Build Coastguard Worker ivec2 pixelCoord = ivec2(TexCoord * vec2(pWidth, pHeight));
44*89a0ef05SAndroid Build Coastguard Worker
45*89a0ef05SAndroid Build Coastguard Worker float y = texelFetch(yuvTexture, ivec2(pixelCoord.r, pixelCoord.g), 0).r;
46*89a0ef05SAndroid Build Coastguard Worker float u = texelFetch(yuvTexture, ivec2(pixelCoord.r, pixelCoord.g + pHeight), 0).r;
47*89a0ef05SAndroid Build Coastguard Worker float v = texelFetch(yuvTexture, ivec2(pixelCoord.r, pixelCoord.g + 2 * pHeight), 0).r;
48*89a0ef05SAndroid Build Coastguard Worker
49*89a0ef05SAndroid Build Coastguard Worker return vec3(y, u, v);
50*89a0ef05SAndroid Build Coastguard Worker }
51*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
52*89a0ef05SAndroid Build Coastguard Worker
53*89a0ef05SAndroid Build Coastguard Worker static const std::string getYuv422PixelShader = R"__SHADER__(
54*89a0ef05SAndroid Build Coastguard Worker uniform sampler2D yuvTexture;
55*89a0ef05SAndroid Build Coastguard Worker uniform int pWidth, pHeight;
56*89a0ef05SAndroid Build Coastguard Worker
57*89a0ef05SAndroid Build Coastguard Worker vec3 getYUVPixel() {
58*89a0ef05SAndroid Build Coastguard Worker // Convert texCoord to pixel coordinates
59*89a0ef05SAndroid Build Coastguard Worker ivec2 pixelCoord = ivec2(TexCoord * vec2(pWidth, pHeight));
60*89a0ef05SAndroid Build Coastguard Worker ivec2 uvCoord = ivec2(pixelCoord.r / 2, pixelCoord.g);
61*89a0ef05SAndroid Build Coastguard Worker int uvWidth = pWidth / 2;
62*89a0ef05SAndroid Build Coastguard Worker int uvHeight = pHeight;
63*89a0ef05SAndroid Build Coastguard Worker uint yPlaneSize = uint(pWidth) * uint(pHeight);
64*89a0ef05SAndroid Build Coastguard Worker uint uPlaneSize = uint(uvWidth) * uint(uvHeight);
65*89a0ef05SAndroid Build Coastguard Worker uint yIndex = uint(pixelCoord.g * pWidth + pixelCoord.r);
66*89a0ef05SAndroid Build Coastguard Worker uint uIndex = yPlaneSize + uint(uvCoord.g * uvWidth + uvCoord.r);
67*89a0ef05SAndroid Build Coastguard Worker uint vIndex = yPlaneSize + uPlaneSize + uint(uvCoord.g * uvWidth + uvCoord.r);
68*89a0ef05SAndroid Build Coastguard Worker
69*89a0ef05SAndroid Build Coastguard Worker float y = texelFetch(yuvTexture, ivec2(yIndex % uint(pWidth), yIndex / uint(pWidth)), 0).r;
70*89a0ef05SAndroid Build Coastguard Worker float u = texelFetch(yuvTexture, ivec2(uIndex % uint(pWidth), uIndex / uint(pWidth)), 0).r;
71*89a0ef05SAndroid Build Coastguard Worker float v = texelFetch(yuvTexture, ivec2(vIndex % uint(pWidth), vIndex / uint(pWidth)), 0).r;
72*89a0ef05SAndroid Build Coastguard Worker
73*89a0ef05SAndroid Build Coastguard Worker return vec3(y, u, v);
74*89a0ef05SAndroid Build Coastguard Worker }
75*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
76*89a0ef05SAndroid Build Coastguard Worker
77*89a0ef05SAndroid Build Coastguard Worker static const std::string getYuv420PixelShader = R"__SHADER__(
78*89a0ef05SAndroid Build Coastguard Worker uniform sampler2D yuvTexture;
79*89a0ef05SAndroid Build Coastguard Worker uniform int pWidth, pHeight;
80*89a0ef05SAndroid Build Coastguard Worker
81*89a0ef05SAndroid Build Coastguard Worker vec3 getYUVPixel() {
82*89a0ef05SAndroid Build Coastguard Worker // Convert texCoord to pixel coordinates
83*89a0ef05SAndroid Build Coastguard Worker ivec2 pixelCoord = ivec2(TexCoord * vec2(pWidth, pHeight));
84*89a0ef05SAndroid Build Coastguard Worker ivec2 uvCoord = pixelCoord / 2;
85*89a0ef05SAndroid Build Coastguard Worker int uvWidth = pWidth / 2;
86*89a0ef05SAndroid Build Coastguard Worker int uvHeight = pHeight / 2;
87*89a0ef05SAndroid Build Coastguard Worker uint yPlaneSize = uint(pWidth) * uint(pHeight);
88*89a0ef05SAndroid Build Coastguard Worker uint uPlaneSize = uint(uvWidth) * uint(uvHeight);
89*89a0ef05SAndroid Build Coastguard Worker uint yIndex = uint(pixelCoord.g * pWidth + pixelCoord.r);
90*89a0ef05SAndroid Build Coastguard Worker uint uIndex = yPlaneSize + uint(uvCoord.g * uvWidth + uvCoord.r);
91*89a0ef05SAndroid Build Coastguard Worker uint vIndex = yPlaneSize + uPlaneSize + uint(uvCoord.g * uvWidth + uvCoord.r);
92*89a0ef05SAndroid Build Coastguard Worker
93*89a0ef05SAndroid Build Coastguard Worker float y = texelFetch(yuvTexture, ivec2(yIndex % uint(pWidth), yIndex / uint(pWidth)), 0).r;
94*89a0ef05SAndroid Build Coastguard Worker float u = texelFetch(yuvTexture, ivec2(uIndex % uint(pWidth), uIndex / uint(pWidth)), 0).r;
95*89a0ef05SAndroid Build Coastguard Worker float v = texelFetch(yuvTexture, ivec2(vIndex % uint(pWidth), vIndex / uint(pWidth)), 0).r;
96*89a0ef05SAndroid Build Coastguard Worker
97*89a0ef05SAndroid Build Coastguard Worker return vec3(y, u, v);
98*89a0ef05SAndroid Build Coastguard Worker }
99*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
100*89a0ef05SAndroid Build Coastguard Worker
101*89a0ef05SAndroid Build Coastguard Worker static const std::string p3YUVToRGBShader = R"__SHADER__(
102*89a0ef05SAndroid Build Coastguard Worker vec3 p3YuvToRgb(const vec3 color) {
103*89a0ef05SAndroid Build Coastguard Worker const vec3 offset = vec3(0.0, 128.0f / 255.0f, 128.0f / 255.0f);
104*89a0ef05SAndroid Build Coastguard Worker const mat3 transform = mat3(
105*89a0ef05SAndroid Build Coastguard Worker 1.0, 1.0, 1.0,
106*89a0ef05SAndroid Build Coastguard Worker 0.0, -0.344136286, 1.772,
107*89a0ef05SAndroid Build Coastguard Worker 1.402, -0.714136286, 0.0);
108*89a0ef05SAndroid Build Coastguard Worker return clamp(transform * (color - offset), 0.0, 1.0);
109*89a0ef05SAndroid Build Coastguard Worker }
110*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
111*89a0ef05SAndroid Build Coastguard Worker
112*89a0ef05SAndroid Build Coastguard Worker static const std::string sRGBEOTFShader = R"__SHADER__(
113*89a0ef05SAndroid Build Coastguard Worker float sRGBEOTF(float e_gamma) {
114*89a0ef05SAndroid Build Coastguard Worker return e_gamma <= 0.04045 ? e_gamma / 12.92 : pow((e_gamma + 0.055) / 1.055, 2.4);
115*89a0ef05SAndroid Build Coastguard Worker }
116*89a0ef05SAndroid Build Coastguard Worker
117*89a0ef05SAndroid Build Coastguard Worker vec3 sRGBEOTF(const vec3 e_gamma) {
118*89a0ef05SAndroid Build Coastguard Worker return vec3(sRGBEOTF(e_gamma.r), sRGBEOTF(e_gamma.g), sRGBEOTF(e_gamma.b));
119*89a0ef05SAndroid Build Coastguard Worker }
120*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
121*89a0ef05SAndroid Build Coastguard Worker
122*89a0ef05SAndroid Build Coastguard Worker static const std::string getGainMapSampleSingleChannel = R"__SHADER__(
123*89a0ef05SAndroid Build Coastguard Worker uniform sampler2D gainMapTexture;
124*89a0ef05SAndroid Build Coastguard Worker
125*89a0ef05SAndroid Build Coastguard Worker vec3 sampleMap(sampler2D map) { return vec3(texture(map, TexCoord).r); }
126*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
127*89a0ef05SAndroid Build Coastguard Worker
128*89a0ef05SAndroid Build Coastguard Worker static const std::string getGainMapSampleMultiChannel = R"__SHADER__(
129*89a0ef05SAndroid Build Coastguard Worker uniform sampler2D gainMapTexture;
130*89a0ef05SAndroid Build Coastguard Worker
131*89a0ef05SAndroid Build Coastguard Worker vec3 sampleMap(sampler2D map) { return texture(map, TexCoord).rgb; }
132*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
133*89a0ef05SAndroid Build Coastguard Worker
134*89a0ef05SAndroid Build Coastguard Worker static const std::string applyGainMapShader = R"__SHADER__(
135*89a0ef05SAndroid Build Coastguard Worker uniform float gamma;
136*89a0ef05SAndroid Build Coastguard Worker uniform float logMinBoost;
137*89a0ef05SAndroid Build Coastguard Worker uniform float logMaxBoost;
138*89a0ef05SAndroid Build Coastguard Worker uniform float weight;
139*89a0ef05SAndroid Build Coastguard Worker uniform float offsetSdr;
140*89a0ef05SAndroid Build Coastguard Worker uniform float offsetHdr;
141*89a0ef05SAndroid Build Coastguard Worker uniform float normalize;
142*89a0ef05SAndroid Build Coastguard Worker
143*89a0ef05SAndroid Build Coastguard Worker float applyGainMapSample(const float channel, float gain) {
144*89a0ef05SAndroid Build Coastguard Worker gain = pow(gain, 1.0f / gamma);
145*89a0ef05SAndroid Build Coastguard Worker float logBoost = logMinBoost * (1.0f - gain) + logMaxBoost * gain;
146*89a0ef05SAndroid Build Coastguard Worker logBoost = exp2(logBoost * weight);
147*89a0ef05SAndroid Build Coastguard Worker return ((channel + offsetSdr) * logBoost - offsetHdr) / normalize;
148*89a0ef05SAndroid Build Coastguard Worker }
149*89a0ef05SAndroid Build Coastguard Worker
150*89a0ef05SAndroid Build Coastguard Worker vec3 applyGain(const vec3 color, const vec3 gain) {
151*89a0ef05SAndroid Build Coastguard Worker return vec3(applyGainMapSample(color.r, gain.r),
152*89a0ef05SAndroid Build Coastguard Worker applyGainMapSample(color.g, gain.g),
153*89a0ef05SAndroid Build Coastguard Worker applyGainMapSample(color.b, gain.b));
154*89a0ef05SAndroid Build Coastguard Worker }
155*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
156*89a0ef05SAndroid Build Coastguard Worker
157*89a0ef05SAndroid Build Coastguard Worker static const std::string linearOETFShader = R"__SHADER__(
158*89a0ef05SAndroid Build Coastguard Worker vec3 OETF(const vec3 linear) { return linear; }
159*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
160*89a0ef05SAndroid Build Coastguard Worker
161*89a0ef05SAndroid Build Coastguard Worker static const std::string hlgOETFShader = R"__SHADER__(
162*89a0ef05SAndroid Build Coastguard Worker float OETF(const float linear) {
163*89a0ef05SAndroid Build Coastguard Worker const float kHlgA = 0.17883277;
164*89a0ef05SAndroid Build Coastguard Worker const float kHlgB = 0.28466892;
165*89a0ef05SAndroid Build Coastguard Worker const float kHlgC = 0.55991073;
166*89a0ef05SAndroid Build Coastguard Worker return linear <= 1.0 / 12.0 ? sqrt(3.0 * linear) : kHlgA * log(12.0 * linear - kHlgB) + kHlgC;
167*89a0ef05SAndroid Build Coastguard Worker }
168*89a0ef05SAndroid Build Coastguard Worker
169*89a0ef05SAndroid Build Coastguard Worker vec3 OETF(const vec3 linear) {
170*89a0ef05SAndroid Build Coastguard Worker return vec3(OETF(linear.r), OETF(linear.g), OETF(linear.b));
171*89a0ef05SAndroid Build Coastguard Worker }
172*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
173*89a0ef05SAndroid Build Coastguard Worker
174*89a0ef05SAndroid Build Coastguard Worker static const std::string pqOETFShader = R"__SHADER__(
175*89a0ef05SAndroid Build Coastguard Worker vec3 OETF(const vec3 linear) {
176*89a0ef05SAndroid Build Coastguard Worker const float kPqM1 = (2610.0 / 4096.0) / 4.0;
177*89a0ef05SAndroid Build Coastguard Worker const float kPqM2 = (2523.0 / 4096.0) * 128.0;
178*89a0ef05SAndroid Build Coastguard Worker const float kPqC1 = (3424.0 / 4096.0);
179*89a0ef05SAndroid Build Coastguard Worker const float kPqC2 = (2413.0 / 4096.0) * 32.0;
180*89a0ef05SAndroid Build Coastguard Worker const float kPqC3 = (2392.0 / 4096.0) * 32.0;
181*89a0ef05SAndroid Build Coastguard Worker vec3 tmp = pow(linear, vec3(kPqM1));
182*89a0ef05SAndroid Build Coastguard Worker tmp = (kPqC1 + kPqC2 * tmp) / (1.0 + kPqC3 * tmp);
183*89a0ef05SAndroid Build Coastguard Worker return pow(tmp, vec3(kPqM2));
184*89a0ef05SAndroid Build Coastguard Worker }
185*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
186*89a0ef05SAndroid Build Coastguard Worker
187*89a0ef05SAndroid Build Coastguard Worker static const std::string hlgInverseOOTFShader = R"__SHADER__(
188*89a0ef05SAndroid Build Coastguard Worker float InverseOOTF(const float linear) {
189*89a0ef05SAndroid Build Coastguard Worker const float kOotfGamma = 1.2f;
190*89a0ef05SAndroid Build Coastguard Worker return pow(linear, 1.0f / kOotfGamma);
191*89a0ef05SAndroid Build Coastguard Worker }
192*89a0ef05SAndroid Build Coastguard Worker
193*89a0ef05SAndroid Build Coastguard Worker vec3 InverseOOTF(const vec3 linear) {
194*89a0ef05SAndroid Build Coastguard Worker return vec3(InverseOOTF(linear.r), InverseOOTF(linear.g), InverseOOTF(linear.b));
195*89a0ef05SAndroid Build Coastguard Worker }
196*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
197*89a0ef05SAndroid Build Coastguard Worker
198*89a0ef05SAndroid Build Coastguard Worker static const std::string IdentityInverseOOTFShader = R"__SHADER__(
199*89a0ef05SAndroid Build Coastguard Worker vec3 InverseOOTF(const vec3 linear) { return linear; }
200*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
201*89a0ef05SAndroid Build Coastguard Worker
getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt,uhdr_img_fmt gm_fmt,uhdr_color_transfer output_ct)202*89a0ef05SAndroid Build Coastguard Worker std::string getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt, uhdr_img_fmt gm_fmt,
203*89a0ef05SAndroid Build Coastguard Worker uhdr_color_transfer output_ct) {
204*89a0ef05SAndroid Build Coastguard Worker std::string shader_code = R"__SHADER__(#version 300 es
205*89a0ef05SAndroid Build Coastguard Worker precision highp float;
206*89a0ef05SAndroid Build Coastguard Worker precision highp int;
207*89a0ef05SAndroid Build Coastguard Worker
208*89a0ef05SAndroid Build Coastguard Worker out vec4 FragColor;
209*89a0ef05SAndroid Build Coastguard Worker in vec2 TexCoord;
210*89a0ef05SAndroid Build Coastguard Worker )__SHADER__";
211*89a0ef05SAndroid Build Coastguard Worker
212*89a0ef05SAndroid Build Coastguard Worker if (sdr_fmt == UHDR_IMG_FMT_24bppYCbCr444) {
213*89a0ef05SAndroid Build Coastguard Worker shader_code.append(getYuv444PixelShader);
214*89a0ef05SAndroid Build Coastguard Worker } else if (sdr_fmt == UHDR_IMG_FMT_16bppYCbCr422) {
215*89a0ef05SAndroid Build Coastguard Worker shader_code.append(getYuv422PixelShader);
216*89a0ef05SAndroid Build Coastguard Worker } else if (sdr_fmt == UHDR_IMG_FMT_12bppYCbCr420) {
217*89a0ef05SAndroid Build Coastguard Worker shader_code.append(getYuv420PixelShader);
218*89a0ef05SAndroid Build Coastguard Worker }
219*89a0ef05SAndroid Build Coastguard Worker shader_code.append(p3YUVToRGBShader);
220*89a0ef05SAndroid Build Coastguard Worker shader_code.append(sRGBEOTFShader);
221*89a0ef05SAndroid Build Coastguard Worker shader_code.append(gm_fmt == UHDR_IMG_FMT_8bppYCbCr400 ? getGainMapSampleSingleChannel
222*89a0ef05SAndroid Build Coastguard Worker : getGainMapSampleMultiChannel);
223*89a0ef05SAndroid Build Coastguard Worker shader_code.append(applyGainMapShader);
224*89a0ef05SAndroid Build Coastguard Worker if (output_ct == UHDR_CT_LINEAR) {
225*89a0ef05SAndroid Build Coastguard Worker shader_code.append(IdentityInverseOOTFShader);
226*89a0ef05SAndroid Build Coastguard Worker shader_code.append(linearOETFShader);
227*89a0ef05SAndroid Build Coastguard Worker } else if (output_ct == UHDR_CT_HLG) {
228*89a0ef05SAndroid Build Coastguard Worker shader_code.append(hlgInverseOOTFShader);
229*89a0ef05SAndroid Build Coastguard Worker shader_code.append(hlgOETFShader);
230*89a0ef05SAndroid Build Coastguard Worker } else if (output_ct == UHDR_CT_PQ) {
231*89a0ef05SAndroid Build Coastguard Worker shader_code.append(IdentityInverseOOTFShader);
232*89a0ef05SAndroid Build Coastguard Worker shader_code.append(pqOETFShader);
233*89a0ef05SAndroid Build Coastguard Worker }
234*89a0ef05SAndroid Build Coastguard Worker
235*89a0ef05SAndroid Build Coastguard Worker shader_code.append(R"__SHADER__(
236*89a0ef05SAndroid Build Coastguard Worker void main() {
237*89a0ef05SAndroid Build Coastguard Worker vec3 yuv_gamma_sdr = getYUVPixel();
238*89a0ef05SAndroid Build Coastguard Worker vec3 rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
239*89a0ef05SAndroid Build Coastguard Worker vec3 rgb_sdr = sRGBEOTF(rgb_gamma_sdr);
240*89a0ef05SAndroid Build Coastguard Worker vec3 gain = sampleMap(gainMapTexture);
241*89a0ef05SAndroid Build Coastguard Worker vec3 rgb_hdr = applyGain(rgb_sdr, gain);
242*89a0ef05SAndroid Build Coastguard Worker rgb_hdr = InverseOOTF(rgb_hdr);
243*89a0ef05SAndroid Build Coastguard Worker vec3 rgb_gamma_hdr = OETF(rgb_hdr);
244*89a0ef05SAndroid Build Coastguard Worker FragColor = vec4(rgb_gamma_hdr, 1.0);
245*89a0ef05SAndroid Build Coastguard Worker }
246*89a0ef05SAndroid Build Coastguard Worker )__SHADER__");
247*89a0ef05SAndroid Build Coastguard Worker return shader_code;
248*89a0ef05SAndroid Build Coastguard Worker }
249*89a0ef05SAndroid Build Coastguard Worker
isBufferDataContiguous(uhdr_raw_image_t * img)250*89a0ef05SAndroid Build Coastguard Worker bool isBufferDataContiguous(uhdr_raw_image_t* img) {
251*89a0ef05SAndroid Build Coastguard Worker if (img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || img->fmt == UHDR_IMG_FMT_24bppRGB888 ||
252*89a0ef05SAndroid Build Coastguard Worker img->fmt == UHDR_IMG_FMT_8bppYCbCr400 || img->fmt == UHDR_IMG_FMT_32bppRGBA1010102 ||
253*89a0ef05SAndroid Build Coastguard Worker img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
254*89a0ef05SAndroid Build Coastguard Worker return img->stride[UHDR_PLANE_PACKED] == img->w;
255*89a0ef05SAndroid Build Coastguard Worker } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
256*89a0ef05SAndroid Build Coastguard Worker uint16_t* y = static_cast<uint16_t*>(img->planes[UHDR_PLANE_Y]);
257*89a0ef05SAndroid Build Coastguard Worker uint16_t* u = static_cast<uint16_t*>(img->planes[UHDR_PLANE_UV]);
258*89a0ef05SAndroid Build Coastguard Worker std::ptrdiff_t sz = u - y;
259*89a0ef05SAndroid Build Coastguard Worker long pixels = img->w * img->h;
260*89a0ef05SAndroid Build Coastguard Worker return img->stride[UHDR_PLANE_Y] == img->w && img->stride[UHDR_PLANE_UV] == img->w &&
261*89a0ef05SAndroid Build Coastguard Worker sz == pixels;
262*89a0ef05SAndroid Build Coastguard Worker } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420 || img->fmt == UHDR_IMG_FMT_24bppYCbCr444 ||
263*89a0ef05SAndroid Build Coastguard Worker img->fmt == UHDR_IMG_FMT_16bppYCbCr422) {
264*89a0ef05SAndroid Build Coastguard Worker int h_samp_factor = img->fmt == UHDR_IMG_FMT_24bppYCbCr444 ? 1 : 2;
265*89a0ef05SAndroid Build Coastguard Worker int v_samp_factor = img->fmt == UHDR_IMG_FMT_12bppYCbCr420 ? 2 : 1;
266*89a0ef05SAndroid Build Coastguard Worker uint8_t* y = static_cast<uint8_t*>(img->planes[UHDR_PLANE_Y]);
267*89a0ef05SAndroid Build Coastguard Worker uint8_t* u = static_cast<uint8_t*>(img->planes[UHDR_PLANE_U]);
268*89a0ef05SAndroid Build Coastguard Worker uint8_t* v = static_cast<uint8_t*>(img->planes[UHDR_PLANE_V]);
269*89a0ef05SAndroid Build Coastguard Worker std::ptrdiff_t sz_a = u - y, sz_b = v - u;
270*89a0ef05SAndroid Build Coastguard Worker long pixels = img->w * img->h;
271*89a0ef05SAndroid Build Coastguard Worker return img->stride[UHDR_PLANE_Y] == img->w &&
272*89a0ef05SAndroid Build Coastguard Worker img->stride[UHDR_PLANE_U] == img->w / h_samp_factor &&
273*89a0ef05SAndroid Build Coastguard Worker img->stride[UHDR_PLANE_V] == img->w / h_samp_factor && sz_a == pixels &&
274*89a0ef05SAndroid Build Coastguard Worker sz_b == pixels / (h_samp_factor * v_samp_factor);
275*89a0ef05SAndroid Build Coastguard Worker }
276*89a0ef05SAndroid Build Coastguard Worker return false;
277*89a0ef05SAndroid Build Coastguard Worker }
278*89a0ef05SAndroid Build Coastguard Worker
applyGainMapGLES(uhdr_raw_image_t * sdr_intent,uhdr_raw_image_t * gainmap_img,uhdr_gainmap_metadata_ext_t * gainmap_metadata,uhdr_color_transfer_t output_ct,float display_boost,uhdr_raw_image_t * dest,uhdr_opengl_ctxt_t * opengl_ctxt)279*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_t* gainmap_img,
280*89a0ef05SAndroid Build Coastguard Worker uhdr_gainmap_metadata_ext_t* gainmap_metadata,
281*89a0ef05SAndroid Build Coastguard Worker uhdr_color_transfer_t output_ct, float display_boost,
282*89a0ef05SAndroid Build Coastguard Worker uhdr_raw_image_t* dest, uhdr_opengl_ctxt_t* opengl_ctxt) {
283*89a0ef05SAndroid Build Coastguard Worker GLuint shaderProgram = 0; // shader program
284*89a0ef05SAndroid Build Coastguard Worker GLuint yuvTexture = 0; // sdr intent texture
285*89a0ef05SAndroid Build Coastguard Worker GLuint frameBuffer = 0;
286*89a0ef05SAndroid Build Coastguard Worker
287*89a0ef05SAndroid Build Coastguard Worker #define RET_IF_ERR() \
288*89a0ef05SAndroid Build Coastguard Worker if (opengl_ctxt->mErrorStatus.error_code != UHDR_CODEC_OK) { \
289*89a0ef05SAndroid Build Coastguard Worker if (frameBuffer) glDeleteFramebuffers(1, &frameBuffer); \
290*89a0ef05SAndroid Build Coastguard Worker if (yuvTexture) glDeleteTextures(1, &yuvTexture); \
291*89a0ef05SAndroid Build Coastguard Worker if (shaderProgram) glDeleteProgram(shaderProgram); \
292*89a0ef05SAndroid Build Coastguard Worker return opengl_ctxt->mErrorStatus; \
293*89a0ef05SAndroid Build Coastguard Worker }
294*89a0ef05SAndroid Build Coastguard Worker
295*89a0ef05SAndroid Build Coastguard Worker shaderProgram = opengl_ctxt->create_shader_program(
296*89a0ef05SAndroid Build Coastguard Worker vertex_shader.c_str(),
297*89a0ef05SAndroid Build Coastguard Worker getApplyGainMapFragmentShader(sdr_intent->fmt, gainmap_img->fmt, output_ct).c_str());
298*89a0ef05SAndroid Build Coastguard Worker RET_IF_ERR()
299*89a0ef05SAndroid Build Coastguard Worker
300*89a0ef05SAndroid Build Coastguard Worker yuvTexture = opengl_ctxt->create_texture(sdr_intent->fmt, sdr_intent->w, sdr_intent->h,
301*89a0ef05SAndroid Build Coastguard Worker sdr_intent->planes[0]);
302*89a0ef05SAndroid Build Coastguard Worker opengl_ctxt->mGainmapImgTexture = opengl_ctxt->create_texture(
303*89a0ef05SAndroid Build Coastguard Worker gainmap_img->fmt, gainmap_img->w, gainmap_img->h, gainmap_img->planes[0]);
304*89a0ef05SAndroid Build Coastguard Worker opengl_ctxt->mDecodedImgTexture = opengl_ctxt->create_texture(
305*89a0ef05SAndroid Build Coastguard Worker output_ct == UHDR_CT_LINEAR ? UHDR_IMG_FMT_64bppRGBAHalfFloat : UHDR_IMG_FMT_32bppRGBA1010102,
306*89a0ef05SAndroid Build Coastguard Worker sdr_intent->w, sdr_intent->h, nullptr);
307*89a0ef05SAndroid Build Coastguard Worker RET_IF_ERR()
308*89a0ef05SAndroid Build Coastguard Worker
309*89a0ef05SAndroid Build Coastguard Worker frameBuffer = opengl_ctxt->setup_framebuffer(opengl_ctxt->mDecodedImgTexture);
310*89a0ef05SAndroid Build Coastguard Worker RET_IF_ERR()
311*89a0ef05SAndroid Build Coastguard Worker
312*89a0ef05SAndroid Build Coastguard Worker glViewport(0, 0, sdr_intent->w, sdr_intent->h);
313*89a0ef05SAndroid Build Coastguard Worker glUseProgram(shaderProgram);
314*89a0ef05SAndroid Build Coastguard Worker
315*89a0ef05SAndroid Build Coastguard Worker // Get the location of the uniform variables
316*89a0ef05SAndroid Build Coastguard Worker GLint pWidthLocation = glGetUniformLocation(shaderProgram, "pWidth");
317*89a0ef05SAndroid Build Coastguard Worker GLint pHeightLocation = glGetUniformLocation(shaderProgram, "pHeight");
318*89a0ef05SAndroid Build Coastguard Worker GLint gammaLocation = glGetUniformLocation(shaderProgram, "gamma");
319*89a0ef05SAndroid Build Coastguard Worker GLint logMinBoostLocation = glGetUniformLocation(shaderProgram, "logMinBoost");
320*89a0ef05SAndroid Build Coastguard Worker GLint logMaxBoostLocation = glGetUniformLocation(shaderProgram, "logMaxBoost");
321*89a0ef05SAndroid Build Coastguard Worker GLint weightLocation = glGetUniformLocation(shaderProgram, "weight");
322*89a0ef05SAndroid Build Coastguard Worker GLint offsetSdrLocation = glGetUniformLocation(shaderProgram, "offsetSdr");
323*89a0ef05SAndroid Build Coastguard Worker GLint offsetHdrLocation = glGetUniformLocation(shaderProgram, "offsetHdr");
324*89a0ef05SAndroid Build Coastguard Worker GLint normalizeLocation = glGetUniformLocation(shaderProgram, "normalize");
325*89a0ef05SAndroid Build Coastguard Worker
326*89a0ef05SAndroid Build Coastguard Worker glUniform1i(pWidthLocation, sdr_intent->w);
327*89a0ef05SAndroid Build Coastguard Worker glUniform1i(pHeightLocation, sdr_intent->h);
328*89a0ef05SAndroid Build Coastguard Worker glUniform1f(gammaLocation, gainmap_metadata->gamma);
329*89a0ef05SAndroid Build Coastguard Worker glUniform1f(logMinBoostLocation, log2(gainmap_metadata->min_content_boost));
330*89a0ef05SAndroid Build Coastguard Worker glUniform1f(logMaxBoostLocation, log2(gainmap_metadata->max_content_boost));
331*89a0ef05SAndroid Build Coastguard Worker glUniform1f(offsetSdrLocation, gainmap_metadata->offset_sdr);
332*89a0ef05SAndroid Build Coastguard Worker glUniform1f(offsetHdrLocation, gainmap_metadata->offset_hdr);
333*89a0ef05SAndroid Build Coastguard Worker float gainmap_weight;
334*89a0ef05SAndroid Build Coastguard Worker if (display_boost != gainmap_metadata->hdr_capacity_max) {
335*89a0ef05SAndroid Build Coastguard Worker gainmap_weight =
336*89a0ef05SAndroid Build Coastguard Worker (log2(display_boost) - log2(gainmap_metadata->hdr_capacity_min)) /
337*89a0ef05SAndroid Build Coastguard Worker (log2(gainmap_metadata->hdr_capacity_max) - log2(gainmap_metadata->hdr_capacity_min));
338*89a0ef05SAndroid Build Coastguard Worker // avoid extrapolating the gain map to fill the displayable range
339*89a0ef05SAndroid Build Coastguard Worker gainmap_weight = CLIP3(0.0f, gainmap_weight, 1.0f);
340*89a0ef05SAndroid Build Coastguard Worker } else {
341*89a0ef05SAndroid Build Coastguard Worker gainmap_weight = 1.0f;
342*89a0ef05SAndroid Build Coastguard Worker }
343*89a0ef05SAndroid Build Coastguard Worker glUniform1f(weightLocation, gainmap_weight);
344*89a0ef05SAndroid Build Coastguard Worker float normalize = 1.0f;
345*89a0ef05SAndroid Build Coastguard Worker if (output_ct == UHDR_CT_HLG) normalize = kHlgMaxNits / kSdrWhiteNits;
346*89a0ef05SAndroid Build Coastguard Worker else if (output_ct == UHDR_CT_PQ) normalize = kPqMaxNits / kSdrWhiteNits;
347*89a0ef05SAndroid Build Coastguard Worker glUniform1f(normalizeLocation, normalize);
348*89a0ef05SAndroid Build Coastguard Worker
349*89a0ef05SAndroid Build Coastguard Worker glActiveTexture(GL_TEXTURE0);
350*89a0ef05SAndroid Build Coastguard Worker glBindTexture(GL_TEXTURE_2D, yuvTexture);
351*89a0ef05SAndroid Build Coastguard Worker glUniform1i(glGetUniformLocation(shaderProgram, "yuvTexture"), 0);
352*89a0ef05SAndroid Build Coastguard Worker
353*89a0ef05SAndroid Build Coastguard Worker glActiveTexture(GL_TEXTURE1);
354*89a0ef05SAndroid Build Coastguard Worker glBindTexture(GL_TEXTURE_2D, opengl_ctxt->mGainmapImgTexture);
355*89a0ef05SAndroid Build Coastguard Worker glUniform1i(glGetUniformLocation(shaderProgram, "gainMapTexture"), 1);
356*89a0ef05SAndroid Build Coastguard Worker
357*89a0ef05SAndroid Build Coastguard Worker opengl_ctxt->check_gl_errors("binding values to uniforms");
358*89a0ef05SAndroid Build Coastguard Worker RET_IF_ERR()
359*89a0ef05SAndroid Build Coastguard Worker
360*89a0ef05SAndroid Build Coastguard Worker glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
361*89a0ef05SAndroid Build Coastguard Worker
362*89a0ef05SAndroid Build Coastguard Worker glBindFramebuffer(GL_FRAMEBUFFER, 0);
363*89a0ef05SAndroid Build Coastguard Worker
364*89a0ef05SAndroid Build Coastguard Worker opengl_ctxt->check_gl_errors("reading gles output");
365*89a0ef05SAndroid Build Coastguard Worker RET_IF_ERR()
366*89a0ef05SAndroid Build Coastguard Worker
367*89a0ef05SAndroid Build Coastguard Worker dest->cg = sdr_intent->cg;
368*89a0ef05SAndroid Build Coastguard Worker
369*89a0ef05SAndroid Build Coastguard Worker if (frameBuffer) glDeleteFramebuffers(1, &frameBuffer);
370*89a0ef05SAndroid Build Coastguard Worker if (yuvTexture) glDeleteTextures(1, &yuvTexture);
371*89a0ef05SAndroid Build Coastguard Worker if (shaderProgram) glDeleteProgram(shaderProgram);
372*89a0ef05SAndroid Build Coastguard Worker
373*89a0ef05SAndroid Build Coastguard Worker return opengl_ctxt->mErrorStatus;
374*89a0ef05SAndroid Build Coastguard Worker }
375*89a0ef05SAndroid Build Coastguard Worker
376*89a0ef05SAndroid Build Coastguard Worker } // namespace ultrahdr
377