From e7f107a8fee992411f7e3841b1d59ceea83f9ed3 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sat, 1 Nov 2025 18:46:16 +0300 Subject: [PATCH] render: calculate texture coordinates based on scaling mode Signed-off-by: NotAShelf Change-Id: Ie4e44a0cea68cbbee8122576c41aa4486a6a6964 --- src/render.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 169 insertions(+), 6 deletions(-) diff --git a/src/render.c b/src/render.c index b73e1ab..be9f98a 100644 --- a/src/render.c +++ b/src/render.c @@ -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