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