xref: /aosp_15_r20/external/libultrahdr/lib/src/gpu/editorhelper_gl.cpp (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ultrahdr/editorhelper.h>
18 
19 
20 namespace ultrahdr {
21 
22 extern const std::string vertex_shader;
23 
24 static const std::string mirror_horz_fragmentSource = R"__SHADER__(#version 300 es
25   precision highp float;
26   precision highp sampler2D;
27   in vec2 TexCoord;
28   out vec4 outColor;
29   uniform sampler2D srcTexture;
30   void main() {
31       vec2 texCoord = TexCoord;
32       texCoord.y = 1.0 - TexCoord.y; // Horizontal mirror
33       ivec2 texelCoord = ivec2(texCoord * vec2(textureSize(srcTexture, 0)));
34       vec4 sampledColor = texelFetch(srcTexture, texelCoord, 0);
35       outColor = sampledColor;
36   }
37 )__SHADER__";
38 
39 static const std::string mirror_vert_fragmentSource = R"__SHADER__(#version 300 es
40   precision highp float;
41   precision highp sampler2D;
42   in vec2 TexCoord;
43   out vec4 outColor;
44   uniform sampler2D srcTexture;
45   void main() {
46       vec2 texCoord = TexCoord;
47       texCoord.x = 1.0 - TexCoord.x; // Vertical mirror
48       ivec2 texelCoord = ivec2(texCoord * vec2(textureSize(srcTexture, 0)));
49       vec4 sampledColor = texelFetch(srcTexture, texelCoord, 0);
50       outColor = sampledColor;
51   }
52 )__SHADER__";
53 
54 static const std::string rotate_90_fragmentSource = R"__SHADER__(#version 300 es
55   precision highp float;
56   precision highp sampler2D;
57   in vec2 TexCoord;
58   out vec4 outColor;
59   uniform sampler2D srcTexture;
60   void main() {
61       vec2 texCoord = TexCoord;
62       texCoord = vec2(TexCoord.y, 1.0 - TexCoord.x); // 90 degree
63       ivec2 texelCoord = ivec2(texCoord * vec2(textureSize(srcTexture, 0)));
64       vec4 sampledColor = texelFetch(srcTexture, texelCoord, 0);
65       outColor = sampledColor;
66   }
67 )__SHADER__";
68 
69 static const std::string rotate_180_fragmentSource = R"__SHADER__(#version 300 es
70   precision highp float;
71   precision highp sampler2D;
72   in vec2 TexCoord;
73   out vec4 outColor;
74   uniform sampler2D srcTexture;
75   uniform int rotateDegree;
76   void main() {
77       vec2 texCoord = TexCoord;
78       texCoord = vec2(1.0 - TexCoord.x, 1.0 - TexCoord.y); // 180 degree
79       ivec2 texelCoord = ivec2(texCoord * vec2(textureSize(srcTexture, 0)));
80       vec4 sampledColor = texelFetch(srcTexture, texelCoord, 0);
81       outColor = sampledColor;
82   }
83 )__SHADER__";
84 
85 static const std::string rotate_270_fragmentSource = R"__SHADER__(#version 300 es
86   precision highp float;
87   precision highp sampler2D;
88   in vec2 TexCoord;
89   out vec4 outColor;
90   uniform sampler2D srcTexture;
91   void main() {
92       vec2 texCoord = TexCoord;
93       texCoord = vec2(1.0 - TexCoord.y, TexCoord.x); // 270 degree
94       ivec2 texelCoord = ivec2(texCoord * vec2(textureSize(srcTexture, 0)));
95       vec4 sampledColor = texelFetch(srcTexture, texelCoord, 0);
96       outColor = sampledColor;
97   }
98 )__SHADER__";
99 
100 static const std::string crop_fragmentSource = R"__SHADER__(#version 300 es
101   precision highp float;
102   precision highp sampler2D;
103   in vec2 TexCoord;
104   out vec4 outColor;
105   uniform sampler2D srcTexture;
106   uniform vec2 cropStart; // Crop start coordinate (normalized)
107   uniform vec2 cropSize;  // Size of the crop region (normalized)
108   void main() {
109     vec2 texCoord = cropStart + TexCoord * cropSize;
110     ivec2 texelCoord = ivec2(texCoord * vec2(textureSize(srcTexture, 0)));
111     vec4 sampledColor = texelFetch(srcTexture, texelCoord, 0);
112     outColor = sampledColor;
113   }
114 )__SHADER__";
115 
116 static const std::string resizeShader = R"__SHADER__(
117   uniform sampler2D srcTexture;
118   uniform int srcWidth;
119   uniform int srcHeight;
120   uniform int dstWidth;
121   uniform int dstHeight;
122 
123   // Cubic interpolation function
124   float cubic(float x) {
125     const float a = -0.5;
126     float absX = abs(x);
127     float absX2 = absX * absX;
128     float absX3 = absX2 * absX;
129     if (absX <= 1.0) {
130       return (a + 2.0) * absX3 - (a + 3.0) * absX2 + 1.0;
131     } else if (absX < 2.0) {
132       return a * absX3 - 5.0 * a * absX2 + 8.0 * a * absX - 4.0 * a;
133     }
134     return 0.0;
135   }
136 
137   // Resizing function using bicubic interpolation
138   vec4 resize() {
139     vec2 texCoord = gl_FragCoord.xy / vec2(float(dstWidth), float(dstHeight));
140     vec2 srcCoord = texCoord * vec2(float(srcWidth), float(srcHeight));
141 
142     // Separate the integer and fractional parts of the source coordinates
143     vec2 srcCoordFloor = floor(srcCoord);
144     vec2 srcCoordFrac = fract(srcCoord);
145     vec4 color = vec4(0.0);
146 
147     // Perform bicubic interpolation
148     // Loop through the 4x4 neighborhood of pixels around the source coordinate
149     for (int y = -1; y <= 2; ++y) {
150       float yWeight = cubic(srcCoordFrac.y - float(y));
151       vec4 rowColor = vec4(0.0);
152       for (int x = -1; x <= 2; ++x) {
153           float xWeight = cubic(srcCoordFrac.x - float(x));
154           vec2 sampleCoord = clamp(
155               (srcCoordFloor + vec2(float(x), float(y))) / vec2(float(srcWidth), float(srcHeight)),
156               0.0, 1.0);
157           rowColor += texture(srcTexture, sampleCoord) * xWeight;
158       }
159       color += rowColor * yWeight;
160     }
161     return color;
162   }
163 )__SHADER__";
164 
release_resources(GLuint * texture,GLuint * frameBuffer)165 void release_resources(GLuint* texture, GLuint* frameBuffer) {
166   if (frameBuffer) glDeleteFramebuffers(1, frameBuffer);
167   if (texture) glDeleteTextures(1, texture);
168 }
169 
170 #define RET_IF_ERR()                                       \
171   if (gl_ctxt->mErrorStatus.error_code != UHDR_CODEC_OK) { \
172     release_resources(&dstTexture, &frameBuffer);          \
173     return nullptr;                                        \
174   }
175 
apply_mirror_gles(ultrahdr::uhdr_mirror_effect_t * desc,uhdr_raw_image_t * src,uhdr_opengl_ctxt * gl_ctxt,GLuint * srcTexture)176 std::unique_ptr<uhdr_raw_image_ext_t> apply_mirror_gles(ultrahdr::uhdr_mirror_effect_t* desc,
177                                                         uhdr_raw_image_t* src,
178                                                         uhdr_opengl_ctxt* gl_ctxt,
179                                                         GLuint* srcTexture) {
180   std::unique_ptr<uhdr_raw_image_ext_t> dst = std::make_unique<uhdr_raw_image_ext_t>(
181       src->fmt, src->cg, src->ct, src->range, src->w, src->h, 1);
182   GLuint* shaderProgram = nullptr;
183 
184   if (desc->m_direction == UHDR_MIRROR_HORIZONTAL) {
185     if (gl_ctxt->mShaderProgram[UHDR_MIR_HORZ] == 0) {
186       gl_ctxt->mShaderProgram[UHDR_MIR_HORZ] =
187           gl_ctxt->create_shader_program(vertex_shader.c_str(), mirror_horz_fragmentSource.c_str());
188     }
189     shaderProgram = &gl_ctxt->mShaderProgram[UHDR_MIR_HORZ];
190   } else if (desc->m_direction == UHDR_MIRROR_VERTICAL) {
191     if (gl_ctxt->mShaderProgram[UHDR_MIR_VERT] == 0) {
192       gl_ctxt->mShaderProgram[UHDR_MIR_VERT] =
193           gl_ctxt->create_shader_program(vertex_shader.c_str(), mirror_vert_fragmentSource.c_str());
194     }
195     shaderProgram = &gl_ctxt->mShaderProgram[UHDR_MIR_VERT];
196   }
197   GLuint dstTexture = gl_ctxt->create_texture(src->fmt, dst->w, dst->h, NULL);
198   GLuint frameBuffer = gl_ctxt->setup_framebuffer(dstTexture);
199 
200   glViewport(0, 0, dst->w, dst->h);
201   glUseProgram(*shaderProgram);
202   RET_IF_ERR()
203 
204   glActiveTexture(GL_TEXTURE0);
205   glBindTexture(GL_TEXTURE_2D, *srcTexture);
206   glUniform1i(glGetUniformLocation(*shaderProgram, "srcTexture"), 0);
207   gl_ctxt->check_gl_errors("binding values to uniform");
208   RET_IF_ERR()
209 
210   glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
211   RET_IF_ERR()
212 
213   std::swap(*srcTexture, dstTexture);
214   release_resources(&dstTexture, &frameBuffer);
215   return dst;
216 }
217 
apply_rotate_gles(ultrahdr::uhdr_rotate_effect_t * desc,uhdr_raw_image_t * src,uhdr_opengl_ctxt * gl_ctxt,GLuint * srcTexture)218 std::unique_ptr<uhdr_raw_image_ext_t> apply_rotate_gles(ultrahdr::uhdr_rotate_effect_t* desc,
219                                                         uhdr_raw_image_t* src,
220                                                         uhdr_opengl_ctxt* gl_ctxt,
221                                                         GLuint* srcTexture) {
222   std::unique_ptr<uhdr_raw_image_ext_t> dst;
223   GLuint* shaderProgram;
224   if (desc->m_degree == 90 || desc->m_degree == 270) {
225     dst = std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, src->h,
226                                                  src->w, 1);
227     if (desc->m_degree == 90) {
228       if (gl_ctxt->mShaderProgram[UHDR_ROT_90] == 0) {
229         gl_ctxt->mShaderProgram[UHDR_ROT_90] =
230             gl_ctxt->create_shader_program(vertex_shader.c_str(), rotate_90_fragmentSource.c_str());
231       }
232       shaderProgram = &gl_ctxt->mShaderProgram[UHDR_ROT_90];
233     } else {
234       if (gl_ctxt->mShaderProgram[UHDR_ROT_270] == 0) {
235         gl_ctxt->mShaderProgram[UHDR_ROT_270] = gl_ctxt->create_shader_program(
236             vertex_shader.c_str(), rotate_270_fragmentSource.c_str());
237       }
238       shaderProgram = &gl_ctxt->mShaderProgram[UHDR_ROT_270];
239     }
240   } else if (desc->m_degree == 180) {
241     dst = std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, src->w,
242                                                  src->h, 1);
243     if (gl_ctxt->mShaderProgram[UHDR_ROT_180] == 0) {
244       gl_ctxt->mShaderProgram[UHDR_ROT_180] =
245           gl_ctxt->create_shader_program(vertex_shader.c_str(), rotate_180_fragmentSource.c_str());
246     }
247     shaderProgram = &gl_ctxt->mShaderProgram[UHDR_ROT_180];
248   } else {
249     return nullptr;
250   }
251   GLuint dstTexture = gl_ctxt->create_texture(src->fmt, dst->w, dst->h, NULL);
252   GLuint frameBuffer = gl_ctxt->setup_framebuffer(dstTexture);
253 
254   glViewport(0, 0, dst->w, dst->h);
255   glUseProgram(*shaderProgram);
256   RET_IF_ERR()
257 
258   glActiveTexture(GL_TEXTURE0);
259   glBindTexture(GL_TEXTURE_2D, *srcTexture);
260   glUniform1i(glGetUniformLocation(*shaderProgram, "srcTexture"), 0);
261   gl_ctxt->check_gl_errors("binding values to uniform");
262   RET_IF_ERR()
263 
264   glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
265   RET_IF_ERR()
266 
267   std::swap(*srcTexture, dstTexture);
268   release_resources(&dstTexture, &frameBuffer);
269   return dst;
270 }
271 
apply_crop_gles(uhdr_raw_image_t * src,int left,int top,int wd,int ht,uhdr_opengl_ctxt * gl_ctxt,GLuint * srcTexture)272 std::unique_ptr<uhdr_raw_image_ext_t> apply_crop_gles(uhdr_raw_image_t* src, int left, int top,
273                                                       int wd, int ht, uhdr_opengl_ctxt* gl_ctxt,
274                                                       GLuint* srcTexture) {
275   std::unique_ptr<uhdr_raw_image_ext_t> dst =
276       std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, wd, ht, 1);
277   GLuint dstTexture = 0;
278   GLuint frameBuffer = 0;
279 
280   if (gl_ctxt->mShaderProgram[UHDR_CROP] == 0) {
281     gl_ctxt->mShaderProgram[UHDR_CROP] =
282         gl_ctxt->create_shader_program(vertex_shader.c_str(), crop_fragmentSource.c_str());
283   }
284   dstTexture = gl_ctxt->create_texture(src->fmt, wd, ht, NULL);
285   frameBuffer = gl_ctxt->setup_framebuffer(dstTexture);
286 
287   glViewport(0, 0, dst->w, dst->h);
288   glUseProgram(gl_ctxt->mShaderProgram[UHDR_CROP]);
289 
290   float normCropX = (float)left / src->w;
291   float normCropY = (float)top / src->h;
292   float normCropW = (float)wd / src->w;
293   float normCropH = (float)ht / src->h;
294 
295   glActiveTexture(GL_TEXTURE0);
296   glBindTexture(GL_TEXTURE_2D, *srcTexture);
297   glUniform1i(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_CROP], "srcTexture"), 0);
298   glUniform2f(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_CROP], "cropStart"), normCropX,
299               normCropY);
300   glUniform2f(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_CROP], "cropSize"), normCropW,
301               normCropH);
302   gl_ctxt->check_gl_errors("binding values to uniform");
303   RET_IF_ERR()
304 
305   glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
306   RET_IF_ERR()
307 
308   std::swap(*srcTexture, dstTexture);
309   release_resources(&dstTexture, &frameBuffer);
310   return dst;
311 }
312 
apply_resize_gles(uhdr_raw_image_t * src,int dst_w,int dst_h,uhdr_opengl_ctxt * gl_ctxt,GLuint * srcTexture)313 std::unique_ptr<uhdr_raw_image_ext_t> apply_resize_gles(uhdr_raw_image_t* src, int dst_w, int dst_h,
314                                                         uhdr_opengl_ctxt* gl_ctxt,
315                                                         GLuint* srcTexture) {
316   std::unique_ptr<uhdr_raw_image_ext_t> dst = std::make_unique<uhdr_raw_image_ext_t>(
317       src->fmt, src->cg, src->ct, src->range, dst_w, dst_h, 1);
318   std::string shader_code = R"__SHADER__(#version 300 es
319     precision highp float;
320     in vec2 TexCoord;
321     out vec4 fragColor;
322   )__SHADER__";
323   shader_code.append(resizeShader);
324   shader_code.append(R"__SHADER__(
325     void main() {
326       fragColor = resize();
327     }
328   )__SHADER__");
329   if (gl_ctxt->mShaderProgram[UHDR_RESIZE] == 0) {
330     gl_ctxt->mShaderProgram[UHDR_RESIZE] =
331         gl_ctxt->create_shader_program(vertex_shader.c_str(), shader_code.c_str());
332   }
333   GLuint dstTexture = gl_ctxt->create_texture(src->fmt, dst_w, dst_h, NULL);
334   GLuint frameBuffer = gl_ctxt->setup_framebuffer(dstTexture);
335 
336   glViewport(0, 0, dst->w, dst->h);
337   glUseProgram(gl_ctxt->mShaderProgram[UHDR_RESIZE]);
338   RET_IF_ERR()
339 
340   glActiveTexture(GL_TEXTURE0);
341   glBindTexture(GL_TEXTURE_2D, *srcTexture);
342   glUniform1i(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_RESIZE], "srcTexture"), 0);
343   glUniform1i(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_RESIZE], "srcWidth"),
344               src->w);
345   glUniform1i(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_RESIZE], "srcHeight"),
346               src->h);
347   glUniform1i(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_RESIZE], "dstWidth"), dst_w);
348   glUniform1i(glGetUniformLocation(gl_ctxt->mShaderProgram[UHDR_RESIZE], "dstHeight"),
349               dst_h);
350   gl_ctxt->check_gl_errors("binding values to uniform");
351   RET_IF_ERR()
352 
353   glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
354   RET_IF_ERR()
355 
356   std::swap(*srcTexture, dstTexture);
357   release_resources(&dstTexture, &frameBuffer);
358   return dst;
359 }
360 #undef RET_IF_ERR
361 }  // namespace ultrahdr
362