render: calculate texture coordinates based on scaling mode

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ie4e44a0cea68cbbee8122576c41aa4486a6a6964
This commit is contained in:
raf 2025-11-01 18:46:16 +03:00
commit e7f107a8fe
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF

View file

@ -8,6 +8,146 @@
#include "../include/chroma.h"
#include "../include/stb_image.h"
// Convert filter quality enum to OpenGL parameters
static void get_gl_filter_params(chroma_filter_quality_t quality,
GLint *min_filter, GLint *mag_filter) {
switch (quality) {
case CHROMA_FILTER_NEAREST:
*min_filter = GL_NEAREST;
*mag_filter = GL_NEAREST;
break;
case CHROMA_FILTER_LINEAR:
*min_filter = GL_LINEAR;
*mag_filter = GL_LINEAR;
break;
case CHROMA_FILTER_BILINEAR:
*min_filter = GL_LINEAR_MIPMAP_LINEAR;
*mag_filter = GL_LINEAR;
break;
case CHROMA_FILTER_TRILINEAR:
*min_filter = GL_LINEAR_MIPMAP_LINEAR;
*mag_filter = GL_LINEAR;
break;
default:
*min_filter = GL_LINEAR;
*mag_filter = GL_LINEAR;
break;
}
}
// Calculate texture coordinates based on scaling mode
static void calculate_texture_coords(chroma_scale_mode_t scale_mode,
int image_width, int image_height,
int output_width, int output_height,
float tex_coords[8]) {
// Default texture coordinates (full texture)
float u1 = 0.0f, v1 = 0.0f; // top-left
float u2 = 1.0f, v2 = 1.0f; // bottom-right
switch (scale_mode) {
case CHROMA_SCALE_STRETCH:
// Use full texture, stretch to fit
u1 = 0.0f;
v1 = 0.0f;
u2 = 1.0f;
v2 = 1.0f;
break;
case CHROMA_SCALE_CENTER:
// Center image at original size
// Calculate how much of the texture to show
{
float image_aspect = (float)image_width / image_height;
float output_aspect = (float)output_width / output_height;
if (image_aspect > output_aspect) {
// Image is wider - fit width, show center portion vertically
float visible_height = (float)image_width / output_aspect;
float v_offset =
(image_height - visible_height) / (2.0f * image_height);
u1 = 0.0f;
v1 = v_offset;
u2 = 1.0f;
v2 = 1.0f - v_offset;
} else {
// Image is taller - fit height, show center portion horizontally
float visible_width = (float)image_height * output_aspect;
float u_offset = (image_width - visible_width) / (2.0f * image_width);
u1 = u_offset;
v1 = 0.0f;
u2 = 1.0f - u_offset;
v2 = 1.0f;
}
}
break;
case CHROMA_SCALE_FIT:
// Fit image within output, maintaining aspect ratio
{
float image_aspect = (float)image_width / image_height;
float output_aspect = (float)output_width / output_height;
if (image_aspect > output_aspect) {
// Image is wider - fit width, add borders top/bottom
float scaled_height = (float)output_width / image_aspect;
float v_border =
(output_height - scaled_height) / (2.0f * output_height);
u1 = 0.0f;
v1 = v_border;
u2 = 1.0f;
v2 = 1.0f - v_border;
} else {
// Image is taller - fit height, add borders left/right
float scaled_width = (float)output_height * image_aspect;
float u_border = (output_width - scaled_width) / (2.0f * output_width);
u1 = u_border;
v1 = 0.0f;
u2 = 1.0f - u_border;
v2 = 1.0f;
}
}
break;
case CHROMA_SCALE_FILL:
default:
// Fill entire output, crop if necessary
{
float image_aspect = (float)image_width / image_height;
float output_aspect = (float)output_width / output_height;
if (image_aspect > output_aspect) {
// Image is wider - crop left/right
float crop_width = image_height * output_aspect;
float u_crop = (image_width - crop_width) / (2.0f * image_width);
u1 = u_crop;
v1 = 0.0f;
u2 = 1.0f - u_crop;
v2 = 1.0f;
} else {
// Image is taller - crop top/bottom
float crop_height = image_width / output_aspect;
float v_crop = (image_height - crop_height) / (2.0f * image_height);
u1 = 0.0f;
v1 = v_crop;
u2 = 1.0f;
v2 = 1.0f - v_crop;
}
}
break;
}
// Set texture coordinates for quad (bottom-left, bottom-right, top-right,
// top-left)
tex_coords[0] = u1;
tex_coords[1] = v2; // bottom-left
tex_coords[2] = u2;
tex_coords[3] = v2; // bottom-right
tex_coords[4] = u2;
tex_coords[5] = v1; // top-right
tex_coords[6] = u1;
tex_coords[7] = v1; // top-left
}
// Vertex shader for simple texture rendering
static const char *vertex_shader_source =
"#version 120\n"
@ -125,7 +265,7 @@ static int init_gl_resources(chroma_output_t *output) {
glGenBuffers(1, &output->ebo);
glBindBuffer(GL_ARRAY_BUFFER, output->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, output->ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
@ -140,7 +280,8 @@ static int init_gl_resources(chroma_output_t *output) {
// Create or update texture from image data
static int update_texture_from_image(chroma_output_t *output,
chroma_image_t *image) {
chroma_image_t *image,
chroma_filter_quality_t filter_quality) {
if (!output || !image || !image->loaded) {
return CHROMA_ERROR_INIT;
}
@ -180,8 +321,12 @@ static int update_texture_from_image(chroma_output_t *output,
// Set texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Use configured filter quality
GLint min_filter, mag_filter;
get_gl_filter_params(filter_quality, &min_filter, &mag_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
// Upload texture data (always RGBA now)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->height, 0,
@ -503,7 +648,8 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
}
if (output->texture_id == 0) {
if (update_texture_from_image(output, output->image) != CHROMA_OK) {
if (update_texture_from_image(output, output->image,
output->filter_quality) != CHROMA_OK) {
chroma_log("ERROR", "Failed to update texture for output %u", output->id);
return CHROMA_ERROR_EGL;
}
@ -524,8 +670,25 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
glBindTexture(GL_TEXTURE_2D, output->texture_id);
glUniform1i(glGetUniformLocation(output->shader_program, "texture"), 0);
// Use cached VBO/EBO
// Calculate texture coordinates based on scaling mode
float tex_coords[8];
calculate_texture_coords(output->scale_mode, output->image->width,
output->image->height, output->width, output->height,
tex_coords);
// Create dynamic vertex data with calculated texture coordinates
float dynamic_vertices[] = {
// Position Texcoord
-1.0f, -1.0f, tex_coords[0], tex_coords[1], // bottom-left
1.0f, -1.0f, tex_coords[2], tex_coords[3], // bottom-right
1.0f, 1.0f, tex_coords[4], tex_coords[5], // top-right
-1.0f, 1.0f, tex_coords[6], tex_coords[7] // top-left
};
// Update VBO with dynamic texture coordinates
glBindBuffer(GL_ARRAY_BUFFER, output->vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(dynamic_vertices),
dynamic_vertices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, output->ebo);
// Set vertex attributes