render: implement coordinate-based anchor positioning
Not to be confused with Minecraft coordinates. Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ifdb90fc92a1565ba1d30b85c91d6e1ab6a6a6964
This commit is contained in:
parent
b6780cc180
commit
dadba853e8
4 changed files with 168 additions and 85 deletions
140
src/config.c
140
src/config.c
|
|
@ -184,7 +184,8 @@ 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_anchor_t anchor) {
|
||||
chroma_anchor_t anchor, float anchor_x,
|
||||
float anchor_y) {
|
||||
if (!config || !output_name || !image_path) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
|
@ -218,13 +219,17 @@ static int add_output_mapping(chroma_config_t *config, const char *output_name,
|
|||
mapping->scale_mode = scale_mode;
|
||||
mapping->filter_quality = filter_quality;
|
||||
mapping->anchor = anchor;
|
||||
mapping->anchor_x = anchor_x;
|
||||
mapping->anchor_y = anchor_y;
|
||||
|
||||
config->mapping_count++;
|
||||
|
||||
chroma_log(
|
||||
"DEBUG", "Added mapping: %s -> %s (scale: %s, filter: %s, anchor: %s)",
|
||||
"DEBUG",
|
||||
"Added mapping: %s -> %s (scale: %s, filter: %s, anchor: %s @ %.1f,%.1f)",
|
||||
output_name, image_path, scale_mode_to_string(scale_mode),
|
||||
filter_quality_to_string(filter_quality), anchor_to_string(anchor));
|
||||
filter_quality_to_string(filter_quality), anchor_to_string(anchor),
|
||||
anchor_x, anchor_y);
|
||||
chroma_log("TRACE", "Output mapping %d: '%s' -> '%s' (path length: %zu)",
|
||||
config->mapping_count, output_name, image_path, path_len);
|
||||
return CHROMA_OK;
|
||||
|
|
@ -244,6 +249,8 @@ static void init_default_config(chroma_config_t *config) {
|
|||
config->default_scale_mode = CHROMA_SCALE_FILL;
|
||||
config->default_filter_quality = CHROMA_FILTER_LINEAR;
|
||||
config->default_anchor = CHROMA_ANCHOR_CENTER;
|
||||
config->default_anchor_x = 50.0f; // center
|
||||
config->default_anchor_y = 50.0f; // center
|
||||
|
||||
// Set default downsampling settings
|
||||
config->enable_downsampling = true; // enable by default, performance etc.
|
||||
|
|
@ -351,6 +358,34 @@ static int parse_config_line(chroma_config_t *config, char *line,
|
|||
config->default_anchor = parse_anchor(value);
|
||||
chroma_log("DEBUG", "Set default anchor: %s",
|
||||
anchor_to_string(config->default_anchor));
|
||||
} else if (strcasecmp(key, "anchor_x") == 0) {
|
||||
char *endptr = NULL;
|
||||
float ax = strtof(value, &endptr);
|
||||
if (endptr == value || *endptr != '\0') {
|
||||
chroma_log("WARN", "Invalid anchor_x: %s (not a number, using 50)",
|
||||
value);
|
||||
config->default_anchor_x = 50.0f;
|
||||
} else if (ax < 0.0f || ax > 100.0f) {
|
||||
chroma_log("WARN", "Invalid anchor_x: %s (range 0-100, using 50)", value);
|
||||
config->default_anchor_x = 50.0f;
|
||||
} else {
|
||||
config->default_anchor_x = ax;
|
||||
chroma_log("DEBUG", "Set default anchor_x: %.1f", ax);
|
||||
}
|
||||
} else if (strcasecmp(key, "anchor_y") == 0) {
|
||||
char *endptr = NULL;
|
||||
float ay = strtof(value, &endptr);
|
||||
if (endptr == value || *endptr != '\0') {
|
||||
chroma_log("WARN", "Invalid anchor_y: %s (not a number, using 50)",
|
||||
value);
|
||||
config->default_anchor_y = 50.0f;
|
||||
} else if (ay < 0.0f || ay > 100.0f) {
|
||||
chroma_log("WARN", "Invalid anchor_y: %s (range 0-100, using 50)", value);
|
||||
config->default_anchor_y = 50.0f;
|
||||
} else {
|
||||
config->default_anchor_y = ay;
|
||||
chroma_log("DEBUG", "Set default anchor_y: %.1f", ay);
|
||||
}
|
||||
} else if (strcasecmp(key, "max_output_width") == 0) {
|
||||
int width = atoi(value);
|
||||
if (width > 0 && width <= 16384) { // Reasonable limits
|
||||
|
|
@ -418,8 +453,70 @@ static int parse_config_line(chroma_config_t *config, char *line,
|
|||
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));
|
||||
// Set anchor_x/anchor_y based on named anchor
|
||||
switch (mapping->anchor) {
|
||||
case CHROMA_ANCHOR_TOP:
|
||||
mapping->anchor_x = 50.0f;
|
||||
mapping->anchor_y = 0.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_BOTTOM:
|
||||
mapping->anchor_x = 50.0f;
|
||||
mapping->anchor_y = 100.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_LEFT:
|
||||
mapping->anchor_x = 0.0f;
|
||||
mapping->anchor_y = 50.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_RIGHT:
|
||||
mapping->anchor_x = 100.0f;
|
||||
mapping->anchor_y = 50.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_TOP_LEFT:
|
||||
mapping->anchor_x = 0.0f;
|
||||
mapping->anchor_y = 0.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_TOP_RIGHT:
|
||||
mapping->anchor_x = 100.0f;
|
||||
mapping->anchor_y = 0.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_BOTTOM_LEFT:
|
||||
mapping->anchor_x = 0.0f;
|
||||
mapping->anchor_y = 100.0f;
|
||||
break;
|
||||
case CHROMA_ANCHOR_BOTTOM_RIGHT:
|
||||
mapping->anchor_x = 100.0f;
|
||||
mapping->anchor_y = 100.0f;
|
||||
break;
|
||||
default:
|
||||
mapping->anchor_x = 50.0f;
|
||||
mapping->anchor_y = 50.0f;
|
||||
break;
|
||||
}
|
||||
chroma_log("DEBUG", "Set anchor for output %s: %s (x=%.1f, y=%.1f)",
|
||||
output_name, anchor_to_string(mapping->anchor),
|
||||
mapping->anchor_x, mapping->anchor_y);
|
||||
} else if (strcasecmp(property, "anchor_x") == 0) {
|
||||
float ax = atof(value);
|
||||
if (ax >= 0.0f && ax <= 100.0f) {
|
||||
mapping->anchor_x = ax;
|
||||
chroma_log("DEBUG", "Set anchor_x for output %s: %.1f", output_name,
|
||||
ax);
|
||||
} else {
|
||||
mapping->anchor_x = 50.0f;
|
||||
chroma_log("WARN", "Invalid anchor_x: %s (range 0-100, using 50)",
|
||||
value);
|
||||
}
|
||||
} else if (strcasecmp(property, "anchor_y") == 0) {
|
||||
float ay = atof(value);
|
||||
if (ay >= 0.0f && ay <= 100.0f) {
|
||||
mapping->anchor_y = ay;
|
||||
chroma_log("DEBUG", "Set anchor_y for output %s: %.1f", output_name,
|
||||
ay);
|
||||
} else {
|
||||
mapping->anchor_y = 50.0f;
|
||||
chroma_log("WARN", "Invalid anchor_y: %s (range 0-100, using 50)",
|
||||
value);
|
||||
}
|
||||
} else {
|
||||
chroma_log("WARN", "Unknown output property: %s (line %d)", property,
|
||||
line_number);
|
||||
|
|
@ -442,10 +539,10 @@ static int parse_config_line(chroma_config_t *config, char *line,
|
|||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
if (add_output_mapping(config, output_name, path_to_validate,
|
||||
config->default_scale_mode,
|
||||
config->default_filter_quality,
|
||||
config->default_anchor) != CHROMA_OK) {
|
||||
if (add_output_mapping(
|
||||
config, output_name, path_to_validate, config->default_scale_mode,
|
||||
config->default_filter_quality, config->default_anchor,
|
||||
config->default_anchor_x, config->default_anchor_y) != CHROMA_OK) {
|
||||
chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name,
|
||||
path_to_validate);
|
||||
if (expanded_path) {
|
||||
|
|
@ -589,12 +686,13 @@ const char *chroma_config_get_image_for_output(chroma_config_t *config,
|
|||
}
|
||||
|
||||
// Get configuration mapping for output, including scale mode, filter
|
||||
// quality, and anchor
|
||||
// quality, anchor, and custom anchor coordinates
|
||||
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_anchor_t *anchor) {
|
||||
if (!config || !output_name || !scale_mode || !filter_quality || !anchor) {
|
||||
chroma_anchor_t *anchor, float *anchor_x, float *anchor_y) {
|
||||
if (!config || !output_name || !scale_mode || !filter_quality || !anchor ||
|
||||
!anchor_x || !anchor_y) {
|
||||
return CHROMA_ERROR_INIT;
|
||||
}
|
||||
|
||||
|
|
@ -604,12 +702,14 @@ int chroma_config_get_mapping_for_output(
|
|||
*scale_mode = config->mappings[i].scale_mode;
|
||||
*filter_quality = config->mappings[i].filter_quality;
|
||||
*anchor = config->mappings[i].anchor;
|
||||
*anchor_x = config->mappings[i].anchor_x;
|
||||
*anchor_y = config->mappings[i].anchor_y;
|
||||
chroma_log("DEBUG",
|
||||
"Found specific mapping for output %s: scale=%s, filter=%s, "
|
||||
"anchor=%s",
|
||||
"anchor=%s @ %.1f,%.1f",
|
||||
output_name, scale_mode_to_string(*scale_mode),
|
||||
filter_quality_to_string(*filter_quality),
|
||||
anchor_to_string(*anchor));
|
||||
anchor_to_string(*anchor), *anchor_x, *anchor_y);
|
||||
return CHROMA_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -618,10 +718,14 @@ int chroma_config_get_mapping_for_output(
|
|||
*scale_mode = config->default_scale_mode;
|
||||
*filter_quality = config->default_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));
|
||||
*anchor_x = config->default_anchor_x;
|
||||
*anchor_y = config->default_anchor_y;
|
||||
chroma_log("DEBUG",
|
||||
"Using defaults for output %s: scale=%s, filter=%s, anchor=%s @ "
|
||||
"%.1f,%.1f",
|
||||
output_name, scale_mode_to_string(*scale_mode),
|
||||
filter_quality_to_string(*filter_quality),
|
||||
anchor_to_string(*anchor), *anchor_x, *anchor_y);
|
||||
return CHROMA_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
39
src/core.c
39
src/core.c
|
|
@ -113,20 +113,29 @@ static int assign_wallpaper_to_output(chroma_state_t *state,
|
|||
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;
|
||||
float old_anchor_x = output->anchor_x;
|
||||
float old_anchor_y = output->anchor_y;
|
||||
bool had_config = output->config_loaded;
|
||||
|
||||
// Load configuration for this output (scale mode, filter quality, anchor)
|
||||
// Load configuration for this output (scale mode, filter quality, anchor,
|
||||
// anchor coords)
|
||||
if (chroma_config_get_mapping_for_output(
|
||||
&state->config, output->name ? output->name : "unknown",
|
||||
&output->scale_mode, &output->filter_quality, &output->anchor) == CHROMA_OK) {
|
||||
&output->scale_mode, &output->filter_quality, &output->anchor,
|
||||
&output->anchor_x, &output->anchor_y) == CHROMA_OK) {
|
||||
output->config_loaded = true;
|
||||
chroma_log("DEBUG", "Loaded config for output %u: scale=%d, filter=%d, anchor=%d",
|
||||
output->id, output->scale_mode, output->filter_quality, output->anchor);
|
||||
chroma_log("DEBUG",
|
||||
"Loaded config for output %u: scale=%d, filter=%d, anchor=%d @ "
|
||||
"%.1f,%.1f",
|
||||
output->id, output->scale_mode, output->filter_quality,
|
||||
output->anchor, output->anchor_x, output->anchor_y);
|
||||
|
||||
// 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_anchor != output->anchor)) {
|
||||
if (had_config &&
|
||||
(old_scale_mode != output->scale_mode ||
|
||||
old_filter_quality != output->filter_quality ||
|
||||
old_anchor != output->anchor || old_anchor_x != output->anchor_x ||
|
||||
old_anchor_y != output->anchor_y)) {
|
||||
chroma_output_invalidate_texture(output);
|
||||
output->vbo_dirty = true; // VBO needs update for new scale mode
|
||||
chroma_log("DEBUG",
|
||||
|
|
@ -380,14 +389,16 @@ void chroma_log(const char *level, const char *format, ...) {
|
|||
gettimeofday(&tv, NULL);
|
||||
tm_info = localtime(&tv.tv_sec);
|
||||
|
||||
truncation_check = snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d.%03d",
|
||||
tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
|
||||
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
|
||||
(int)(tv.tv_usec / 1000));
|
||||
truncation_check =
|
||||
snprintf(timestamp, sizeof(timestamp),
|
||||
"%04d-%02d-%02d %02d:%02d:%02d.%03d", tm_info->tm_year + 1900,
|
||||
tm_info->tm_mon + 1, tm_info->tm_mday, tm_info->tm_hour,
|
||||
tm_info->tm_min, tm_info->tm_sec, (int)(tv.tv_usec / 1000));
|
||||
|
||||
if(truncation_check > 32 || truncation_check < 0) {
|
||||
// Something went seriously wrong with the snprintf, this is a fairly serious error as
|
||||
// the timestamp may be incomplete or corrupted, so print a warning
|
||||
if (truncation_check > 32 || truncation_check < 0) {
|
||||
// Something went seriously wrong with the snprintf, this is a fairly
|
||||
// serious error as the timestamp may be incomplete or corrupted, so print a
|
||||
// warning
|
||||
printf("Following timestamp may be incomplete, truncated or corrupted!\n");
|
||||
}
|
||||
printf("[%s] %s: ", timestamp, level);
|
||||
|
|
|
|||
66
src/render.c
66
src/render.c
|
|
@ -43,7 +43,7 @@ static void get_gl_filter_params(chroma_filter_quality_t quality,
|
|||
|
||||
// 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,
|
||||
float anchor_x, float anchor_y,
|
||||
int image_width, int image_height,
|
||||
int output_width, int output_height,
|
||||
float tex_coords[8]) {
|
||||
|
|
@ -143,53 +143,13 @@ 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;
|
||||
}
|
||||
// Apply anchor-based offset using anchor_x and anchor_y (0-100, 50=center)
|
||||
// anchor_x: 0=left edge, 50=center, 100=right edge
|
||||
// anchor_y: 0=top edge, 50=center, 100=bottom edge
|
||||
// u_shift: negative moves view left (shows more right side of image)
|
||||
// v_shift: negative moves view up (shows more top of image)
|
||||
float u_shift = (anchor_x - 50.0f) / 50.0f; // -1 to 1
|
||||
float v_shift = (50.0f - anchor_y) / 50.0f; // -1 to 1 (inverted for top-down)
|
||||
|
||||
// Calculate the shift amount based on crop/border space available
|
||||
float u_crop = 1.0f - (u2 - u1);
|
||||
|
|
@ -755,11 +715,13 @@ 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 and anchor
|
||||
// Calculate texture coordinates based on scaling mode, anchor, and anchor
|
||||
// coords
|
||||
float tex_coords[8];
|
||||
calculate_texture_coords(output->scale_mode, output->anchor,
|
||||
output->image->width, output->image->height,
|
||||
output->width, output->height, tex_coords);
|
||||
calculate_texture_coords(output->scale_mode, output->anchor_x,
|
||||
output->anchor_y, 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