From c3046ec1728a6179139b6ed3653d10f9b81f5e52 Mon Sep 17 00:00:00 2001 From: Lain Date: Wed, 19 Aug 2026 14:10:50 -0400 Subject: [PATCH] libobs/graphics: Break image file API to prevent ABI issues --- libobs/graphics/image-file.c | 100 +++++------------- libobs/graphics/image-file.h | 75 ++----------- plugins/image-source/image-source.c | 47 ++++---- plugins/obs-filters/color-grade-filter.c | 8 +- plugins/obs-filters/mask-filter.c | 17 +-- .../obs-transitions/transition-luma-wipe.c | 10 +- 6 files changed, 77 insertions(+), 180 deletions(-) diff --git a/libobs/graphics/image-file.c b/libobs/graphics/image-file.c index 61ff71332..ab8e0ce94 100644 --- a/libobs/graphics/image-file.c +++ b/libobs/graphics/image-file.c @@ -69,22 +69,23 @@ static void bi_def_bitmap_modified(void *bitmap) UNUSED_PARAMETER(bitmap); } -static inline int get_full_decoded_gif_size(gs_image_file_t *image) +static inline int get_full_decoded_gif_size(gs_image_file_ex_t *image) { return image->internal->gif_info->width * image->internal->gif_info->height * 4 * image->internal->gif_info->frame_count; } -static inline void *alloc_mem(gs_image_file_t *image, uint64_t *mem_usage, size_t size) +static inline void *alloc_mem(gs_image_file_ex_t *image, uint64_t *mem_usage, size_t size) { UNUSED_PARAMETER(image); if (mem_usage) *mem_usage += size; + return bzalloc(size); } -static bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t *mem_usage, +static bool init_animated_gif(gs_image_file_ex_t *image, const char *path, uint64_t *mem_usage, enum gs_image_alpha_mode alpha_mode) { bool is_animated_gif = true; @@ -159,10 +160,7 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t for (unsigned int i = 0; i < image->internal->gif_info->frame_count; i++) { if (nsgif_frame_decode(image->internal->gif, i, &bitmap) != NSGIF_OK) - blog(LOG_WARNING, - "Couldn't decode frame %u " - "of '%s'", - i, path); + blog(LOG_WARNING, "Couldn't decode frame %u of '%s'", i, path); } nsgif_frame_decode(image->internal->gif, 0, &bitmap); @@ -195,7 +193,8 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t fail: if (!image->loaded) - gs_image_file_free(image); + gs_image_file_ex_free(image); + not_animated: if (file) fclose(file); @@ -203,8 +202,8 @@ not_animated: return is_animated_gif; } -static void gs_image_file_init_internal(gs_image_file_t *image, const char *file, uint64_t *mem_usage, - enum gs_color_space *space, enum gs_image_alpha_mode alpha_mode) +static void gs_image_file_ex_init_internal(gs_image_file_ex_t *image, const char *file, uint64_t *mem_usage, + enum gs_color_space *space, enum gs_image_alpha_mode alpha_mode) { size_t len; @@ -236,17 +235,17 @@ static void gs_image_file_init_internal(gs_image_file_t *image, const char *file image->loaded = !!image->texture_data; if (!image->loaded) { blog(LOG_WARNING, "Failed to load file '%s'", file); - gs_image_file_free(image); + gs_image_file_ex_free(image); } } -void gs_image_file_init(gs_image_file_t *image, const char *file) +void gs_image_file_ex_init(gs_image_file_ex_t *image, const char *file, enum gs_image_alpha_mode alpha_mode) { enum gs_color_space unused; - gs_image_file_init_internal(image, file, NULL, &unused, GS_IMAGE_ALPHA_STRAIGHT); + gs_image_file_ex_init_internal(image, file, NULL, &unused, alpha_mode); } -void gs_image_file_free(gs_image_file_t *image) +void gs_image_file_ex_free(gs_image_file_ex_t *image) { if (!image) return; @@ -269,27 +268,7 @@ void gs_image_file_free(gs_image_file_t *image) memset(image, 0, sizeof(*image)); } -void gs_image_file2_init(gs_image_file2_t *if2, const char *file) -{ - enum gs_color_space unused; - gs_image_file_init_internal(&if2->image, file, &if2->mem_usage, &unused, GS_IMAGE_ALPHA_STRAIGHT); -} - -void gs_image_file3_init(gs_image_file3_t *if3, const char *file, enum gs_image_alpha_mode alpha_mode) -{ - enum gs_color_space unused; - gs_image_file_init_internal(&if3->image2.image, file, &if3->image2.mem_usage, &unused, alpha_mode); - if3->alpha_mode = alpha_mode; -} - -void gs_image_file4_init(gs_image_file4_t *if4, const char *file, enum gs_image_alpha_mode alpha_mode) -{ - gs_image_file_init_internal(&if4->image3.image2.image, file, &if4->image3.image2.mem_usage, &if4->space, - alpha_mode); - if4->image3.alpha_mode = alpha_mode; -} - -void gs_image_file_init_texture(gs_image_file_t *image) +void gs_image_file_ex_init_texture(gs_image_file_ex_t *image) { if (!image->loaded) return; @@ -309,16 +288,17 @@ void gs_image_file_init_texture(gs_image_file_t *image) } } -static inline uint64_t get_time(gs_image_file_t *image, int i) +static inline uint64_t get_time(gs_image_file_ex_t *image, int i) { const nsgif_frame_info_t *gif_frame_info = nsgif_get_frame_info(image->internal->gif, i); uint64_t val = (uint64_t)gif_frame_info->delay * 10000000ULL; if (!val) val = 100000000; + return val; } -static inline int calculate_new_frame(gs_image_file_t *image, uint64_t elapsed_time_ns, int loops) +static inline int calculate_new_frame(gs_image_file_ex_t *image, uint64_t elapsed_time_ns, int loops) { int new_frame = image->cur_frame; @@ -342,7 +322,7 @@ static inline int calculate_new_frame(gs_image_file_t *image, uint64_t elapsed_t return new_frame; } -static void decode_new_frame(gs_image_file_t *image, int new_frame, enum gs_image_alpha_mode alpha_mode) +static void decode_new_frame(gs_image_file_ex_t *image, int new_frame, enum gs_image_alpha_mode alpha_mode) { if (!image->internal->animation_frame_cache[new_frame]) { int last_frame; @@ -382,8 +362,8 @@ static void decode_new_frame(gs_image_file_t *image, int new_frame, enum gs_imag image->cur_frame = new_frame; } -static bool gs_image_file_tick_internal(gs_image_file_t *image, uint64_t elapsed_time_ns, - enum gs_image_alpha_mode alpha_mode) +static bool gs_image_file_ex_tick_internal(gs_image_file_ex_t *image, uint64_t elapsed_time_ns, + enum gs_image_alpha_mode alpha_mode) { int loops; @@ -406,27 +386,12 @@ static bool gs_image_file_tick_internal(gs_image_file_t *image, uint64_t elapsed return false; } -bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns) +bool gs_image_file_ex_tick(gs_image_file_ex_t *image, uint64_t elapsed_time_ns) { - return gs_image_file_tick_internal(image, elapsed_time_ns, false); + return gs_image_file_ex_tick_internal(image, elapsed_time_ns, false); } -bool gs_image_file2_tick(gs_image_file2_t *if2, uint64_t elapsed_time_ns) -{ - return gs_image_file_tick_internal(&if2->image, elapsed_time_ns, false); -} - -bool gs_image_file3_tick(gs_image_file3_t *if3, uint64_t elapsed_time_ns) -{ - return gs_image_file_tick_internal(&if3->image2.image, elapsed_time_ns, if3->alpha_mode); -} - -bool gs_image_file4_tick(gs_image_file4_t *if4, uint64_t elapsed_time_ns) -{ - return gs_image_file_tick_internal(&if4->image3.image2.image, elapsed_time_ns, if4->image3.alpha_mode); -} - -static void gs_image_file_update_texture_internal(gs_image_file_t *image, enum gs_image_alpha_mode alpha_mode) +static void gs_image_file_ex_update_texture_internal(gs_image_file_ex_t *image, enum gs_image_alpha_mode alpha_mode) { if (!image->is_animated_gif || !image->loaded || !image->internal) return; @@ -438,22 +403,7 @@ static void gs_image_file_update_texture_internal(gs_image_file_t *image, enum g image->internal->gif_info->width * 4, false); } -void gs_image_file_update_texture(gs_image_file_t *image) +void gs_image_file_ex_update_texture(gs_image_file_ex_t *image) { - gs_image_file_update_texture_internal(image, false); -} - -void gs_image_file2_update_texture(gs_image_file2_t *if2) -{ - gs_image_file_update_texture_internal(&if2->image, false); -} - -void gs_image_file3_update_texture(gs_image_file3_t *if3) -{ - gs_image_file_update_texture_internal(&if3->image2.image, if3->alpha_mode); -} - -void gs_image_file4_update_texture(gs_image_file4_t *if4) -{ - gs_image_file_update_texture_internal(&if4->image3.image2.image, if4->image3.alpha_mode); + gs_image_file_ex_update_texture_internal(image, false); } diff --git a/libobs/graphics/image-file.h b/libobs/graphics/image-file.h index 4773b512d..e74a0afba 100644 --- a/libobs/graphics/image-file.h +++ b/libobs/graphics/image-file.h @@ -23,7 +23,7 @@ extern "C" { #endif -struct gs_image_file { +struct gs_image_file_ex { gs_texture_t *texture; enum gs_color_format format; uint32_t cx; @@ -39,80 +39,19 @@ struct gs_image_file { int cur_loop; uint8_t *texture_data; -}; - -struct gs_image_file2 { - struct gs_image_file image; uint64_t mem_usage; -}; - -struct gs_image_file3 { - struct gs_image_file2 image2; enum gs_image_alpha_mode alpha_mode; -}; - -struct gs_image_file4 { - struct gs_image_file3 image3; enum gs_color_space space; }; -typedef struct gs_image_file gs_image_file_t; -typedef struct gs_image_file2 gs_image_file2_t; -typedef struct gs_image_file3 gs_image_file3_t; -typedef struct gs_image_file4 gs_image_file4_t; +typedef struct gs_image_file_ex gs_image_file_ex_t; -EXPORT void gs_image_file_init(gs_image_file_t *image, const char *file); -EXPORT void gs_image_file_free(gs_image_file_t *image); +EXPORT void gs_image_file_ex_init(gs_image_file_ex_t *image, const char *file, enum gs_image_alpha_mode alpha_mode); +EXPORT void gs_image_file_ex_free(gs_image_file_ex_t *image); -EXPORT void gs_image_file_init_texture(gs_image_file_t *image); -EXPORT bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns); -EXPORT void gs_image_file_update_texture(gs_image_file_t *image); - -EXPORT void gs_image_file2_init(gs_image_file2_t *if2, const char *file); - -EXPORT bool gs_image_file2_tick(gs_image_file2_t *if2, uint64_t elapsed_time_ns); -EXPORT void gs_image_file2_update_texture(gs_image_file2_t *if2); - -EXPORT void gs_image_file3_init(gs_image_file3_t *if3, const char *file, enum gs_image_alpha_mode alpha_mode); - -EXPORT bool gs_image_file3_tick(gs_image_file3_t *if3, uint64_t elapsed_time_ns); -EXPORT void gs_image_file3_update_texture(gs_image_file3_t *if3); - -EXPORT void gs_image_file4_init(gs_image_file4_t *if4, const char *file, enum gs_image_alpha_mode alpha_mode); - -EXPORT bool gs_image_file4_tick(gs_image_file4_t *if4, uint64_t elapsed_time_ns); -EXPORT void gs_image_file4_update_texture(gs_image_file4_t *if4); - -static inline void gs_image_file2_free(gs_image_file2_t *if2) -{ - gs_image_file_free(&if2->image); - if2->mem_usage = 0; -} - -static inline void gs_image_file2_init_texture(gs_image_file2_t *if2) -{ - gs_image_file_init_texture(&if2->image); -} - -static inline void gs_image_file3_free(gs_image_file3_t *if3) -{ - gs_image_file2_free(&if3->image2); -} - -static inline void gs_image_file3_init_texture(gs_image_file3_t *if3) -{ - gs_image_file2_init_texture(&if3->image2); -} - -static inline void gs_image_file4_free(gs_image_file4_t *if4) -{ - gs_image_file3_free(&if4->image3); -} - -static inline void gs_image_file4_init_texture(gs_image_file4_t *if4) -{ - gs_image_file3_init_texture(&if4->image3); -} +EXPORT void gs_image_file_ex_init_texture(gs_image_file_ex_t *image); +EXPORT bool gs_image_file_ex_tick(gs_image_file_ex_t *image, uint64_t elapsed_time_ns); +EXPORT void gs_image_file_ex_update_texture(gs_image_file_ex_t *image); #ifdef __cplusplus } diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index 259d2fce0..758d70761 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -27,7 +27,7 @@ struct image_source { volatile bool file_decoded; volatile bool texture_loaded; - gs_image_file4_t if4; + gs_image_file_ex_t image; }; static time_t get_modified_timestamp(const char *filename) @@ -35,6 +35,7 @@ static time_t get_modified_timestamp(const char *filename) struct stat stats; if (os_stat(filename, &stats) != 0) return -1; + return stats.st_mtime; } @@ -51,8 +52,8 @@ void image_source_preload_image(void *data) return; context->file_timestamp = get_modified_timestamp(context->file); - gs_image_file4_init(&context->if4, context->file, - context->linear_alpha ? GS_IMAGE_ALPHA_PREMULTIPLY_SRGB : GS_IMAGE_ALPHA_PREMULTIPLY); + gs_image_file_ex_init(&context->image, context->file, + context->linear_alpha ? GS_IMAGE_ALPHA_PREMULTIPLY_SRGB : GS_IMAGE_ALPHA_PREMULTIPLY); os_atomic_set_bool(&context->file_decoded, true); } @@ -65,11 +66,12 @@ static void image_source_load_texture(void *data) debug("loading texture '%s'", context->file); obs_enter_graphics(); - gs_image_file4_init_texture(&context->if4); + gs_image_file_ex_init_texture(&context->image); obs_leave_graphics(); - if (!context->if4.image3.image2.image.loaded) + if (!context->image.loaded) warn("failed to load texture '%s'", context->file); + context->update_time_elapsed = 0; os_atomic_set_bool(&context->texture_loaded, true); } @@ -81,7 +83,7 @@ static void image_source_unload(void *data) os_atomic_set_bool(&context->texture_loaded, false); obs_enter_graphics(); - gs_image_file4_free(&context->if4); + gs_image_file_ex_free(&context->image); obs_leave_graphics(); } @@ -105,6 +107,7 @@ static void image_source_update(void *data, obs_data_t *settings) if (context->file) bfree(context->file); + context->file = bstrdup(file); context->persistent = !unload; context->linear_alpha = linear_alpha; @@ -146,13 +149,13 @@ static void restart_gif(void *data) { struct image_source *context = data; - if (context->if4.image3.image2.image.is_animated_gif) { - context->if4.image3.image2.image.cur_frame = 0; - context->if4.image3.image2.image.cur_loop = 0; - context->if4.image3.image2.image.cur_time = 0; + if (context->image.is_animated_gif) { + context->image.cur_frame = 0; + context->image.cur_loop = 0; + context->image.cur_time = 0; obs_enter_graphics(); - gs_image_file4_update_texture(&context->if4); + gs_image_file_ex_update_texture(&context->image); obs_leave_graphics(); context->restart_gif = false; @@ -182,19 +185,20 @@ static void image_source_destroy(void *data) if (context->file) bfree(context->file); + bfree(context); } static uint32_t image_source_getwidth(void *data) { struct image_source *context = data; - return context->if4.image3.image2.image.cx; + return context->image.cx; } static uint32_t image_source_getheight(void *data) { struct image_source *context = data; - return context->if4.image3.image2.image.cy; + return context->image.cy; } static void image_source_render(void *data, gs_effect_t *effect) @@ -203,7 +207,7 @@ static void image_source_render(void *data, gs_effect_t *effect) if (!os_atomic_load_bool(&context->texture_loaded)) return; - struct gs_image_file *const image = &context->if4.image3.image2.image; + gs_image_file_ex_t *const image = &context->image; gs_texture_t *const texture = image->texture; if (!texture) return; @@ -251,8 +255,9 @@ static void image_source_tick(void *data, float seconds) if (obs_source_showing(context->source)) { if (!context->active) { - if (context->if4.image3.image2.image.is_animated_gif) + if (context->image.is_animated_gif) context->last_time = frame_time; + context->active = true; } @@ -268,13 +273,13 @@ static void image_source_tick(void *data, float seconds) return; } - if (context->last_time && context->if4.image3.image2.image.is_animated_gif) { + if (context->last_time && context->image.is_animated_gif) { uint64_t elapsed = frame_time - context->last_time; - bool updated = gs_image_file4_tick(&context->if4, elapsed); + bool updated = gs_image_file_ex_tick(&context->image, elapsed); if (updated) { obs_enter_graphics(); - gs_image_file4_update_texture(&context->if4); + gs_image_file_ex_update_texture(&context->image); obs_leave_graphics(); } } @@ -316,7 +321,7 @@ static obs_properties_t *image_source_properties(void *data) uint64_t image_source_get_memory_usage(void *data) { struct image_source *s = data; - return s->if4.image3.image2.mem_usage; + return s->image.mem_usage; } static void missing_file_callback(void *src, const char *new_path, void *data) @@ -356,8 +361,8 @@ static enum gs_color_space image_source_get_color_space(void *data, size_t count UNUSED_PARAMETER(preferred_spaces); struct image_source *const s = data; - gs_image_file4_t *const if4 = &s->if4; - return if4->image3.image2.image.texture ? if4->space : GS_CS_SRGB; + gs_image_file_ex_t *const image = &s->image; + return image->texture ? image->space : GS_CS_SRGB; } static struct obs_source_info image_source_info = { diff --git a/plugins/obs-filters/color-grade-filter.c b/plugins/obs-filters/color-grade-filter.c index 7fd31edc7..f113a7254 100644 --- a/plugins/obs-filters/color-grade-filter.c +++ b/plugins/obs-filters/color-grade-filter.c @@ -30,7 +30,7 @@ struct lut_filter_data { gs_effect_t *effect; gs_texture_t *target; - gs_image_file_t image; + gs_image_file_ex_t image; uint32_t cube_width; void *cube_data; @@ -243,7 +243,7 @@ static void color_grade_filter_update(void *data, obs_data_t *settings) filter->cube_data = NULL; obs_enter_graphics(); - gs_image_file_free(&filter->image); + gs_image_file_ex_free(&filter->image); gs_voltexture_destroy(filter->target); filter->target = NULL; obs_leave_graphics(); @@ -261,7 +261,7 @@ static void color_grade_filter_update(void *data, obs_data_t *settings) filter->cube_data = load_cube_file(path, &filter->cube_width, &filter->domain_min, &filter->domain_max, &clut_dim); } else { - gs_image_file_init(&filter->image, path); + gs_image_file_ex_init(&filter->image, path, GS_IMAGE_ALPHA_STRAIGHT); filter->cube_width = LUT_WIDTH; } @@ -384,7 +384,7 @@ static void color_grade_filter_destroy(void *data) obs_enter_graphics(); gs_effect_destroy(filter->effect); gs_voltexture_destroy(filter->target); - gs_image_file_free(&filter->image); + gs_image_file_ex_free(&filter->image); obs_leave_graphics(); bfree(filter->cube_data); diff --git a/plugins/obs-filters/mask-filter.c b/plugins/obs-filters/mask-filter.c index 8b9536f98..dbf48146b 100644 --- a/plugins/obs-filters/mask-filter.c +++ b/plugins/obs-filters/mask-filter.c @@ -37,7 +37,7 @@ struct mask_filter_data { float update_time_elapsed; gs_texture_t *target; - gs_image_file_t image; + gs_image_file_ex_t image; struct vec4 color; bool lock_aspect; }; @@ -47,6 +47,7 @@ static time_t get_modified_timestamp(const char *filename) struct stat stats; if (os_stat(filename, &stats) != 0) return -1; + return stats.st_mtime; } @@ -59,7 +60,7 @@ static const char *mask_filter_get_name(void *unused) static void mask_filter_image_unload(struct mask_filter_data *filter) { obs_enter_graphics(); - gs_image_file_free(&filter->image); + gs_image_file_ex_free(&filter->image); obs_leave_graphics(); } @@ -71,11 +72,11 @@ static void mask_filter_image_load(struct mask_filter_data *filter) if (path && *path) { filter->image_file_timestamp = get_modified_timestamp(path); - gs_image_file_init(&filter->image, path); + gs_image_file_ex_init(&filter->image, path, GS_IMAGE_ALPHA_STRAIGHT); filter->update_time_elapsed = 0; obs_enter_graphics(); - gs_image_file_init_texture(&filter->image); + gs_image_file_ex_init_texture(&filter->image); obs_leave_graphics(); } @@ -93,12 +94,14 @@ static void mask_filter_update_internal(void *data, obs_data_t *settings, float if (filter->image_file) bfree(filter->image_file); + filter->image_file = bstrdup(path); if (srgb) vec4_from_rgba_srgb(&filter->color, color); else vec4_from_rgba(&filter->color, color); + filter->color.w = opacity; mask_filter_image_load(filter); @@ -209,7 +212,7 @@ static void mask_filter_destroy(void *data) obs_enter_graphics(); gs_effect_destroy(filter->effect); - gs_image_file_free(&filter->image); + gs_image_file_ex_free(&filter->image); obs_leave_graphics(); bfree(filter); @@ -235,9 +238,9 @@ static void mask_filter_tick(void *data, float seconds) if (!filter->last_time) filter->last_time = cur_time; - gs_image_file_tick(&filter->image, cur_time - filter->last_time); + gs_image_file_ex_tick(&filter->image, cur_time - filter->last_time); obs_enter_graphics(); - gs_image_file_update_texture(&filter->image); + gs_image_file_ex_update_texture(&filter->image); obs_leave_graphics(); filter->last_time = cur_time; diff --git a/plugins/obs-transitions/transition-luma-wipe.c b/plugins/obs-transitions/transition-luma-wipe.c index b2c04efeb..90475de23 100644 --- a/plugins/obs-transitions/transition-luma-wipe.c +++ b/plugins/obs-transitions/transition-luma-wipe.c @@ -25,7 +25,7 @@ struct luma_wipe_info { gs_eparam_t *ep_invert; gs_eparam_t *ep_softness; - gs_image_file_t luma_image; + gs_image_file_ex_t luma_image; bool invert_luma; float softness; obs_data_t *wipes_list; @@ -53,13 +53,13 @@ static void luma_wipe_update(void *data, obs_data_t *settings) char *file = obs_module_file(path.array); obs_enter_graphics(); - gs_image_file_free(&lwipe->luma_image); + gs_image_file_ex_free(&lwipe->luma_image); obs_leave_graphics(); - gs_image_file_init(&lwipe->luma_image, file); + gs_image_file_ex_init(&lwipe->luma_image, file, GS_IMAGE_ALPHA_STRAIGHT); obs_enter_graphics(); - gs_image_file_init_texture(&lwipe->luma_image); + gs_image_file_ex_init_texture(&lwipe->luma_image); obs_leave_graphics(); bfree(file); @@ -117,7 +117,7 @@ static void luma_wipe_destroy(void *data) struct luma_wipe_info *lwipe = data; obs_enter_graphics(); - gs_image_file_free(&lwipe->luma_image); + gs_image_file_ex_free(&lwipe->luma_image); obs_leave_graphics(); obs_data_release(lwipe->wipes_list);