{render,config}: allow specifying wallpaper anchor position
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Iee73e9d149e85d2c00eaba4be25d42bd6a6a6964
This commit is contained in:
parent
dd0252fe7b
commit
a82b986ac6
4 changed files with 189 additions and 29 deletions
19
include/chroma.h
vendored
19
include/chroma.h
vendored
|
|
@ -44,6 +44,19 @@ typedef enum {
|
|||
CHROMA_SCALE_CENTER = 3 // center image at original size
|
||||
} chroma_scale_mode_t;
|
||||
|
||||
// Anchor positions for wallpaper placement
|
||||
typedef enum {
|
||||
CHROMA_ANCHOR_CENTER = 0, // center of the output (default)
|
||||
CHROMA_ANCHOR_TOP = 1, // top edge, centered horizontally
|
||||
CHROMA_ANCHOR_BOTTOM = 2, // bottom edge, centered horizontally
|
||||
CHROMA_ANCHOR_LEFT = 3, // left edge, centered vertically
|
||||
CHROMA_ANCHOR_RIGHT = 4, // right edge, centered vertically
|
||||
CHROMA_ANCHOR_TOP_LEFT = 5, // top-left corner
|
||||
CHROMA_ANCHOR_TOP_RIGHT = 6, // top-right corner
|
||||
CHROMA_ANCHOR_BOTTOM_LEFT = 7, // bottom-left corner
|
||||
CHROMA_ANCHOR_BOTTOM_RIGHT = 8 // bottom-right corner
|
||||
} chroma_anchor_t;
|
||||
|
||||
// Image filtering quality settings
|
||||
typedef enum {
|
||||
CHROMA_FILTER_NEAREST = 0, // nearest neighbor filtering (pixelated)
|
||||
|
|
@ -90,6 +103,7 @@ typedef struct {
|
|||
// Configuration for this output
|
||||
chroma_scale_mode_t scale_mode;
|
||||
chroma_filter_quality_t filter_quality;
|
||||
chroma_anchor_t anchor;
|
||||
bool config_loaded;
|
||||
|
||||
// OpenGL resource cache
|
||||
|
|
@ -108,6 +122,7 @@ typedef struct {
|
|||
char image_path[MAX_PATH_LEN];
|
||||
chroma_scale_mode_t scale_mode;
|
||||
chroma_filter_quality_t filter_quality;
|
||||
chroma_anchor_t anchor;
|
||||
} chroma_config_mapping_t;
|
||||
|
||||
// Application configuration
|
||||
|
|
@ -120,6 +135,7 @@ typedef struct {
|
|||
// Global scaling and filtering settings (used as defaults)
|
||||
chroma_scale_mode_t default_scale_mode;
|
||||
chroma_filter_quality_t default_filter_quality;
|
||||
chroma_anchor_t default_anchor;
|
||||
|
||||
// Image downsampling settings
|
||||
bool enable_downsampling; // enable automatic downsampling
|
||||
|
|
@ -231,7 +247,8 @@ const char *chroma_config_get_image_for_output(chroma_config_t *config,
|
|||
const char *output_name);
|
||||
int chroma_config_get_mapping_for_output(
|
||||
chroma_config_t *config, const char *output_name,
|
||||
chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality);
|
||||
chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality,
|
||||
chroma_anchor_t *anchor);
|
||||
|
||||
void chroma_config_print(const chroma_config_t *config);
|
||||
|
||||
|
|
|
|||
110
src/config.c
110
src/config.c
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "../include/chroma.h"
|
||||
|
||||
// Default configuration values
|
||||
|
||||
static char *trim_whitespace(char *str) {
|
||||
char *end;
|
||||
|
||||
|
|
@ -122,11 +120,71 @@ static const char *filter_quality_to_string(chroma_filter_quality_t quality) {
|
|||
}
|
||||
}
|
||||
|
||||
// Parse anchor position from string
|
||||
static chroma_anchor_t parse_anchor(const char *value) {
|
||||
if (!value)
|
||||
return CHROMA_ANCHOR_CENTER;
|
||||
|
||||
if (strcasecmp(value, "center") == 0) {
|
||||
return CHROMA_ANCHOR_CENTER;
|
||||
} else if (strcasecmp(value, "top") == 0) {
|
||||
return CHROMA_ANCHOR_TOP;
|
||||
} else if (strcasecmp(value, "bottom") == 0) {
|
||||
return CHROMA_ANCHOR_BOTTOM;
|
||||
} else if (strcasecmp(value, "left") == 0) {
|
||||
return CHROMA_ANCHOR_LEFT;
|
||||
} else if (strcasecmp(value, "right") == 0) {
|
||||
return CHROMA_ANCHOR_RIGHT;
|
||||
} else if (strcasecmp(value, "top-left") == 0 ||
|
||||
strcasecmp(value, "topleft") == 0) {
|
||||
return CHROMA_ANCHOR_TOP_LEFT;
|
||||
} else if (strcasecmp(value, "top-right") == 0 ||
|
||||
strcasecmp(value, "topright") == 0) {
|
||||
return CHROMA_ANCHOR_TOP_RIGHT;
|
||||
} else if (strcasecmp(value, "bottom-left") == 0 ||
|
||||
strcasecmp(value, "bottomleft") == 0) {
|
||||
return CHROMA_ANCHOR_BOTTOM_LEFT;
|
||||
} else if (strcasecmp(value, "bottom-right") == 0 ||
|
||||
strcasecmp(value, "bottomright") == 0) {
|
||||
return CHROMA_ANCHOR_BOTTOM_RIGHT;
|
||||
}
|
||||
|
||||
chroma_log("WARN", "Unknown anchor: %s (using center)", value);
|
||||
return CHROMA_ANCHOR_CENTER;
|
||||
}
|
||||
|
||||
// Get string representation of anchor position
|
||||
static const char *anchor_to_string(chroma_anchor_t anchor) {
|
||||
switch (anchor) {
|
||||
case CHROMA_ANCHOR_CENTER:
|
||||
return "center";
|
||||
case CHROMA_ANCHOR_TOP:
|
||||
return "top";
|
||||
case CHROMA_ANCHOR_BOTTOM:
|
||||
return "bottom";
|
||||
case CHROMA_ANCHOR_LEFT:
|
||||
return "left";
|
||||
case CHROMA_ANCHOR_RIGHT:
|
||||
return "right";
|
||||
case CHROMA_ANCHOR_TOP_LEFT:
|
||||
return "top-left";
|
||||
case CHROMA_ANCHOR_TOP_RIGHT:
|
||||
return "top-right";
|
||||
case CHROMA_ANCHOR_BOTTOM_LEFT:
|
||||
return "bottom-left";
|
||||
case CHROMA_ANCHOR_BOTTOM_RIGHT:
|
||||
return "bottom-right";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Output-to-image mapping
|
||||
static int add_output_mapping(chroma_config_t *config, const char *output_name,
|
||||
const char *image_path,
|
||||
chroma_scale_mode_t scale_mode,
|
||||
chroma_filter_quality_t filter_quality) {
|
||||
chroma_filter_quality_t filter_quality,
|
||||
chroma_anchor_t anchor) {
|
||||
if (!config || !output_name || !image_path) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
|
@ -159,12 +217,14 @@ static int add_output_mapping(chroma_config_t *config, const char *output_name,
|
|||
strcpy(mapping->image_path, image_path);
|
||||
mapping->scale_mode = scale_mode;
|
||||
mapping->filter_quality = filter_quality;
|
||||
mapping->anchor = anchor;
|
||||
|
||||
config->mapping_count++;
|
||||
|
||||
chroma_log("DEBUG", "Added mapping: %s -> %s (scale: %s, filter: %s)",
|
||||
output_name, image_path, scale_mode_to_string(scale_mode),
|
||||
filter_quality_to_string(filter_quality));
|
||||
chroma_log(
|
||||
"DEBUG", "Added mapping: %s -> %s (scale: %s, filter: %s, anchor: %s)",
|
||||
output_name, image_path, scale_mode_to_string(scale_mode),
|
||||
filter_quality_to_string(filter_quality), anchor_to_string(anchor));
|
||||
chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)",
|
||||
config->mapping_count, output_name, image_path, path_len);
|
||||
return CHROMA_OK;
|
||||
|
|
@ -183,6 +243,7 @@ static void init_default_config(chroma_config_t *config) {
|
|||
// Set default scaling and filtering
|
||||
config->default_scale_mode = CHROMA_SCALE_FILL;
|
||||
config->default_filter_quality = CHROMA_FILTER_LINEAR;
|
||||
config->default_anchor = CHROMA_ANCHOR_CENTER;
|
||||
|
||||
// Set default downsampling settings
|
||||
config->enable_downsampling = true; // enable by default, performance etc.
|
||||
|
|
@ -286,6 +347,10 @@ static int parse_config_line(chroma_config_t *config, char *line,
|
|||
config->enable_downsampling = parse_bool(value);
|
||||
chroma_log("DEBUG", "Set downsampling: %s",
|
||||
config->enable_downsampling ? "enabled" : "disabled");
|
||||
} else if (strcasecmp(key, "anchor") == 0) {
|
||||
config->default_anchor = parse_anchor(value);
|
||||
chroma_log("DEBUG", "Set default anchor: %s",
|
||||
anchor_to_string(config->default_anchor));
|
||||
} else if (strcasecmp(key, "max_output_width") == 0) {
|
||||
int width = atoi(value);
|
||||
if (width > 0 && width <= 16384) { // Reasonable limits
|
||||
|
|
@ -351,6 +416,10 @@ static int parse_config_line(chroma_config_t *config, char *line,
|
|||
mapping->filter_quality = parse_filter_quality(value);
|
||||
chroma_log("DEBUG", "Set filter quality for output %s: %s", output_name,
|
||||
filter_quality_to_string(mapping->filter_quality));
|
||||
} else if (strcasecmp(property, "anchor") == 0) {
|
||||
mapping->anchor = parse_anchor(value);
|
||||
chroma_log("DEBUG", "Set anchor for output %s: %s", output_name,
|
||||
anchor_to_string(mapping->anchor));
|
||||
} else {
|
||||
chroma_log("WARN", "Unknown output property: %s (line %d)", property,
|
||||
line_number);
|
||||
|
|
@ -375,7 +444,8 @@ static int parse_config_line(chroma_config_t *config, char *line,
|
|||
|
||||
if (add_output_mapping(config, output_name, path_to_validate,
|
||||
config->default_scale_mode,
|
||||
config->default_filter_quality) != CHROMA_OK) {
|
||||
config->default_filter_quality,
|
||||
config->default_anchor) != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name,
|
||||
path_to_validate);
|
||||
if (expanded_path) {
|
||||
|
|
@ -518,12 +588,13 @@ const char *chroma_config_get_image_for_output(chroma_config_t *config,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Get configuration mapping for output, including scale mode and filter
|
||||
// quality
|
||||
// Get configuration mapping for output, including scale mode, filter
|
||||
// quality, and anchor
|
||||
int chroma_config_get_mapping_for_output(
|
||||
chroma_config_t *config, const char *output_name,
|
||||
chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality) {
|
||||
if (!config || !output_name || !scale_mode || !filter_quality) {
|
||||
chroma_scale_mode_t *scale_mode, chroma_filter_quality_t *filter_quality,
|
||||
chroma_anchor_t *anchor) {
|
||||
if (!config || !output_name || !scale_mode || !filter_quality || !anchor) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
|
|
@ -532,10 +603,13 @@ int chroma_config_get_mapping_for_output(
|
|||
if (strcmp(config->mappings[i].output_name, output_name) == 0) {
|
||||
*scale_mode = config->mappings[i].scale_mode;
|
||||
*filter_quality = config->mappings[i].filter_quality;
|
||||
*anchor = config->mappings[i].anchor;
|
||||
chroma_log("DEBUG",
|
||||
"Found specific mapping for output %s: scale=%s, filter=%s",
|
||||
"Found specific mapping for output %s: scale=%s, filter=%s, "
|
||||
"anchor=%s",
|
||||
output_name, scale_mode_to_string(*scale_mode),
|
||||
filter_quality_to_string(*filter_quality));
|
||||
filter_quality_to_string(*filter_quality),
|
||||
anchor_to_string(*anchor));
|
||||
return CHROMA_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -543,14 +617,14 @@ int chroma_config_get_mapping_for_output(
|
|||
// Return defaults if no specific mapping found
|
||||
*scale_mode = config->default_scale_mode;
|
||||
*filter_quality = config->default_filter_quality;
|
||||
chroma_log("DEBUG", "Using defaults for output %s: scale=%s, filter=%s",
|
||||
output_name, scale_mode_to_string(*scale_mode),
|
||||
filter_quality_to_string(*filter_quality));
|
||||
*anchor = config->default_anchor;
|
||||
chroma_log(
|
||||
"DEBUG", "Using defaults for output %s: scale=%s, filter=%s, anchor=%s",
|
||||
output_name, scale_mode_to_string(*scale_mode),
|
||||
filter_quality_to_string(*filter_quality), anchor_to_string(*anchor));
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Print current configuration for debugging
|
||||
void chroma_config_print(const chroma_config_t *config) {
|
||||
if (!config) {
|
||||
|
|
|
|||
12
src/core.c
12
src/core.c
|
|
@ -112,19 +112,21 @@ static int assign_wallpaper_to_output(chroma_state_t *state,
|
|||
// Store old configuration values for comparison
|
||||
chroma_scale_mode_t old_scale_mode = output->scale_mode;
|
||||
chroma_filter_quality_t old_filter_quality = output->filter_quality;
|
||||
chroma_anchor_t old_anchor = output->anchor;
|
||||
bool had_config = output->config_loaded;
|
||||
|
||||
// Load configuration for this output (scale mode and filter quality)
|
||||
// Load configuration for this output (scale mode, filter quality, anchor)
|
||||
if (chroma_config_get_mapping_for_output(
|
||||
&state->config, output->name ? output->name : "unknown",
|
||||
&output->scale_mode, &output->filter_quality) == CHROMA_OK) {
|
||||
&output->scale_mode, &output->filter_quality, &output->anchor) == CHROMA_OK) {
|
||||
output->config_loaded = true;
|
||||
chroma_log("DEBUG", "Loaded config for output %u: scale=%d, filter=%d",
|
||||
output->id, output->scale_mode, output->filter_quality);
|
||||
chroma_log("DEBUG", "Loaded config for output %u: scale=%d, filter=%d, anchor=%d",
|
||||
output->id, output->scale_mode, output->filter_quality, output->anchor);
|
||||
|
||||
// Check if configuration changed and invalidate texture if needed
|
||||
if (had_config && (old_scale_mode != output->scale_mode ||
|
||||
old_filter_quality != output->filter_quality)) {
|
||||
old_filter_quality != output->filter_quality ||
|
||||
old_anchor != output->anchor)) {
|
||||
chroma_output_invalidate_texture(output);
|
||||
output->vbo_dirty = true; // VBO needs update for new scale mode
|
||||
chroma_log("DEBUG",
|
||||
|
|
|
|||
77
src/render.c
77
src/render.c
|
|
@ -41,8 +41,9 @@ static void get_gl_filter_params(chroma_filter_quality_t quality,
|
|||
}
|
||||
}
|
||||
|
||||
// Calculate texture coordinates based on scaling mode
|
||||
// Calculate texture coordinates based on scaling mode and anchor position
|
||||
static void calculate_texture_coords(chroma_scale_mode_t scale_mode,
|
||||
chroma_anchor_t anchor,
|
||||
int image_width, int image_height,
|
||||
int output_width, int output_height,
|
||||
float tex_coords[8]) {
|
||||
|
|
@ -142,6 +143,72 @@ static void calculate_texture_coords(chroma_scale_mode_t scale_mode,
|
|||
break;
|
||||
}
|
||||
|
||||
// Apply anchor-based offset by shifting the visible crop region
|
||||
// For fill: shift which part of image is shown
|
||||
// For fit/center: shift where the image is positioned
|
||||
//
|
||||
// Positive shift reveals left/bottom portion
|
||||
// Negative shift reveals right/top portion
|
||||
float u_shift = 0.0f;
|
||||
float v_shift = 0.0f;
|
||||
|
||||
switch (anchor) {
|
||||
case CHROMA_ANCHOR_CENTER:
|
||||
u_shift = 0.0f;
|
||||
v_shift = 0.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_TOP:
|
||||
u_shift = 0.0f;
|
||||
v_shift = -0.5f; // shift up to show top of image
|
||||
break;
|
||||
case CHROMA_ANCHOR_BOTTOM:
|
||||
u_shift = 0.0f;
|
||||
v_shift = 0.5f; // shift down to show bottom of image
|
||||
break;
|
||||
case CHROMA_ANCHOR_LEFT:
|
||||
u_shift = -0.5f; // shift left to show left of image
|
||||
v_shift = 0.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_RIGHT:
|
||||
u_shift = 0.5f; // shift right to show right of image
|
||||
v_shift = 0.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_TOP_LEFT:
|
||||
u_shift = -0.5f;
|
||||
v_shift = -0.5f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_TOP_RIGHT:
|
||||
u_shift = 0.5f;
|
||||
v_shift = -0.5f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_BOTTOM_LEFT:
|
||||
u_shift = -0.5f;
|
||||
v_shift = 0.5f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_BOTTOM_RIGHT:
|
||||
u_shift = 0.5f;
|
||||
v_shift = 0.5f;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate the shift amount based on crop/border space available
|
||||
float u_crop = 1.0f - (u2 - u1);
|
||||
float v_crop = 1.0f - (v2 - v1);
|
||||
u1 += u_shift * u_crop;
|
||||
u2 += u_shift * u_crop;
|
||||
v1 += v_shift * v_crop;
|
||||
v2 += v_shift * v_crop;
|
||||
|
||||
// Clamp to valid range [0, 1]
|
||||
if (u1 < 0.0f)
|
||||
u1 = 0.0f;
|
||||
if (u2 > 1.0f)
|
||||
u2 = 1.0f;
|
||||
if (v1 < 0.0f)
|
||||
v1 = 0.0f;
|
||||
if (v2 > 1.0f)
|
||||
v2 = 1.0f;
|
||||
|
||||
// Set texture coordinates for quad (bottom-left, bottom-right, top-right,
|
||||
// top-left)
|
||||
tex_coords[0] = u1;
|
||||
|
|
@ -688,11 +755,11 @@ int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
|
|||
// Update VBO only if needed. E.g, image changed, scale mode changed, or first
|
||||
// render
|
||||
if (output->vbo_dirty) {
|
||||
// Calculate texture coordinates based on scaling mode
|
||||
// Calculate texture coordinates based on scaling mode and anchor
|
||||
float tex_coords[8];
|
||||
calculate_texture_coords(output->scale_mode, output->image->width,
|
||||
output->image->height, output->width,
|
||||
output->height, tex_coords);
|
||||
calculate_texture_coords(output->scale_mode, output->anchor,
|
||||
output->image->width, output->image->height,
|
||||
output->width, output->height, tex_coords);
|
||||
|
||||
// Create dynamic vertex data with calculated texture coordinates
|
||||
float dynamic_vertices[] = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue