Merge pull request #11178 from RytoEX/really-update-libnsgif

libobs/graphics: Update libnsgif to 1.0.0
libobs/graphics: Hide GIF decoding data from exported header file
libobs/graphics: Break image file API to prevent ABI issues

Co-authored-by: Norihiro Kamae <norihiro@nagater.net>
Co-authored-by: Lain <lain@obsproject.com>
This commit is contained in:
Ryan Foster
2026-08-21 16:09:31 -04:00
committed by GitHub
14 changed files with 3538 additions and 1677 deletions
+5 -3
View File
@@ -190,8 +190,10 @@ target_sources(
graphics/image-file.c
graphics/image-file.h
graphics/input.h
graphics/libnsgif/libnsgif.c
graphics/libnsgif/libnsgif.h
graphics/libnsgif/gif.c
graphics/libnsgif/lzw.c
graphics/libnsgif/lzw.h
graphics/libnsgif/nsgif.h
graphics/math-defs.h
graphics/math-extra.c
graphics/math-extra.h
@@ -282,7 +284,7 @@ set(
graphics/graphics.h
graphics/image-file.h
graphics/input.h
graphics/libnsgif/libnsgif.h
graphics/libnsgif/nsgif.h
graphics/math-defs.h
graphics/math-extra.h
graphics/matrix3.h
+8
View File
@@ -67,6 +67,14 @@ set_source_files_properties(
PROPERTIES COMPILE_DEFINITIONS OBS_VERSION="${OBS_VERSION_CANONICAL}"
)
set_source_files_properties(
graphics/libnsgif/gif.c
graphics/libnsgif/lzw.c
graphics/libnsgif/lzw.h
graphics/libnsgif/nsgif.h
PROPERTIES COMPILE_OPTIONS "/wd4244;/wd4267"
)
target_link_libraries(
libobs
PRIVATE Avrt Dwmapi Dxgi winmm Rpcrt4 OBS::obfuscate OBS::winhandle OBS::COMutils
+118 -129
View File
@@ -20,9 +20,23 @@
#include "../util/platform.h"
#include "../util/dstr.h"
#include "vec4.h"
#include "libnsgif/nsgif.h"
#define blog(level, format, ...) blog(level, "%s: " format, __FUNCTION__, __VA_ARGS__)
struct gs_image_file_internal {
nsgif_t *gif;
const nsgif_info_t *gif_info;
const uint8_t *gif_frame_image;
nsgif_bitmap_cb_vt bitmap_callbacks;
uint8_t *gif_data;
uint8_t **animation_frame_cache;
uint8_t *animation_frame_data;
int last_decoded_frame;
};
static void *bi_def_bitmap_create(int width, int height)
{
return bmalloc((size_t)4 * width * height);
@@ -55,37 +69,40 @@ 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->gif.width * image->gif.height * 4 * image->gif.frame_count;
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;
gif_result result;
nsgif_error result;
uint64_t max_size;
size_t size, size_read;
FILE *file;
nsgif_bitmap_t *bitmap;
image->bitmap_callbacks.bitmap_create = bi_def_bitmap_create;
image->bitmap_callbacks.bitmap_destroy = bi_def_bitmap_destroy;
image->bitmap_callbacks.bitmap_get_buffer = bi_def_bitmap_get_buffer;
image->bitmap_callbacks.bitmap_modified = bi_def_bitmap_modified;
image->bitmap_callbacks.bitmap_set_opaque = bi_def_bitmap_set_opaque;
image->bitmap_callbacks.bitmap_test_opaque = bi_def_bitmap_test_opaque;
image->internal->bitmap_callbacks.create = bi_def_bitmap_create;
image->internal->bitmap_callbacks.destroy = bi_def_bitmap_destroy;
image->internal->bitmap_callbacks.get_buffer = bi_def_bitmap_get_buffer;
image->internal->bitmap_callbacks.modified = bi_def_bitmap_modified;
image->internal->bitmap_callbacks.set_opaque = bi_def_bitmap_set_opaque;
image->internal->bitmap_callbacks.test_opaque = bi_def_bitmap_test_opaque;
gif_create(&image->gif, &image->bitmap_callbacks);
nsgif_create(&image->internal->bitmap_callbacks, NSGIF_BITMAP_FMT_R8G8B8A8, &image->internal->gif);
file = os_fopen(path, "rb");
if (!file) {
@@ -97,15 +114,15 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t
size = (size_t)os_ftelli64(file);
fseek(file, 0, SEEK_SET);
image->gif_data = bmalloc(size);
size_read = fread(image->gif_data, 1, size, file);
image->internal->gif_data = bmalloc(size);
size_read = fread(image->internal->gif_data, 1, size, file);
if (size_read != size) {
blog(LOG_WARNING, "Failed to fully read gif file '%s'.", path);
goto fail;
}
do {
result = gif_initialise(&image->gif, size, image->gif_data);
result = nsgif_data_scan(image->internal->gif, size, image->internal->gif_data);
if (result < 0) {
blog(LOG_WARNING,
"Failed to initialize gif '%s', "
@@ -113,39 +130,45 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t
path);
goto fail;
}
} while (result != GIF_OK);
} while (result != NSGIF_OK);
if (image->gif.width > 4096 || image->gif.height > 4096) {
blog(LOG_WARNING, "Bad texture dimensions (%dx%d) in '%s'", image->gif.width, image->gif.height, path);
nsgif_data_complete(image->internal->gif);
image->internal->gif_info = nsgif_get_info(image->internal->gif);
if (image->internal->gif_info->width > 4096 || image->internal->gif_info->height > 4096) {
blog(LOG_WARNING, "Bad texture dimensions (%dx%d) in '%s'", image->internal->gif_info->width,
image->internal->gif_info->height, path);
goto fail;
}
max_size = (uint64_t)image->gif.width * (uint64_t)image->gif.height * (uint64_t)image->gif.frame_count * 4LLU;
max_size = (uint64_t)image->internal->gif_info->width * (uint64_t)image->internal->gif_info->height *
(uint64_t)image->internal->gif_info->frame_count * 4LLU;
if ((uint64_t)get_full_decoded_gif_size(image) != max_size) {
blog(LOG_WARNING, "Gif '%s' overflowed maximum pointer size", path);
goto fail;
}
image->is_animated_gif = (image->gif.frame_count > 1 && result >= 0);
image->is_animated_gif = (image->internal->gif_info->frame_count > 1 && result >= 0);
if (image->is_animated_gif) {
gif_decode_frame(&image->gif, 0);
nsgif_frame_decode(image->internal->gif, 0, &bitmap);
image->animation_frame_cache = alloc_mem(image, mem_usage, image->gif.frame_count * sizeof(uint8_t *));
image->animation_frame_data = alloc_mem(image, mem_usage, get_full_decoded_gif_size(image));
image->internal->animation_frame_cache =
alloc_mem(image, mem_usage, image->internal->gif_info->frame_count * sizeof(uint8_t *));
image->internal->animation_frame_data = alloc_mem(image, mem_usage, get_full_decoded_gif_size(image));
for (unsigned int i = 0; i < image->gif.frame_count; i++) {
if (gif_decode_frame(&image->gif, i) != GIF_OK)
blog(LOG_WARNING,
"Couldn't decode frame %u "
"of '%s'",
i, path);
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);
}
gif_decode_frame(&image->gif, 0);
nsgif_frame_decode(image->internal->gif, 0, &bitmap);
image->cx = (uint32_t)image->gif.width;
image->cy = (uint32_t)image->gif.height;
image->internal->gif_frame_image = (const uint8_t *)bitmap;
image->cx = (uint32_t)image->internal->gif_info->width;
image->cy = (uint32_t)image->internal->gif_info->height;
image->format = GS_RGBA;
if (mem_usage) {
@@ -154,14 +177,14 @@ static bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t
}
if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {
gs_premultiply_xyza_srgb_loop(image->gif.frame_image, (size_t)image->cx * image->cy);
gs_premultiply_xyza_srgb_loop((uint8_t *)bitmap, (size_t)image->cx * image->cy);
} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {
gs_premultiply_xyza_loop(image->gif.frame_image, (size_t)image->cx * image->cy);
gs_premultiply_xyza_loop((uint8_t *)bitmap, (size_t)image->cx * image->cy);
}
} else {
gif_finalise(&image->gif);
bfree(image->gif_data);
image->gif_data = NULL;
nsgif_destroy(image->internal->gif);
bfree(image->internal->gif_data);
image->internal->gif_data = NULL;
is_animated_gif = false;
goto not_animated;
}
@@ -170,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);
@@ -178,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;
@@ -191,6 +215,8 @@ static void gs_image_file_init_internal(gs_image_file_t *image, const char *file
if (!file)
return;
image->internal = bzalloc(sizeof(struct gs_image_file_internal));
len = strlen(file);
if (len > 4 && astrcmpi(file + len - 4, ".gif") == 0) {
@@ -209,64 +235,50 @@ 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;
if (image->loaded) {
if (image->is_animated_gif) {
gif_finalise(&image->gif);
bfree(image->animation_frame_cache);
bfree(image->animation_frame_data);
if (image->is_animated_gif && image->internal) {
nsgif_destroy(image->internal->gif);
bfree(image->internal->animation_frame_cache);
bfree(image->internal->animation_frame_data);
}
gs_texture_destroy(image->texture);
}
bfree(image->texture_data);
bfree(image->gif_data);
if (image->internal) {
bfree(image->internal->gif_data);
bfree(image->internal);
}
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;
if (!image->internal)
image->internal = bzalloc(sizeof(struct gs_image_file_internal));
if (image->is_animated_gif) {
image->texture = gs_texture_create(image->cx, image->cy, image->format, 1,
(const uint8_t **)&image->gif.frame_image, GS_DYNAMIC);
(const uint8_t **)&image->internal->gif_frame_image, GS_DYNAMIC);
} else {
image->texture = gs_texture_create(image->cx, image->cy, image->format, 1,
@@ -276,15 +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)
{
uint64_t val = (uint64_t)image->gif.frames[i].frame_delay * 10000000ULL;
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;
@@ -295,7 +309,7 @@ static inline int calculate_new_frame(gs_image_file_t *image, uint64_t elapsed_t
break;
image->cur_time -= t;
if ((unsigned int)++new_frame == image->gif.frame_count) {
if ((unsigned int)++new_frame == image->internal->gif_info->frame_count) {
if (!loops || ++image->cur_loop < loops) {
new_frame = 0;
} else if (image->cur_loop == loops) {
@@ -308,50 +322,55 @@ 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->animation_frame_cache[new_frame]) {
if (!image->internal->animation_frame_cache[new_frame]) {
int last_frame;
nsgif_bitmap_t *bitmap;
/* if looped, decode frame 0 */
last_frame = (new_frame < image->last_decoded_frame) ? 0 : image->last_decoded_frame + 1;
last_frame =
(new_frame < image->internal->last_decoded_frame) ? 0 : image->internal->last_decoded_frame + 1;
/* decode missed frames */
for (int i = last_frame; i < new_frame; i++) {
if (gif_decode_frame(&image->gif, i) != GIF_OK)
if (nsgif_frame_decode(image->internal->gif, i, &bitmap) != NSGIF_OK)
return;
}
/* decode actual desired frame */
if (gif_decode_frame(&image->gif, new_frame) == GIF_OK) {
const size_t area = (size_t)image->gif.width * image->gif.height;
if (nsgif_frame_decode(image->internal->gif, new_frame, &bitmap) == NSGIF_OK) {
const size_t area =
(size_t)image->internal->gif_info->width * image->internal->gif_info->height;
size_t pos = new_frame * area * 4;
image->animation_frame_cache[new_frame] = image->animation_frame_data + pos;
image->internal->animation_frame_cache[new_frame] = image->internal->animation_frame_data + pos;
if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {
gs_premultiply_xyza_srgb_loop(image->gif.frame_image, area);
gs_premultiply_xyza_srgb_loop((uint8_t *)bitmap, area);
} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {
gs_premultiply_xyza_loop(image->gif.frame_image, area);
gs_premultiply_xyza_loop((uint8_t *)bitmap, area);
}
memcpy(image->animation_frame_cache[new_frame], image->gif.frame_image, area * 4);
image->internal->gif_frame_image = (const uint8_t *)bitmap;
image->last_decoded_frame = new_frame;
memcpy(image->internal->animation_frame_cache[new_frame], (const uint8_t *)bitmap, area * 4);
image->internal->last_decoded_frame = new_frame;
}
}
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;
if (!image->is_animated_gif || !image->loaded)
if (!image->is_animated_gif || !image->loaded || !image->internal)
return false;
loops = image->gif.loop_count;
loops = image->internal->gif_info->loop_max;
if (loops >= 0xFFFF)
loops = 0;
@@ -367,54 +386,24 @@ 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)
static void gs_image_file_ex_update_texture_internal(gs_image_file_ex_t *image, enum gs_image_alpha_mode alpha_mode)
{
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)
{
if (!image->is_animated_gif || !image->loaded)
if (!image->is_animated_gif || !image->loaded || !image->internal)
return;
if (!image->animation_frame_cache[image->cur_frame])
if (!image->internal->animation_frame_cache[image->cur_frame])
decode_new_frame(image, image->cur_frame, alpha_mode);
gs_texture_set_image(image->texture, image->animation_frame_cache[image->cur_frame], image->gif.width * 4,
false);
gs_texture_set_image(image->texture, image->internal->animation_frame_cache[image->cur_frame],
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);
}
+9 -75
View File
@@ -18,13 +18,12 @@
#pragma once
#include "graphics.h"
#include "libnsgif/libnsgif.h"
#ifdef __cplusplus
extern "C" {
#endif
struct gs_image_file {
struct gs_image_file_ex {
gs_texture_t *texture;
enum gs_color_format format;
uint32_t cx;
@@ -33,91 +32,26 @@ struct gs_image_file {
bool frame_updated;
bool loaded;
gif_animation gif;
uint8_t *gif_data;
uint8_t **animation_frame_cache;
uint8_t *animation_frame_data;
struct gs_image_file_internal *internal;
uint64_t cur_time;
int cur_frame;
int cur_loop;
int last_decoded_frame;
uint8_t *texture_data;
gif_bitmap_callback_vt bitmap_callbacks;
};
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
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-142
View File
@@ -1,142 +0,0 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
/** \file
* Progressive animated GIF file decoding (interface).
*/
#ifndef _LIBNSGIF_H_
#define _LIBNSGIF_H_
#include <stdbool.h>
#include <inttypes.h>
#if defined(__cplusplus)
extern "C"
{
#endif
/* Error return values
*/
typedef enum {
GIF_WORKING = 1,
GIF_OK = 0,
GIF_INSUFFICIENT_FRAME_DATA = -1,
GIF_FRAME_DATA_ERROR = -2,
GIF_INSUFFICIENT_DATA = -3,
GIF_DATA_ERROR = -4,
GIF_INSUFFICIENT_MEMORY = -5,
GIF_FRAME_NO_DISPLAY = -6,
GIF_END_OF_FRAME = -7
} gif_result;
/* Maximum LZW bits available
*/
#define GIF_MAX_LZW 12
/* The GIF frame data
*/
typedef struct gif_frame {
bool display; /**< whether the frame should be displayed/animated */
unsigned int frame_delay; /**< delay (in 100th second intervals) before animating the frame */
/** Internal members are listed below
*/
unsigned int frame_pointer; /**< offset (in bytes) to the GIF frame data */
bool virgin; /**< whether the frame has previously been used */
bool opaque; /**< whether the frame is totally opaque */
bool redraw_required; /**< whether a forcable screen redraw is required */
unsigned char disposal_method; /**< how the previous frame should be disposed; affects plotting */
bool transparency; /**< whether we acknowledge transparency */
unsigned char transparency_index; /**< the index designating a transparent pixel */
unsigned int redraw_x; /**< x co-ordinate of redraw rectangle */
unsigned int redraw_y; /**< y co-ordinate of redraw rectangle */
unsigned int redraw_width; /**< width of redraw rectangle */
unsigned int redraw_height; /**< height of redraw rectangle */
} gif_frame;
/* API for Bitmap callbacks
*/
typedef void* (*gif_bitmap_cb_create)(int width, int height);
typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
typedef void (*gif_bitmap_cb_modified)(void *bitmap);
/* The Bitmap callbacks function table
*/
typedef struct gif_bitmap_callback_vt {
gif_bitmap_cb_create bitmap_create; /**< Create a bitmap. */
gif_bitmap_cb_destroy bitmap_destroy; /**< Free a bitmap. */
gif_bitmap_cb_get_buffer bitmap_get_buffer; /**< Return a pointer to the pixel data in a bitmap. */
/** Members below are optional
*/
gif_bitmap_cb_set_opaque bitmap_set_opaque; /**< Sets whether a bitmap should be plotted opaque. */
gif_bitmap_cb_test_opaque bitmap_test_opaque; /**< Tests whether a bitmap has an opaque alpha channel. */
gif_bitmap_cb_modified bitmap_modified; /**< The bitmap image has changed, so flush any persistent cache. */
} gif_bitmap_callback_vt;
/* The GIF animation data
*/
typedef struct gif_animation {
gif_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */
unsigned char *gif_data; /**< pointer to GIF data */
unsigned int width; /**< width of GIF (may increase during decoding) */
unsigned int height; /**< height of GIF (may increase during decoding) */
unsigned int frame_count; /**< number of frames decoded */
unsigned int frame_count_partial; /**< number of frames partially decoded */
gif_frame *frames; /**< decoded frames */
int decoded_frame; /**< current frame decoded to bitmap */
void *frame_image; /**< currently decoded image; stored as bitmap from bitmap_create callback */
int loop_count; /**< number of times to loop animation */
gif_result current_error; /**< current error type, or 0 for none*/
/** Internal members are listed below
*/
unsigned int buffer_position; /**< current index into GIF data */
unsigned int buffer_size; /**< total number of bytes of GIF data available */
unsigned int frame_holders; /**< current number of frame holders */
unsigned int background_index; /**< index in the colour table for the background colour */
unsigned int aspect_ratio; /**< image aspect ratio (ignored) */
unsigned int colour_table_size; /**< size of colour table (in entries) */
bool global_colours; /**< whether the GIF has a global colour table */
unsigned int *global_colour_table; /**< global colour table */
unsigned int *local_colour_table; /**< local colour table */
/* General LZW values. They are NO LONGER shared for all GIFs being decoded BECAUSE
THAT IS A TERRIBLE IDEA TO SAVE 10Kb or so per GIF.
*/
unsigned char buf[4];
unsigned char *direct;
int table[2][(1 << GIF_MAX_LZW)];
unsigned char stack[(1 << GIF_MAX_LZW) * 2];
unsigned char *stack_pointer;
int code_size, set_code_size;
int max_code, max_code_size;
int clear_code, end_code;
int curbit, lastbit, last_byte;
int firstcode, oldcode;
bool zero_data_block;
bool get_done;
/* Whether to clear the decoded image rather than plot
*/
bool clear_image;
} gif_animation;
void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
void gif_finalise(gif_animation *gif);
#if defined(__cplusplus)
};
#endif
#endif
+613
View File
@@ -0,0 +1,613 @@
/*
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
* Copyright 2021 Michael Drake <tlsa@netsurf-browser.org>
*/
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "lzw.h"
/**
* \file
* \brief LZW decompression (implementation)
*
* Decoder for GIF LZW data.
*/
/** Maximum number of lzw table entries. */
#define LZW_TABLE_ENTRY_MAX (1u << LZW_CODE_MAX)
/**
* Context for reading LZW data.
*
* LZW data is split over multiple sub-blocks. Each sub-block has a
* byte at the start, which says the sub-block size, and then the data.
* Zero-size sub-blocks have no data, and the biggest sub-block size is
* 255, which means there are 255 bytes of data following the sub-block
* size entry.
*
* Note that an individual LZW code can be split over up to three sub-blocks.
*/
struct lzw_read_ctx {
const uint8_t *restrict data; /**< Pointer to start of input data */
size_t data_len; /**< Input data length */
size_t data_sb_next; /**< Offset to sub-block size */
const uint8_t *sb_data; /**< Pointer to current sub-block in data */
size_t sb_bit; /**< Current bit offset in sub-block */
uint32_t sb_bit_count; /**< Bit count in sub-block */
};
/**
* LZW table entry.
*
* Records in the table are composed of 1 or more entries.
* Entries refer to the entry they extend which can be followed to compose
* the complete record. To compose the record in reverse order, take
* the `value` from each entry, and move to the entry it extends.
* If the extended entries index is < the current clear_code, then it
* is the last entry in the record.
*/
struct lzw_table_entry {
uint8_t value; /**< Last value for record ending at entry. */
uint8_t first; /**< First value in entry's entire record. */
uint16_t count; /**< Count of values in this entry's record. */
uint16_t extends; /**< Offset in table to previous entry. */
};
/**
* LZW decompression context.
*/
struct lzw_ctx {
struct lzw_read_ctx input; /**< Input reading context */
uint16_t prev_code; /**< Code read from input previously. */
uint16_t prev_code_first; /**< First value of previous code. */
uint16_t prev_code_count; /**< Total values for previous code. */
uint8_t initial_code_size; /**< Starting LZW code size. */
uint8_t code_size; /**< Current LZW code size. */
uint16_t code_max; /**< Max code value for current code size. */
uint16_t clear_code; /**< Special Clear code value */
uint16_t eoi_code; /**< Special End of Information code value */
uint16_t table_size; /**< Next position in table to fill. */
uint16_t output_code; /**< Code that has been partially output. */
uint16_t output_left; /**< Number of values left for output_code. */
bool has_transparency; /**< Whether the image is opaque. */
uint8_t transparency_idx; /**< Index representing transparency. */
const uint32_t *restrict colour_map; /**< Index to colour mapping. */
/** LZW code table. Generated during decode. */
struct lzw_table_entry table[LZW_TABLE_ENTRY_MAX];
/** Output value stack. */
uint8_t stack_base[LZW_TABLE_ENTRY_MAX];
};
/* Exported function, documented in lzw.h */
lzw_result lzw_context_create(struct lzw_ctx **ctx)
{
struct lzw_ctx *c = malloc(sizeof(*c));
if (c == NULL) {
return LZW_NO_MEM;
}
*ctx = c;
return LZW_OK;
}
/* Exported function, documented in lzw.h */
void lzw_context_destroy(struct lzw_ctx *ctx)
{
free(ctx);
}
/**
* Advance the context to the next sub-block in the input data.
*
* \param[in] ctx LZW reading context, updated on success.
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
*/
static lzw_result lzw__block_advance(struct lzw_read_ctx *restrict ctx)
{
size_t block_size;
size_t next_block_pos = ctx->data_sb_next;
const uint8_t *data_next = ctx->data + next_block_pos;
if (next_block_pos >= ctx->data_len) {
return LZW_NO_DATA;
}
block_size = *data_next;
if ((next_block_pos + block_size) >= ctx->data_len) {
return LZW_NO_DATA;
}
ctx->sb_bit = 0;
ctx->sb_bit_count = block_size * 8;
if (block_size == 0) {
ctx->data_sb_next += 1;
return LZW_OK_EOD;
}
ctx->sb_data = data_next + 1;
ctx->data_sb_next += block_size + 1;
return LZW_OK;
}
/**
* Get the next LZW code of given size from the raw input data.
*
* Reads codes from the input data stream coping with GIF data sub-blocks.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] code_size Size of LZW code to get from data.
* \param[out] code_out Returns an LZW code on success.
* \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
*/
static inline lzw_result lzw__read_code(
struct lzw_read_ctx *restrict ctx,
uint16_t code_size,
uint16_t *restrict code_out)
{
uint32_t code = 0;
uint32_t current_bit = ctx->sb_bit & 0x7;
if (ctx->sb_bit + 24 <= ctx->sb_bit_count) {
/* Fast path: read three bytes from this sub-block */
const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
code |= *data++ << 0;
code |= *data++ << 8;
code |= *data << 16;
ctx->sb_bit += code_size;
} else {
/* Slow path: code spans sub-blocks */
uint8_t byte_advance = (current_bit + code_size) >> 3;
uint8_t byte = 0;
uint8_t bits_remaining_0 = (code_size < (8u - current_bit)) ?
code_size : (8u - current_bit);
uint8_t bits_remaining_1 = code_size - bits_remaining_0;
uint8_t bits_used[3] = {
[0] = bits_remaining_0,
[1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
[2] = bits_remaining_1 - 8,
};
assert(byte_advance <= 2);
while (true) {
const uint8_t *data = ctx->sb_data;
lzw_result res;
/* Get any data from end of this sub-block */
while (byte <= byte_advance &&
ctx->sb_bit < ctx->sb_bit_count) {
code |= data[ctx->sb_bit >> 3] << (byte << 3);
ctx->sb_bit += bits_used[byte];
byte++;
}
/* Check if we have all we need */
if (byte > byte_advance) {
break;
}
/* Move to next sub-block */
res = lzw__block_advance(ctx);
if (res != LZW_OK) {
return res;
}
}
}
*code_out = (code >> current_bit) & ((1 << code_size) - 1);
return LZW_OK;
}
/**
* Handle clear code.
*
* \param[in] ctx LZW reading context, updated.
* \param[out] code_out Returns next code after a clear code.
* \return LZW_OK or error code.
*/
static inline lzw_result lzw__handle_clear(
struct lzw_ctx *ctx,
uint16_t *code_out)
{
uint16_t code;
/* Reset table building context */
ctx->code_size = ctx->initial_code_size;
ctx->code_max = (1 << ctx->initial_code_size) - 1;
ctx->table_size = ctx->eoi_code + 1;
/* There might be a sequence of clear codes, so process them all */
do {
lzw_result res = lzw__read_code(&ctx->input,
ctx->code_size, &code);
if (res != LZW_OK) {
return res;
}
} while (code == ctx->clear_code);
/* The initial code must be from the initial table. */
if (code > ctx->clear_code) {
return LZW_BAD_ICODE;
}
*code_out = code;
return LZW_OK;
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode_init(
struct lzw_ctx *ctx,
uint8_t minimum_code_size,
const uint8_t *input_data,
size_t input_length,
size_t input_pos)
{
struct lzw_table_entry *table = ctx->table;
lzw_result res;
uint16_t code;
if (minimum_code_size >= LZW_CODE_MAX) {
return LZW_BAD_ICODE;
}
/* Initialise the input reading context */
ctx->input.data = input_data;
ctx->input.data_len = input_length;
ctx->input.data_sb_next = input_pos;
ctx->input.sb_bit = 0;
ctx->input.sb_bit_count = 0;
/* Initialise the table building context */
ctx->initial_code_size = minimum_code_size + 1;
ctx->clear_code = (1 << minimum_code_size) + 0;
ctx->eoi_code = (1 << minimum_code_size) + 1;
ctx->output_left = 0;
/* Initialise the standard table entries */
for (uint16_t i = 0; i < ctx->clear_code; i++) {
table[i].first = i;
table[i].value = i;
table[i].count = 1;
}
res = lzw__handle_clear(ctx, &code);
if (res != LZW_OK) {
return res;
}
/* Store details of this code as "previous code" to the context. */
ctx->prev_code_first = ctx->table[code].first;
ctx->prev_code_count = ctx->table[code].count;
ctx->prev_code = code;
/* Add code to context for immediate output. */
ctx->output_code = code;
ctx->output_left = 1;
ctx->has_transparency = false;
ctx->transparency_idx = 0;
ctx->colour_map = NULL;
return LZW_OK;
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode_init_map(
struct lzw_ctx *ctx,
uint8_t minimum_code_size,
uint32_t transparency_idx,
const uint32_t *colour_table,
const uint8_t *input_data,
size_t input_length,
size_t input_pos)
{
lzw_result res;
if (colour_table == NULL) {
return LZW_BAD_PARAM;
}
res = lzw_decode_init(ctx, minimum_code_size,
input_data, input_length, input_pos);
if (res != LZW_OK) {
return res;
}
ctx->has_transparency = (transparency_idx <= 0xFF);
ctx->transparency_idx = transparency_idx;
ctx->colour_map = colour_table;
return LZW_OK;
}
/**
* Create new table entry.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] code Last value code for new table entry.
*/
static inline void lzw__table_add_entry(
struct lzw_ctx *ctx,
uint16_t code)
{
struct lzw_table_entry *entry = &ctx->table[ctx->table_size];
entry->value = code;
entry->first = ctx->prev_code_first;
entry->count = ctx->prev_code_count + 1;
entry->extends = ctx->prev_code;
ctx->table_size++;
}
typedef uint32_t (*lzw_writer_fn)(
struct lzw_ctx *ctx,
void *restrict output_data,
uint32_t output_length,
uint32_t output_pos,
uint16_t code,
uint16_t left);
/**
* Get the next LZW code and write its value(s) to output buffer.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] write_fn Function for writing pixels to output.
* \param[in] output_data Array to write output values into.
* \param[in] output_length Size of output array.
* \param[in,out] output_written Number of values written. Updated on exit.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
static inline lzw_result lzw__decode(
struct lzw_ctx *ctx,
lzw_writer_fn write_fn,
void *restrict output_data,
uint32_t output_length,
uint32_t *restrict output_written)
{
lzw_result res;
uint16_t code;
/* Get a new code from the input */
res = lzw__read_code(&ctx->input, ctx->code_size, &code);
if (res != LZW_OK) {
return res;
}
/* Handle the new code */
if (code == ctx->eoi_code) {
/* Got End of Information code */
return LZW_EOI_CODE;
} else if (code > ctx->table_size) {
/* Code is invalid */
return LZW_BAD_CODE;
} else if (code == ctx->clear_code) {
res = lzw__handle_clear(ctx, &code);
if (res != LZW_OK) {
return res;
}
} else if (ctx->table_size < LZW_TABLE_ENTRY_MAX) {
uint16_t size = ctx->table_size;
lzw__table_add_entry(ctx, (code < size) ?
ctx->table[code].first :
ctx->prev_code_first);
/* Ensure code size is increased, if needed. */
if (size == ctx->code_max && ctx->code_size < LZW_CODE_MAX) {
ctx->code_size++;
ctx->code_max = (1 << ctx->code_size) - 1;
}
}
*output_written += write_fn(ctx,
output_data, output_length, *output_written,
code, ctx->table[code].count);
/* Store details of this code as "previous code" to the context. */
ctx->prev_code_first = ctx->table[code].first;
ctx->prev_code_count = ctx->table[code].count;
ctx->prev_code = code;
return LZW_OK;
}
/**
* Write values for this code to the output stack.
*
* If there isn't enough space in the output stack, this function will write
* the as many as it can into the output. If `ctx->output_left > 0` after
* this call, then there is more data for this code left to output. The code
* is stored to the context as `ctx->output_code`.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] output_data Array to write output values into.
* \param[in] output_length length Size of output array.
* \param[in] output_used Current position in output array.
* \param[in] code LZW code to output values for.
* \param[in] left Number of values remaining to output for this code.
* \return Number of pixel values written.
*/
static inline uint32_t lzw__write_fn(struct lzw_ctx *ctx,
void *restrict output_data,
uint32_t output_length,
uint32_t output_used,
uint16_t code,
uint16_t left)
{
uint8_t *restrict output_pos = (uint8_t *)output_data + output_used;
const struct lzw_table_entry * const table = ctx->table;
uint32_t space = output_length - output_used;
uint16_t count = left;
if (count > space) {
left = count - space;
count = space;
} else {
left = 0;
}
ctx->output_code = code;
ctx->output_left = left;
/* Skip over any values we don't have space for. */
for (unsigned i = left; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
code = entry->extends;
}
output_pos += count;
for (unsigned i = count; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
*--output_pos = entry->value;
code = entry->extends;
}
return count;
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode(struct lzw_ctx *ctx,
const uint8_t *restrict *const restrict output_data,
uint32_t *restrict output_written)
{
const uint32_t output_length = sizeof(ctx->stack_base);
*output_written = 0;
*output_data = ctx->stack_base;
if (ctx->output_left != 0) {
*output_written += lzw__write_fn(ctx,
ctx->stack_base, output_length, *output_written,
ctx->output_code, ctx->output_left);
}
while (*output_written != output_length) {
lzw_result res = lzw__decode(ctx, lzw__write_fn,
ctx->stack_base, output_length, output_written);
if (res != LZW_OK) {
return res;
}
}
return LZW_OK;
}
/**
* Write colour mapped values for this code to the output.
*
* If there isn't enough space in the output stack, this function will write
* the as many as it can into the output. If `ctx->output_left > 0` after
* this call, then there is more data for this code left to output. The code
* is stored to the context as `ctx->output_code`.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] output_data Array to write output values into.
* \param[in] output_length Size of output array.
* \param[in] output_used Current position in output array.
* \param[in] code LZW code to output values for.
* \param[in] left Number of values remaining to output for code.
* \return Number of pixel values written.
*/
static inline uint32_t lzw__map_write_fn(struct lzw_ctx *ctx,
void *restrict output_data,
uint32_t output_length,
uint32_t output_used,
uint16_t code,
uint16_t left)
{
uint32_t *restrict output_pos = (uint32_t *)output_data + output_used;
const struct lzw_table_entry * const table = ctx->table;
uint32_t space = output_length - output_used;
uint16_t count = left;
if (count > space) {
left = count - space;
count = space;
} else {
left = 0;
}
ctx->output_code = code;
ctx->output_left = left;
for (unsigned i = left; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
code = entry->extends;
}
output_pos += count;
if (ctx->has_transparency) {
for (unsigned i = count; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
--output_pos;
if (entry->value != ctx->transparency_idx) {
*output_pos = ctx->colour_map[entry->value];
}
code = entry->extends;
}
} else {
for (unsigned i = count; i != 0; i--) {
const struct lzw_table_entry *entry = table + code;
*--output_pos = ctx->colour_map[entry->value];
code = entry->extends;
}
}
return count;
}
/* Exported function, documented in lzw.h */
lzw_result lzw_decode_map(struct lzw_ctx *ctx,
uint32_t *restrict output_data,
uint32_t output_length,
uint32_t *restrict output_written)
{
*output_written = 0;
if (ctx->colour_map == NULL) {
return LZW_NO_COLOUR;
}
if (ctx->output_left != 0) {
*output_written += lzw__map_write_fn(ctx,
output_data, output_length, *output_written,
ctx->output_code, ctx->output_left);
}
while (*output_written != output_length) {
lzw_result res = lzw__decode(ctx, lzw__map_write_fn,
output_data, output_length, output_written);
if (res != LZW_OK) {
return res;
}
}
return LZW_OK;
}
+137
View File
@@ -0,0 +1,137 @@
/*
* This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
* Licensed under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
* Copyright 2021 Michael Drake <tlsa@netsurf-browser.org>
*/
#ifndef LZW_H_
#define LZW_H_
/**
* \file
* \brief LZW decompression (interface)
*
* Decoder for GIF LZW data.
*/
/** Maximum LZW code size in bits */
#define LZW_CODE_MAX 12
/* Declare lzw internal context structure */
struct lzw_ctx;
/** LZW decoding response codes */
typedef enum lzw_result {
LZW_OK, /**< Success */
LZW_OK_EOD, /**< Success; reached zero-length sub-block */
LZW_NO_MEM, /**< Error: Out of memory */
LZW_NO_DATA, /**< Error: Out of data */
LZW_EOI_CODE, /**< Error: End of Information code */
LZW_NO_COLOUR, /**< Error: No colour map provided. */
LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
LZW_BAD_PARAM, /**< Error: Bad function parameter. */
LZW_BAD_CODE, /**< Error: Bad LZW code */
} lzw_result;
/**
* Create an LZW decompression context.
*
* \param[out] ctx Returns an LZW decompression context. Caller owned,
* free with lzw_context_destroy().
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_context_create(
struct lzw_ctx **ctx);
/**
* Destroy an LZW decompression context.
*
* \param[in] ctx The LZW decompression context to destroy.
*/
void lzw_context_destroy(
struct lzw_ctx *ctx);
/**
* Initialise an LZW decompression context for decoding.
*
* \param[in] ctx The LZW decompression context to initialise.
* \param[in] minimum_code_size The LZW Minimum Code Size.
* \param[in] input_data The compressed data.
* \param[in] input_length Byte length of compressed data.
* \param[in] input_pos Start position in data. Must be position
* of a size byte at sub-block start.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_init(
struct lzw_ctx *ctx,
uint8_t minimum_code_size,
const uint8_t *input_data,
size_t input_length,
size_t input_pos);
/**
* Read input codes until end of LZW context owned output buffer.
*
* Ensure anything in output is used before calling this, as anything
* there before this call will be trampled.
*
* \param[in] ctx LZW reading context, updated.
* \param[out] output_data Returns pointer to array of output values.
* \param[out] output_written Returns the number of values written to data.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode(struct lzw_ctx *ctx,
const uint8_t *restrict *const restrict output_data,
uint32_t *restrict output_written);
/**
* Initialise an LZW decompression context for decoding to colour map values.
*
* For transparency to work correctly, the given client buffer must have
* the values from the previous frame. The transparency_idx should be a value
* of 256 or above, if the frame does not have transparency.
*
* \param[in] ctx The LZW decompression context to initialise.
* \param[in] minimum_code_size The LZW Minimum Code Size.
* \param[in] transparency_idx Index representing transparency.
* \param[in] colour_table Index to pixel colour mapping.
* \param[in] input_data The compressed data.
* \param[in] input_length Byte length of compressed data.
* \param[in] input_pos Start position in data. Must be position
* of a size byte at sub-block start.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_init_map(
struct lzw_ctx *ctx,
uint8_t minimum_code_size,
uint32_t transparency_idx,
const uint32_t *colour_table,
const uint8_t *input_data,
size_t input_length,
size_t input_pos);
/**
* Read LZW codes into client buffer, mapping output to colours.
*
* The context must have been initialised using \ref lzw_decode_init_map
* before calling this function, in order to provide the colour mapping table
* and any transparency index.
*
* Ensure anything in output is used before calling this, as anything
* there before this call will be trampled.
*
* \param[in] ctx LZW reading context, updated.
* \param[in] output_data Client buffer to fill with colour mapped values.
* \param[in] output_length Size of output array.
* \param[out] output_written Returns the number of values written to data.
* \return LZW_OK on success, or appropriate error code otherwise.
*/
lzw_result lzw_decode_map(struct lzw_ctx *ctx,
uint32_t *restrict output_data,
uint32_t output_length,
uint32_t *restrict output_written);
#endif
+527
View File
@@ -0,0 +1,527 @@
/*
* Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
* Copyright 2008 Sean Fox <dyntryx@gmail.com>
* Copyright 2013-2022 Michael Drake <tlsa@netsurf-browser.org>
*
* This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*/
/**
* \file
* Interface to progressive animated GIF file decoding.
*/
#ifndef NSNSGIF_H
#define NSNSGIF_H
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
/** Representation of infinity. */
#define NSGIF_INFINITE (UINT32_MAX)
/** Maximum colour table size */
#define NSGIF_MAX_COLOURS 256
/**
* Opaque type used by LibNSGIF to represent a GIF object in memory.
*/
typedef struct nsgif nsgif_t;
/**
* LibNSGIF rectangle structure.
*
* * Top left coordinate is `(x0, y0)`.
* * Width is `x1 - x0`.
* * Height is `y1 - y0`.
* * Units are pixels.
*/
typedef struct nsgif_rect {
/** x co-ordinate of redraw rectangle, left */
uint32_t x0;
/** y co-ordinate of redraw rectangle, top */
uint32_t y0;
/** x co-ordinate of redraw rectangle, right */
uint32_t x1;
/** y co-ordinate of redraw rectangle, bottom */
uint32_t y1;
} nsgif_rect_t;
/**
* LibNSGIF return codes.
*/
typedef enum {
/**
* Success.
*/
NSGIF_OK,
/**
* Out of memory error.
*/
NSGIF_ERR_OOM,
/**
* GIF source data is invalid, and no frames are recoverable.
*/
NSGIF_ERR_DATA,
/**
* Frame number is not valid.
*/
NSGIF_ERR_BAD_FRAME,
/**
* GIF source data contained an error in a frame.
*/
NSGIF_ERR_DATA_FRAME,
/**
* Unexpected end of GIF source data.
*/
NSGIF_ERR_END_OF_DATA,
/**
* Can't supply more data after calling \ref nsgif_data_complete.
*/
NSGIF_ERR_DATA_COMPLETE,
/**
* The current frame cannot be displayed.
*/
NSGIF_ERR_FRAME_DISPLAY,
/**
* Indicates an animation is complete, and \ref nsgif_reset must be
* called to restart the animation from the beginning.
*/
NSGIF_ERR_ANIMATION_END,
} nsgif_error;
/**
* NSGIF \ref nsgif_bitmap_t pixel format.
*
* All pixel formats are 32 bits per pixel (bpp). The different formats
* allow control over the ordering of the colour channels. All colour
* channels are 8 bits wide.
*
* Note that the GIF file format only supports an on/off mask, so the
* alpha (A) component (opacity) will always have a value of `0` (fully
* transparent) or `255` (fully opaque).
*/
typedef enum nsgif_bitmap_fmt {
/** Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA. */
NSGIF_BITMAP_FMT_R8G8B8A8,
/** Bite-wise BGRA: Byte order: 0xBB, 0xGG, 0xRR, 0xAA. */
NSGIF_BITMAP_FMT_B8G8R8A8,
/** Bite-wise ARGB: Byte order: 0xAA, 0xRR, 0xGG, 0xBB. */
NSGIF_BITMAP_FMT_A8R8G8B8,
/** Bite-wise ABGR: Byte order: 0xAA, 0xBB, 0xGG, 0xRR. */
NSGIF_BITMAP_FMT_A8B8G8R8,
/**
* 32-bit RGBA (0xRRGGBBAA).
*
* * On little endian host, same as \ref NSGIF_BITMAP_FMT_A8B8G8R8.
* * On big endian host, same as \ref NSGIF_BITMAP_FMT_R8G8B8A8.
*/
NSGIF_BITMAP_FMT_RGBA8888,
/**
* 32-bit BGRA (0xBBGGRRAA).
*
* * On little endian host, same as \ref NSGIF_BITMAP_FMT_A8R8G8B8.
* * On big endian host, same as \ref NSGIF_BITMAP_FMT_B8G8R8A8.
*/
NSGIF_BITMAP_FMT_BGRA8888,
/**
* 32-bit ARGB (0xAARRGGBB).
*
* * On little endian host, same as \ref NSGIF_BITMAP_FMT_B8G8R8A8.
* * On big endian host, same as \ref NSGIF_BITMAP_FMT_A8R8G8B8.
*/
NSGIF_BITMAP_FMT_ARGB8888,
/**
* 32-bit BGRA (0xAABBGGRR).
*
* * On little endian host, same as \ref NSGIF_BITMAP_FMT_R8G8B8A8.
* * On big endian host, same as \ref NSGIF_BITMAP_FMT_A8B8G8R8.
*/
NSGIF_BITMAP_FMT_ABGR8888,
} nsgif_bitmap_fmt_t;
/**
* Client bitmap type.
*
* These are client-created and destroyed, via the \ref nsgif_bitmap_cb_vt
* callbacks, but they are owned by a \ref nsgif_t.
*
* See \ref nsgif_bitmap_fmt for pixel format information.
*
* The bitmap may have a row_span greater than the bitmap width, but the
* difference between row span and width must be a whole number of pixels
* (a multiple of four bytes).
*/
typedef void nsgif_bitmap_t;
/** Bitmap callbacks function table */
typedef struct nsgif_bitmap_cb_vt {
/**
* Callback to create a bitmap with the given dimensions.
*
* \param[in] width Required bitmap width in pixels.
* \param[in] height Required bitmap height in pixels.
* \return pointer to client's bitmap structure or NULL on error.
*/
nsgif_bitmap_t* (*create)(int width, int height);
/**
* Callback to free a bitmap.
*
* \param[in] bitmap The bitmap to destroy.
*/
void (*destroy)(nsgif_bitmap_t *bitmap);
/**
* Get pointer to pixel buffer in a bitmap.
*
* The pixel buffer must be `(width + N) * height * sizeof(uint32_t)`.
* Where `N` is any number greater than or equal to 0.
* Note that the returned pointer to uint8_t must be 4-byte aligned.
*
* \param[in] bitmap The bitmap.
* \return pointer to bitmap's pixel buffer.
*/
uint8_t* (*get_buffer)(nsgif_bitmap_t *bitmap);
/* The following functions are optional. */
/**
* Set whether a bitmap can be plotted opaque.
*
* \param[in] bitmap The bitmap.
* \param[in] opaque Whether the current frame is opaque.
*/
void (*set_opaque)(nsgif_bitmap_t *bitmap, bool opaque);
/**
* Tests whether a bitmap has an opaque alpha channel.
*
* \param[in] bitmap The bitmap.
* \return true if the bitmap is opaque, false otherwise.
*/
bool (*test_opaque)(nsgif_bitmap_t *bitmap);
/**
* Bitmap modified notification.
*
* \param[in] bitmap The bitmap.
*/
void (*modified)(nsgif_bitmap_t *bitmap);
/**
* Get row span in pixels.
*
* If this callback is not provided, LibNSGIF will use the width.
*
* If row span is greater than width, this callback must be provided.
*
* \param[in] bitmap The bitmap.
*/
uint32_t (*get_rowspan)(nsgif_bitmap_t *bitmap);
} nsgif_bitmap_cb_vt;
/**
* Convert an error code to a string.
*
* \param[in] err The error code to convert.
* \return String representation of given error code.
*/
const char *nsgif_strerror(nsgif_error err);
/**
* Create the NSGIF object.
*
* \param[in] bitmap_vt Bitmap operation functions v-table.
* \param[in] bitmap_fmt Bitmap pixel format specification.
* \param[out] gif_out Return \ref nsgif_t object on success.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_create(
const nsgif_bitmap_cb_vt *bitmap_vt,
nsgif_bitmap_fmt_t bitmap_fmt,
nsgif_t **gif_out);
/**
* Free a NSGIF object.
*
* \param[in] gif The NSGIF to free.
*/
void nsgif_destroy(nsgif_t *gif);
/**
* Scan the source image data.
*
* This is used to feed the source data into LibNSGIF. This must be called
* before calling \ref nsgif_frame_decode.
*
* It can be called multiple times with, with increasing sizes. If it is called
* several times, as more data is available (e.g. slow network fetch) the data
* already given to \ref nsgif_data_scan must be provided each time.
*
* Once all the data has been provided, call \ref nsgif_data_complete.
*
* For example, if you call \ref nsgif_data_scan with 25 bytes of data, and then
* fetch another 10 bytes, you would need to call \ref nsgif_data_scan with a
* size of 35 bytes, and the whole 35 bytes must be contiguous memory. It is
* safe to `realloc` the source buffer between calls to \ref nsgif_data_scan.
* (The actual data pointer is allowed to be different.)
*
* If an error occurs, all previously scanned frames are retained.
*
* Note that an error returned from this function is purely informational.
* So long as at least one frame is available, you can display frames.
*
* \param[in] gif The \ref nsgif_t object.
* \param[in] size Number of bytes in data.
* \param[in] data Raw source GIF data.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_data_scan(
nsgif_t *gif,
size_t size,
const uint8_t *data);
/**
* Tell libnsgif that all the gif data has been provided.
*
* Call this after calling \ref nsgif_data_scan with the the entire GIF
* source data. You can call \ref nsgif_data_scan multiple times up until
* this is called, and after this is called, \ref nsgif_data_scan will
* return an error.
*
* You can decode a GIF before this is called, however, it will fail to
* decode any truncated final frame data and will not perform loops when
* driven via \ref nsgif_frame_prepare (because it doesn't know if there
* will be more frames supplied in future data).
*
* \param[in] gif The \ref nsgif_t object.
*/
void nsgif_data_complete(
nsgif_t *gif);
/**
* Prepare to show a frame.
*
* If this is the last frame of an animation with a finite loop count, the
* returned `delay_cs` will be \ref NSGIF_INFINITE, indicating that the frame
* should be shown forever.
*
* Note that if \ref nsgif_data_complete has not been called on this gif,
* animated GIFs will not loop back to the start. Instead it will return
* \ref NSGIF_ERR_END_OF_DATA.
*
* \param[in] gif The \ref nsgif_t object.
* \param[out] area The area in pixels that must be redrawn.
* \param[out] delay_cs Time to wait after frame_new before next frame in cs.
* \param[out] frame_new The frame to decode.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_frame_prepare(
nsgif_t *gif,
nsgif_rect_t *area,
uint32_t *delay_cs,
uint32_t *frame_new);
/**
* Decodes a GIF frame.
*
* \param[in] gif The \ref nsgif_t object.
* \param[in] frame The frame number to decode.
* \param[out] bitmap On success, returns pointer to the client-allocated,
* nsgif-owned client bitmap structure.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_frame_decode(
nsgif_t *gif,
uint32_t frame,
nsgif_bitmap_t **bitmap);
/**
* Reset a GIF animation.
*
* Some animations are only meant to loop N times, and then show the
* final frame forever. This function resets the loop and frame counters,
* so that the animation can be replayed without the overhead of recreating
* the \ref nsgif_t object and rescanning the raw data.
*
* \param[in] gif A \ref nsgif_t object.
*
* \return NSGIF_OK on success, or appropriate error otherwise.
*/
nsgif_error nsgif_reset(
nsgif_t *gif);
/**
* Information about a GIF.
*/
typedef struct nsgif_info {
/** width of GIF (may increase during decoding) */
uint32_t width;
/** height of GIF (may increase during decoding) */
uint32_t height;
/** number of frames decoded */
uint32_t frame_count;
/** number of times to play animation (zero means loop forever) */
int loop_max;
/** background colour in same pixel format as \ref nsgif_bitmap_t. */
uint32_t background;
/** whether the GIF has a global colour table */
bool global_palette;
} nsgif_info_t;
/**
* Frame disposal method.
*
* Clients do not need to know about this, it is provided purely for dumping
* raw information about GIF frames.
*/
enum nsgif_disposal {
NSGIF_DISPOSAL_UNSPECIFIED, /**< No disposal method specified. */
NSGIF_DISPOSAL_NONE, /**< Frame remains. */
NSGIF_DISPOSAL_RESTORE_BG, /**< Clear frame to background colour. */
NSGIF_DISPOSAL_RESTORE_PREV, /**< Restore previous frame. */
NSGIF_DISPOSAL_RESTORE_QUIRK, /**< Alias for NSGIF_DISPOSAL_RESTORE_PREV. */
};
/**
* Convert a disposal method to a string.
*
* \param[in] disposal The disposal method to convert.
* \return String representation of given disposal method.
*/
const char *nsgif_str_disposal(enum nsgif_disposal disposal);
/**
* Information about a GIF frame.
*/
typedef struct nsgif_frame_info {
/** whether the frame should be displayed/animated */
bool display;
/** whether the frame may have transparency */
bool transparency;
/** whether the frame has a local colour table */
bool local_palette;
/** whether the frame is interlaced */
bool interlaced;
/** Disposal method for previous frame; affects plotting */
uint8_t disposal;
/** delay (in cs) before animating the frame */
uint32_t delay;
/** Frame's redraw rectangle. */
nsgif_rect_t rect;
} nsgif_frame_info_t;
/**
* Get information about a GIF from an \ref nsgif_t object.
*
* \param[in] gif The \ref nsgif_t object to get info for.
*
* \return The gif info, or NULL on error.
*/
const nsgif_info_t *nsgif_get_info(const nsgif_t *gif);
/**
* Get information about a GIF from an \ref nsgif_t object.
*
* \param[in] gif The \ref nsgif_t object to get frame info for.
* \param[in] frame The frame number to get info for.
*
* \return The gif frame info, or NULL on error.
*/
const nsgif_frame_info_t *nsgif_get_frame_info(
const nsgif_t *gif,
uint32_t frame);
/**
* Get the global colour palette.
*
* If the GIF has no global colour table, this will return the default
* colour palette.
*
* Colours in same pixel format as \ref nsgif_bitmap_t.
*
* \param[in] gif The \ref nsgif_t object.
* \param[out] table Client buffer to hold the colour table.
* \param[out] entries The number of used entries in the colour table.
*/
void nsgif_global_palette(
const nsgif_t *gif,
uint32_t table[NSGIF_MAX_COLOURS],
size_t *entries);
/**
* Get the local colour palette for a frame.
*
* Frames may have no local palette. In this case they use the global palette.
* This function returns false if the frame has no local palette.
*
* Colours in same pixel format as \ref nsgif_bitmap_t.
*
* \param[in] gif The \ref nsgif_t object.
* \param[in] frame The frame to get the palette for.
* \param[out] table Client buffer to hold the colour table.
* \param[out] entries The number of used entries in the colour table.
* \return true if a palette is returned, false otherwise.
*/
bool nsgif_local_palette(
const nsgif_t *gif,
uint32_t frame,
uint32_t table[NSGIF_MAX_COLOURS],
size_t *entries);
/**
* Configure handling of small frame delays.
*
* Historically people created GIFs with a tiny frame delay, however the slow
* hardware of the time meant they actually played much slower. As computers
* sped up, to prevent animations playing faster than intended, decoders came
* to ignore overly small frame delays.
*
* By default a \ref nsgif_frame_prepare() managed animation will override
* frame delays of less than 2 centiseconds with a default frame delay of
* 10 centiseconds. This matches the behaviour of web browsers and other
* renderers.
*
* Both the minimum and the default values can be overridden for a given GIF
* by the client. To get frame delays exactly as specified by the GIF file, set
* `delay_min` to zero.
*
* Note that this does not affect the frame delay in the frame info
* (\ref nsgif_frame_info_t) structure, which will always contain values
* specified by the GIF.
*
* \param[in] gif The \ref nsgif_t object to configure.
* \param[in] delay_min The minimum frame delay in centiseconds.
* \param[in] delay_default The delay to use if a frame delay is less than
* `delay_min`.
*/
void nsgif_set_frame_delay_behaviour(
nsgif_t *gif,
uint16_t delay_min,
uint16_t delay_default);
#endif
+26 -21
View File
@@ -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 = {
+4 -4
View File
@@ -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);
+10 -7
View File
@@ -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;
@@ -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);