diff options
author | Harry Jeffery <harry@exec64.co.uk> | 2019-08-30 00:40:29 +0100 |
---|---|---|
committer | Harry Jeffery <harry@exec64.co.uk> | 2019-08-30 00:40:29 +0100 |
commit | 8b2aaf102f76921721209459eea10bed2094a730 (patch) | |
tree | 6942c0e840252e8832aa0a8276435045525a40b4 /src | |
parent | 4c0123bb3b449a2ea45b3e831db725d853bb03a1 (diff) | |
download | imv-8b2aaf102f76921721209459eea10bed2094a730.tar.gz |
source: Refactor out common async logic
Diffstat (limited to 'src')
-rw-r--r-- | src/backend_freeimage.c | 207 | ||||
-rw-r--r-- | src/backend_libjpeg.c | 139 | ||||
-rw-r--r-- | src/backend_libpng.c | 132 | ||||
-rw-r--r-- | src/backend_librsvg.c | 116 | ||||
-rw-r--r-- | src/backend_libtiff.c | 149 | ||||
-rw-r--r-- | src/image.c | 2 | ||||
-rw-r--r-- | src/imv.c | 59 | ||||
-rw-r--r-- | src/source.c | 132 | ||||
-rw-r--r-- | src/source.h | 79 | ||||
-rw-r--r-- | src/source_private.h | 24 | ||||
-rw-r--r-- | src/viewport.c | 2 |
11 files changed, 414 insertions, 627 deletions
diff --git a/src/backend_freeimage.c b/src/backend_freeimage.c index bdddff2..f3b8d7e 100644 --- a/src/backend_freeimage.c +++ b/src/backend_freeimage.c @@ -1,28 +1,35 @@ #include "backend.h" -#include "source.h" +#include "bitmap.h" +#include "image.h" #include "log.h" - -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "source.h" +#include "source_private.h" #include <FreeImage.h> +#include <stdlib.h> struct private { + char *path; FIMEMORY *memory; FREE_IMAGE_FORMAT format; FIMULTIBITMAP *multibitmap; FIBITMAP *last_frame; + int num_frames; + int next_frame; + int width; + int height; }; -static void source_free(struct imv_source *src) +static void free_private(void *raw_private) { - pthread_mutex_lock(&src->busy); - free(src->name); - src->name = NULL; + if (!raw_private) { + return; + } + + struct private *private = raw_private; - struct private *private = src->private; + free(private->path); + private->path = NULL; if (private->memory) { FreeImage_CloseMemory(private->memory); @@ -40,11 +47,6 @@ static void source_free(struct imv_source *src) } free(private); - src->private = NULL; - - pthread_mutex_unlock(&src->busy); - pthread_mutex_destroy(&src->busy); - free(src); } static struct imv_image *to_image(FIBITMAP *in_bmp) @@ -95,55 +97,20 @@ static FIBITMAP *normalise_bitmap(FIBITMAP *input) return output; } -static void report_error(struct imv_source *src, const char *error) +static void first_frame(void *raw_private, struct imv_image **image, int *frametime) { - imv_log(IMV_ERROR, "freeimage: %s\n", error); - - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = NULL; - msg.error = error; + *image = NULL; + *frametime = 0; - pthread_mutex_unlock(&src->busy); - src->callback(&msg); -} - -static void send_bitmap(struct imv_source *src, FIBITMAP *fibitmap, int frametime) -{ - imv_log(IMV_DEBUG, "freeimage: returning bitmap\n"); - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = to_image(fibitmap); - msg.frametime = frametime; - msg.error = NULL; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); -} - -static void *first_frame(struct imv_source *src) -{ - /* Don't run if this source is already active */ - if (pthread_mutex_trylock(&src->busy)) { - return NULL; - } imv_log(IMV_DEBUG, "freeimage: first_frame called\n"); FIBITMAP *bmp = NULL; - struct private *private = src->private; - - int frametime = 0; + struct private *private = raw_private; if (private->format == FIF_GIF) { - if (src->name) { - private->multibitmap = FreeImage_OpenMultiBitmap(FIF_GIF, src->name, + if (private->path) { + private->multibitmap = FreeImage_OpenMultiBitmap(FIF_GIF, private->path, /* don't create file */ 0, /* read only */ 1, /* keep in memory */ 1, @@ -153,110 +120,109 @@ static void *first_frame(struct imv_source *src) private->memory, /* flags */ GIF_LOAD256); } else { - report_error(src, "src->name and private->memory both NULL"); - return NULL; + imv_log(IMV_ERROR, "private->path and private->memory both NULL"); + return; } if (!private->multibitmap) { - report_error(src, "first frame already loaded"); - return NULL; + imv_log(IMV_ERROR, "first frame already loaded"); + return; } FIBITMAP *frame = FreeImage_LockPage(private->multibitmap, 0); - src->num_frames = FreeImage_GetPageCount(private->multibitmap); + private->num_frames = FreeImage_GetPageCount(private->multibitmap); /* Get duration of first frame */ FITAG *tag = NULL; FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag); - if(FreeImage_GetTagValue(tag)) { - frametime = *(int*)FreeImage_GetTagValue(tag); + if (FreeImage_GetTagValue(tag)) { + *frametime = *(int*)FreeImage_GetTagValue(tag); } else { - frametime = 100; /* default value for gifs */ + *frametime = 100; /* default value for gifs */ } bmp = FreeImage_ConvertTo24Bits(frame); FreeImage_UnlockPage(private->multibitmap, frame, 0); } else { /* not a gif */ - src->num_frames = 1; + private->num_frames = 1; int flags = (private->format == FIF_JPEG) ? JPEG_EXIFROTATE : 0; FIBITMAP *fibitmap = NULL; - if (src->name) { - fibitmap = FreeImage_Load(private->format, src->name, flags); + if (private->path) { + fibitmap = FreeImage_Load(private->format, private->path, flags); } else if (private->memory) { fibitmap = FreeImage_LoadFromMemory(private->format, private->memory, flags); } if (!fibitmap) { - report_error(src, "FreeImage_Load returned NULL"); - return NULL; + imv_log(IMV_ERROR, "FreeImage_Load returned NULL"); + return; } bmp = normalise_bitmap(fibitmap); } - src->width = FreeImage_GetWidth(bmp); - src->height = FreeImage_GetHeight(bmp); + private->width = FreeImage_GetWidth(bmp); + private->height = FreeImage_GetHeight(bmp); private->last_frame = bmp; - send_bitmap(src, bmp, frametime); - return NULL; + private->next_frame = 1 % private->num_frames; + + *image = to_image(bmp); } -static void *next_frame(struct imv_source *src) +static void next_frame(void *raw_private, struct imv_image **image, int *frametime) { - /* Don't run if this source is already active */ - if (pthread_mutex_trylock(&src->busy)) { - return NULL; - } + *image = NULL; + *frametime = 0; + + struct private *private = raw_private; - struct private *private = src->private; - if (src->num_frames == 1) { - send_bitmap(src, private->last_frame, 0); - return NULL; + if (private->num_frames == 1) { + *image = to_image(private->last_frame); + return; } FITAG *tag = NULL; char disposal_method = 0; - int frametime = 0; short top = 0; short left = 0; - FIBITMAP *frame = FreeImage_LockPage(private->multibitmap, src->next_frame); + FIBITMAP *frame = FreeImage_LockPage(private->multibitmap, private->next_frame); FIBITMAP *frame32 = FreeImage_ConvertTo32Bits(frame); /* First frame is always going to use the raw frame */ - if(src->next_frame > 0) { + if (private->next_frame > 0) { FreeImage_GetMetadata(FIMD_ANIMATION, frame, "DisposalMethod", &tag); - if(FreeImage_GetTagValue(tag)) { + if (FreeImage_GetTagValue(tag)) { disposal_method = *(char*)FreeImage_GetTagValue(tag); } } FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameLeft", &tag); - if(FreeImage_GetTagValue(tag)) { + if (FreeImage_GetTagValue(tag)) { left = *(short*)FreeImage_GetTagValue(tag); } FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTop", &tag); - if(FreeImage_GetTagValue(tag)) { + if (FreeImage_GetTagValue(tag)) { top = *(short*)FreeImage_GetTagValue(tag); } FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag); - if(FreeImage_GetTagValue(tag)) { - frametime = *(int*)FreeImage_GetTagValue(tag); + if (FreeImage_GetTagValue(tag)) { + *frametime = *(int*)FreeImage_GetTagValue(tag); } /* some gifs don't provide a frame time at all */ - if(frametime == 0) { - frametime = 100; + if (*frametime == 0) { + *frametime = 100; } FreeImage_UnlockPage(private->multibitmap, frame, 0); /* If this frame is inset, we need to expand it for compositing */ - if(src->width != (int)FreeImage_GetWidth(frame32) || - src->height != (int)FreeImage_GetHeight(frame32)) { - FIBITMAP *expanded = FreeImage_Allocate(src->width, src->height, 32, 0,0,0); + if (private->width != (int)FreeImage_GetWidth(frame32) || + private->height != (int)FreeImage_GetHeight(frame32)) { + FIBITMAP *expanded = FreeImage_Allocate(private->width, private->height, 32, 0,0,0); FreeImage_Paste(expanded, frame32, left, top, 255); FreeImage_Unload(frame32); frame32 = expanded; @@ -265,7 +231,7 @@ static void *next_frame(struct imv_source *src) switch(disposal_method) { case 0: /* nothing specified, fall through to compositing */ case 1: /* composite over previous frame */ - if(private->last_frame && src->next_frame > 0) { + if (private->last_frame && private->next_frame > 0) { FIBITMAP *bg_frame = FreeImage_ConvertTo24Bits(private->last_frame); FreeImage_Unload(private->last_frame); FIBITMAP *comp = FreeImage_Composite(frame32, 1, NULL, bg_frame); @@ -281,25 +247,30 @@ static void *next_frame(struct imv_source *src) } break; case 2: /* TODO - set to background, composite over that */ - if(private->last_frame) { + if (private->last_frame) { FreeImage_Unload(private->last_frame); } private->last_frame = frame32; break; case 3: /* TODO - restore to previous content */ - if(private->last_frame) { + if (private->last_frame) { FreeImage_Unload(private->last_frame); } private->last_frame = frame32; break; } - src->next_frame = (src->next_frame + 1) % src->num_frames; + private->next_frame = (private->next_frame + 1) % private->num_frames; - send_bitmap(src, private->last_frame, frametime); - return NULL; + *image = to_image(private->last_frame); } +static const struct imv_source_vtable vtable = { + .load_first_frame = first_frame, + .load_next_frame = next_frame, + .free = free_private +}; + static enum backend_result open_path(const char *path, struct imv_source **src) { imv_log(IMV_DEBUG, "freeimage: open_path(%s)\n", path); @@ -312,21 +283,9 @@ static enum backend_result open_path(const char *path, struct imv_source **src) struct private *private = calloc(1, sizeof(struct private)); private->format = fmt; + private->path = strdup(path); - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup(path); - source->width = 0; - source->height = 0; - source->num_frames = 0; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &first_frame; - source->load_next_frame = &next_frame; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = private; - *src = source; - + *src = imv_source_create(&vtable, private); return BACKEND_SUCCESS; } @@ -344,21 +303,9 @@ static enum backend_result open_memory(void *data, size_t len, struct imv_source struct private *private = calloc(1, sizeof(struct private)); private->format = fmt; private->memory = fmem; + private->path = NULL; - struct imv_source *source = calloc(1, sizeof *source); - source->name = NULL; - source->width = 0; - source->height = 0; - source->num_frames = 0; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &first_frame; - source->load_next_frame = &next_frame; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = private; - *src = source; - + *src = imv_source_create(&vtable, private); return BACKEND_SUCCESS; } diff --git a/src/backend_libjpeg.c b/src/backend_libjpeg.c index 34dfb43..c9d70df 100644 --- a/src/backend_libjpeg.c +++ b/src/backend_libjpeg.c @@ -1,5 +1,8 @@ #include "backend.h" +#include "bitmap.h" +#include "image.h" #include "source.h" +#include "source_private.h" #include <assert.h> #include <stdlib.h> @@ -16,15 +19,16 @@ struct private { void *data; size_t len; tjhandle jpeg; + int width; + int height; }; -static void source_free(struct imv_source *src) +static void free_private(void *raw_private) { - pthread_mutex_lock(&src->busy); - free(src->name); - src->name = NULL; - - struct private *private = src->private; + if (!raw_private) { + return; + } + struct private *private = raw_private; tjDestroy(private->jpeg); if (private->fd >= 0) { munmap(private->data, private->len); @@ -34,77 +38,38 @@ static void source_free(struct imv_source *src) } private->data = NULL; - free(src->private); - src->private = NULL; - - pthread_mutex_unlock(&src->busy); - pthread_mutex_destroy(&src->busy); - free(src); -} - -static struct imv_image *to_image(int width, int height, void *bitmap) -{ - struct imv_bitmap *bmp = malloc(sizeof *bmp); - bmp->width = width; - bmp->height = height; - bmp->format = IMV_ABGR; - bmp->data = bitmap; - struct imv_image *image = imv_image_create_from_bitmap(bmp); - return image; -} - -static void report_error(struct imv_source *src) -{ - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = NULL; - msg.error = "Internal error"; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); + free(private); } -static void send_bitmap(struct imv_source *src, void *bitmap) +static void load_image(void *raw_private, struct imv_image **image, int *frametime) { - assert(src->callback); + *image = NULL; + *frametime = 0; - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = to_image(src->width, src->height, bitmap); - msg.frametime = 0; - msg.error = NULL; + struct private *private = raw_private; - pthread_mutex_unlock(&src->busy); - src->callback(&msg); -} - -static void *load_image(struct imv_source *src) -{ - /* Don't run if this source is already active */ - if (pthread_mutex_trylock(&src->busy)) { - return NULL; - } - - struct private *private = src->private; - - void *bitmap = malloc(src->height * src->width * 4); + void *bitmap = malloc(private->height * private->width * 4); int rcode = tjDecompress2(private->jpeg, private->data, private->len, - bitmap, src->width, 0, src->height, TJPF_RGBA, TJFLAG_FASTDCT); + bitmap, private->width, 0, private->height, TJPF_RGBA, TJFLAG_FASTDCT); if (rcode) { free(bitmap); - report_error(src); - return NULL; + return; } - send_bitmap(src, bitmap); - return NULL; + struct imv_bitmap *bmp = malloc(sizeof *bmp); + bmp->width = private->width; + bmp->height = private->height; + bmp->format = IMV_ABGR; + bmp->data = bitmap; + *image = imv_image_create_from_bitmap(bmp); } +static const struct imv_source_vtable vtable = { + .load_first_frame = load_image, + .free = free_private +}; + static enum backend_result open_path(const char *path, struct imv_source **src) { struct private private; @@ -135,9 +100,8 @@ static enum backend_result open_path(const char *path, struct imv_source **src) return BACKEND_UNSUPPORTED; } - int width, height; int rcode = tjDecompressHeader(private.jpeg, private.data, private.len, - &width, &height); + &private.width, &private.height); if (rcode) { tjDestroy(private.jpeg); munmap(private.data, private.len); @@ -145,22 +109,10 @@ static enum backend_result open_path(const char *path, struct imv_source **src) return BACKEND_UNSUPPORTED; } - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup(path); - source->width = width; - source->height = height; - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = malloc(sizeof private); - memcpy(source->private, &private, sizeof private); - - *src = source; + struct private *new_private = malloc(sizeof private); + memcpy(new_private, &private, sizeof private); + + *src = imv_source_create(&vtable, new_private); return BACKEND_SUCCESS; } @@ -177,30 +129,17 @@ static enum backend_result open_memory(void *data, size_t len, struct imv_source return BACKEND_UNSUPPORTED; } - int width, height; int rcode = tjDecompressHeader(private.jpeg, private.data, private.len, - &width, &height); + &private.width, &private.height); if (rcode) { tjDestroy(private.jpeg); return BACKEND_UNSUPPORTED; } - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup("-"); - source->width = width; - source->height = height; - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = malloc(sizeof private); - memcpy(source->private, &private, sizeof private); - - *src = source; + struct private *new_private = malloc(sizeof private); + memcpy(new_private, &private, sizeof private); + + *src = imv_source_create(&vtable, new_private); return BACKEND_SUCCESS; } diff --git a/src/backend_libpng.c b/src/backend_libpng.c index 88a163e..ef94028 100644 --- a/src/backend_libpng.c +++ b/src/backend_libpng.c @@ -1,11 +1,11 @@ #include "backend.h" -#include "source.h" +#include "bitmap.h" +#include "image.h" #include "log.h" +#include "source.h" +#include "source_private.h" -#include <assert.h> #include <stdlib.h> -#include <stdio.h> -#include <string.h> #include <png.h> @@ -15,104 +15,64 @@ struct private { png_infop info; }; -static void source_free(struct imv_source *src) +static void free_private(void *raw_private) { - pthread_mutex_lock(&src->busy); - free(src->name); - src->name = NULL; + if (!raw_private) { + return; + } - struct private *private = src->private; + struct private *private = raw_private; png_destroy_read_struct(&private->png, &private->info, NULL); if (private->file) { fclose(private->file); } - - free(src->private); - src->private = NULL; - - pthread_mutex_unlock(&src->busy); - pthread_mutex_destroy(&src->busy); - free(src); -} - -static struct imv_image *to_image(int width, int height, void *bitmap) -{ - struct imv_bitmap *bmp = malloc(sizeof *bmp); - bmp->width = width; - bmp->height = height; - bmp->format = IMV_ABGR; - bmp->data = bitmap; - struct imv_image *image = imv_image_create_from_bitmap(bmp); - return image; -} - -static void report_error(struct imv_source *src) -{ - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = NULL; - msg.error = "Internal error"; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); + free(private); } - -static void send_bitmap(struct imv_source *src, void *bitmap) +static void load_image(void *raw_private, struct imv_image **image, int *frametime) { - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = to_image(src->width, src->height, bitmap); - msg.frametime = 0; - msg.error = NULL; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); -} - -static void *load_image(struct imv_source *src) -{ - /* Don't run if this source is already active */ - if (pthread_mutex_trylock(&src->busy)) { - return NULL; - } - - struct private *private = src->private; + *image = NULL; + *frametime = 0; + struct private *private = raw_private; if (setjmp(png_jmpbuf(private->png))) { - report_error(src); - return NULL; + return; } - png_bytep *rows = malloc(sizeof(png_bytep) * src->height); + const int width = png_get_image_width(private->png, private->info); + const int height = png_get_image_height(private->png, private->info); + + png_bytep *rows = malloc(sizeof(png_bytep) * height); size_t row_len = png_get_rowbytes(private->png, private->info); - rows[0] = malloc(src->height * row_len); - for (int y = 1; y < src->height; ++y) { + rows[0] = malloc(height * row_len); + for (int y = 1; y < height; ++y) { rows[y] = rows[0] + row_len * y; } if (setjmp(png_jmpbuf(private->png))) { - free(rows[0]); - free(rows); - report_error(src); - return NULL; + return; } png_read_image(private->png, rows); - void *bmp = rows[0]; + void *raw_bmp = rows[0]; free(rows); fclose(private->file); private->file = NULL; - send_bitmap(src, bmp); - return NULL; + + + struct imv_bitmap *bmp = malloc(sizeof *bmp); + bmp->width = width; + bmp->height = height; + bmp->format = IMV_ABGR; + bmp->data = raw_bmp; + *image = imv_image_create_from_bitmap(bmp); } +static const struct imv_source_vtable vtable = { + .load_first_frame = load_image, + .free = free_private +}; + static enum backend_result open_path(const char *path, struct imv_source **src) { @@ -127,7 +87,7 @@ static enum backend_result open_path(const char *path, struct imv_source **src) return BACKEND_UNSUPPORTED; } - struct private *private = malloc(sizeof *private); + struct private *private = calloc(1, sizeof *private); private->file = f; private->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!private->png) { @@ -168,30 +128,14 @@ static enum backend_result open_path(const char *path, struct imv_source **src) png_get_bit_depth(private->png, private->info), png_get_color_type(private->png, private->info)); - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup(path); if (setjmp(png_jmpbuf(private->png))) { - free(source->name); - free(source); png_destroy_read_struct(&private->png, &private->info, NULL); fclose(private->file); free(private); return BACKEND_UNSUPPORTED; } - source->width = png_get_image_width(private->png, private->info); - source->height = png_get_image_height(private->png, private->info); - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = private; - - *src = source; + *src = imv_source_create(&vtable, private); return BACKEND_SUCCESS; } diff --git a/src/backend_librsvg.c b/src/backend_librsvg.c index ff27477..9023b10 100644 --- a/src/backend_librsvg.c +++ b/src/backend_librsvg.c @@ -1,12 +1,12 @@ #include "backend.h" +#include "image.h" #include "source.h" +#include "source_private.h" -#include <assert.h> +#include <librsvg/rsvg.h> #include <stdlib.h> #include <string.h> -#include <librsvg/rsvg.h> - /* Some systems like GNU/Hurd don't define PATH_MAX */ #ifndef PATH_MAX #define PATH_MAX 4096 @@ -15,85 +15,40 @@ struct private { void *data; size_t len; + char path[PATH_MAX+8]; }; -static void source_free(struct imv_source *src) -{ - pthread_mutex_lock(&src->busy); - free(src->name); - src->name = NULL; - - struct private *private = src->private; - free(private); - src->private = NULL; - - pthread_mutex_unlock(&src->busy); - pthread_mutex_destroy(&src->busy); - free(src); -} - -static void report_error(struct imv_source *src) +static void free_private(void *raw_private) { - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = NULL; - msg.error = "Internal error"; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); + free(raw_private); } -static void send_svg(struct imv_source *src, RsvgHandle *handle) +static void load_image(void *raw_private, struct imv_image **image, int *frametime) { - assert(src->callback); + *image = NULL; + *frametime = 0; - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = imv_image_create_from_svg(handle); - msg.frametime = 0; - msg.error = NULL; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); -} - -static void *load_image(struct imv_source *src) -{ - /* Don't run if this source is already active */ - if (pthread_mutex_trylock(&src->busy)) { - return NULL; - } + struct private *private = raw_private; RsvgHandle *handle = NULL; GError *error = NULL; - struct private *private = src->private; if (private->data) { handle = rsvg_handle_new_from_data(private->data, private->len, &error); } else { - char path[PATH_MAX+8]; - snprintf(path, sizeof path, "file://%s", src->name); - handle = rsvg_handle_new_from_file(path, &error); + handle = rsvg_handle_new_from_file(private->path, &error); } - if (!handle) { - report_error(src); - return NULL; + if (handle) { + *image = imv_image_create_from_svg(handle); } - - RsvgDimensionData dim; - rsvg_handle_get_dimensions(handle, &dim); - src->width = dim.width; - src->height = dim.height; - - send_svg(src, handle); - return NULL; } +static const struct imv_source_vtable vtable = { + .load_first_frame = load_image, + .free = free_private +}; + static enum backend_result open_path(const char *path, struct imv_source **src) { /* Look for an <SVG> tag near the start of the file */ @@ -113,23 +68,9 @@ static enum backend_result open_path(const char *path, struct imv_source **src) struct private *private = malloc(sizeof *private); private->data = NULL; private->len = 0; + snprintf(private->path, sizeof private->path, "file://%s", path); - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup(path); - - source->width = 1024; - source->height = 1024; - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = private; - - *src = source; + *src = imv_source_create(&vtable, private); return BACKEND_SUCCESS; } @@ -151,22 +92,7 @@ static enum backend_result open_memory(void *data, size_t len, struct imv_source private->data = data; private->len = len; - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup("-"); - - source->width = 1024; - source->height = 1024; - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = private; - - *src = source; + *src = imv_source_create(&vtable, private); return BACKEND_SUCCESS; } diff --git a/src/backend_libtiff.c b/src/backend_libtiff.c index de7fc70..b0e955f 100644 --- a/src/backend_libtiff.c +++ b/src/backend_libtiff.c @@ -1,20 +1,19 @@ #include "backend.h" +#include "bitmap.h" +#include "image.h" #include "source.h" +#include "source_private.h" -#include <assert.h> #include <stdlib.h> -#include <stdio.h> -#include <sys/mman.h> #include <string.h> -#include <unistd.h> -#include <fcntl.h> - #include <tiffio.h> struct private { TIFF *tiff; void *data; size_t pos, len; + int width; + int height; }; static tsize_t mem_read(thandle_t data, tdata_t buffer, tsize_t len) @@ -60,93 +59,53 @@ static toff_t mem_size(thandle_t data) return private->len; } -static void source_free(struct imv_source *src) +static void free_private(void *raw_private) { - pthread_mutex_lock(&src->busy); - free(src->name); - src->name = NULL; + if (!raw_private) { + return; + } - struct private *private = src->private; + struct private *private = raw_private; TIFFClose(private->tiff); private->tiff = NULL; - free(src->private); - src->private = NULL; - - pthread_mutex_unlock(&src->busy); - pthread_mutex_destroy(&src->busy); - free(src); -} - -static struct imv_image *to_image(int width, int height, void *bitmap) -{ - struct imv_bitmap *bmp = malloc(sizeof *bmp); - bmp->width = width; - bmp->height = height; - bmp->format = IMV_ABGR; - bmp->data = bitmap; - struct imv_image *image = imv_image_create_from_bitmap(bmp); - return image; -} - -static void report_error(struct imv_source *src) -{ - assert(src->callback); - - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = NULL; - msg.error = "Internal error"; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); + free(private); } -static void send_bitmap(struct imv_source *src, void *bitmap) +static void load_image(void *raw_private, struct imv_image **image, int *frametime) { - assert(src->callback); + *image = NULL; + *frametime = 0; - struct imv_source_message msg; - msg.source = src; - msg.user_data = src->user_data; - msg.image = to_image(src->width, src->height, bitmap); - msg.frametime = 0; - msg.error = NULL; - - pthread_mutex_unlock(&src->busy); - src->callback(&msg); -} - -static void *load_image(struct imv_source *src) -{ - /* Don't run if this source is already active */ - if (pthread_mutex_trylock(&src->busy)) { - return NULL; - } - - struct private *private = src->private; + struct private *private = raw_private; /* libtiff suggests using their own allocation routines to support systems * with segmented memory. I have no desire to support that, so I'm just * going to use vanilla malloc/free. Systems where that isn't acceptable * don't have upstream support from imv. */ - void *bitmap = malloc(src->height * src->width * 4); - int rcode = TIFFReadRGBAImageOriented(private->tiff, src->width, src->height, + void *bitmap = malloc(private->height * private->width * 4); + int rcode = TIFFReadRGBAImageOriented(private->tiff, private->width, private->height, bitmap, ORIENTATION_TOPLEFT, 0); /* 1 = success, unlike the rest of *nix */ if (rcode != 1) { - free(bitmap); - report_error(src); - return NULL; + return; } - send_bitmap(src, bitmap); - return NULL; + struct imv_bitmap *bmp = malloc(sizeof *bmp); + bmp->width = private->width; + bmp->height = private->height; + bmp->format = IMV_ABGR; + bmp->data = bitmap; + *image = imv_image_create_from_bitmap(bmp); } +static const struct imv_source_vtable vtable = { + .load_first_frame = load_image, + .free = free_private +}; + static enum backend_result open_path(const char *path, struct imv_source **src) { struct private private; @@ -157,26 +116,13 @@ static enum backend_result open_path(const char *path, struct imv_source **src) return BACKEND_UNSUPPORTED; } - unsigned int width, height; - TIFFGetField(private.tiff, TIFFTAG_IMAGEWIDTH, &width); - TIFFGetField(private.tiff, TIFFTAG_IMAGELENGTH, &height); - - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup(path); - source->width = width; - source->height = height; - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = malloc(sizeof private); - memcpy(source->private, &private, sizeof private); - - *src = source; + TIFFGetField(private.tiff, TIFFTAG_IMAGEWIDTH, &private.width); + TIFFGetField(private.tiff, TIFFTAG_IMAGELENGTH, &private.height); + + struct private *new_private = malloc(sizeof private); + memcpy(new_private, &private, sizeof private); + + *src = imv_source_create(&vtable, new_private); return BACKEND_SUCCESS; } @@ -195,25 +141,10 @@ static enum backend_result open_memory(void *data, size_t len, struct imv_source return BACKEND_UNSUPPORTED; } - unsigned int width, height; - TIFFGetField(private->tiff, TIFFTAG_IMAGEWIDTH, &width); - TIFFGetField(private->tiff, TIFFTAG_IMAGELENGTH, &height); - - struct imv_source *source = calloc(1, sizeof *source); - source->name = strdup("-"); - source->width = width; - source->height = height; - source->num_frames = 1; - source->next_frame = 1; - pthread_mutex_init(&source->busy, NULL); - source->load_first_frame = &load_image; - source->load_next_frame = NULL; - source->free = &source_free; - source->callback = NULL; - source->user_data = NULL; - source->private = private; - - *src = source; + TIFFGetField(private->tiff, TIFFTAG_IMAGEWIDTH, &private->width); + TIFFGetField(private->tiff, TIFFTAG_IMAGELENGTH, &private->height); + + *src = imv_source_create(&vtable, private); return BACKEND_SUCCESS; } diff --git a/src/image.c b/src/image.c index f4df7c9..e4cda9d 100644 --- a/src/image.c +++ b/src/image.c @@ -2,6 +2,8 @@ #include "bitmap.h" +#include <stdlib.h> + struct imv_image { int width; int height; @@ -4,9 +4,11 @@ #include <ctype.h> #include <errno.h> #include <getopt.h> +#include <pthread.h> #include <stdbool.h> #include <stdint.h> #include <stdlib.h> +#include <time.h> #include <unistd.h> #include <wordexp.h> @@ -59,9 +61,6 @@ struct internal_event { bool is_new_image; } new_image; struct { - char *error; - } bad_image; - struct { char *path; } new_path; struct { @@ -318,37 +317,6 @@ static double cur_time(void) return ts.tv_sec + (double)ts.tv_nsec * 0.000000001; } -static void *async_free_source_thread(void *raw) -{ - struct imv_source *src = raw; - src->free(src); - return NULL; -} - -static void async_free_source(struct imv_source *src) -{ - typedef void *(*thread_func)(void*); - pthread_t thread; - pthread_create(&thread, NULL, (thread_func)async_free_source_thread, src); - pthread_detach(thread); -} - -static void async_load_first_frame(struct imv_source *src) -{ - typedef void *(*thread_func)(void*); - pthread_t thread; - pthread_create(&thread, NULL, (thread_func)src->load_first_frame, src); - pthread_detach(thread); -} - -static void async_load_next_frame(struct imv_source *src) -{ - typedef void *(*thread_func)(void*); - pthread_t thread; - pthread_create(&thread, NULL, (thread_func)src->load_next_frame, src); - pthread_detach(thread); -} - static void source_callback(struct imv_source_message *msg) { struct imv *imv = msg->user_data; @@ -376,8 +344,6 @@ static void source_callback(struct imv_source_message *msg) imv->last_source = msg->source; } else { event->type = BAD_IMAGE; - /* TODO: Something more elegant with error messages */ - event->data.bad_image.error = strdup(msg->error); } struct imv_event e = { @@ -601,7 +567,7 @@ void imv_free(struct imv *imv) imv_binds_free(imv->binds); imv_navigator_free(imv->navigator); if (imv->current_source) { - imv->current_source->free(imv->current_source); + imv_source_free(imv->current_source); } imv_commands_free(imv->commands); imv_console_free(imv->console); @@ -960,12 +926,11 @@ int imv_run(struct imv *imv) if (result == BACKEND_SUCCESS) { if (imv->current_source) { - async_free_source(imv->current_source); + imv_source_async_free(imv->current_source); } imv->current_source = new_source; - imv->current_source->callback = &source_callback; - imv->current_source->user_data = imv; - async_load_first_frame(imv->current_source); + imv_source_set_callback(imv->current_source, &source_callback, imv); + imv_source_async_load_first_frame(imv->current_source); imv->loading = true; imv_viewport_set_playing(imv->view, true); @@ -1016,8 +981,8 @@ int imv_run(struct imv *imv) imv->need_redraw = true; /* Trigger loading of a new frame, now this one's being displayed */ - if (imv->current_source && imv->current_source->load_next_frame) { - async_load_next_frame(imv->current_source); + if (imv->current_source) { + imv_source_async_load_next_frame(imv->current_source); } } @@ -1128,8 +1093,8 @@ static void handle_new_image(struct imv *imv, struct imv_image *image, int frame imv->next_frame.duration = 0.0; /* If this is an animated image, we should kick off loading the next frame */ - if (imv->current_source && imv->current_source->load_next_frame && frametime) { - async_load_next_frame(imv->current_source); + if (imv->current_source && frametime) { + imv_source_async_load_next_frame(imv->current_source); } } @@ -1614,8 +1579,8 @@ static void command_next_frame(struct list *args, const char *argstr, void *data (void)args; (void)argstr; struct imv *imv = data; - if (imv->current_source && imv->current_source->load_next_frame) { - async_load_next_frame(imv->current_source); + if (imv->current_source) { + imv_source_async_load_next_frame(imv->current_source); imv->next_frame.force_next_frame = true; } } diff --git a/src/source.c b/src/source.c new file mode 100644 index 0000000..6203dfe --- /dev/null +++ b/src/source.c @@ -0,0 +1,132 @@ +#include "source.h" +#include "source_private.h" + +#include <pthread.h> +#include <stdlib.h> + +struct imv_source { + /* pointers to implementation's functions */ + const struct imv_source_vtable *vtable; + + /* pointer to implementation data */ + void *private; + + /* Attempted to be locked by load_first_frame or load_next_frame. + * If the mutex can't be locked, the call is aborted. + * Used to prevent the source from having multiple worker threads at once. + * Released by the source before calling the message callback with a result. + */ + pthread_mutex_t busy; + + /* callback function */ + imv_source_callback callback; + /* callback data */ + void *callback_data; +}; + +struct imv_source *imv_source_create(const struct imv_source_vtable *vtable, void *private) +{ + struct imv_source *source = calloc(1, sizeof *source); + source->vtable = vtable; + source->private = private; + pthread_mutex_init(&source->busy, NULL); + return source; +} + +static void *free_thread(void *src) +{ + imv_source_free(src); + return NULL; +} + +void imv_source_async_free(struct imv_source *src) +{ + pthread_t thread; + pthread_create(&thread, NULL, free_thread, src); + pthread_detach(thread); +} + +static void *first_frame_thread(void *src) +{ + imv_source_load_first_frame(src); + return NULL; +} + +void imv_source_async_load_first_frame(struct imv_source *src) +{ + pthread_t thread; + pthread_create(&thread, NULL, first_frame_thread, src); + pthread_detach(thread); +} + +static void *next_frame_thread(void *src) +{ + imv_source_load_next_frame(src); + return NULL; +} +void imv_source_async_load_next_frame(struct imv_source *src) +{ + pthread_t thread; + pthread_create(&thread, NULL, next_frame_thread, src); + pthread_detach(thread); +} + +void imv_source_free(struct imv_source *src) +{ + pthread_mutex_lock(&src->busy); + src->vtable->free(src->private); + pthread_mutex_unlock(&src->busy); + pthread_mutex_destroy(&src->busy); + free(src); +} + +void imv_source_load_first_frame(struct imv_source *src) +{ + if (!src->vtable->load_first_frame) { + return; + } + + if (pthread_mutex_trylock(&src->busy)) { + return; + } + + struct imv_source_message msg = { + .source = src, + .user_data = src->callback_data + }; + + src->vtable->load_first_frame(src->private, &msg.image, &msg.frametime); + + pthread_mutex_unlock(&src->busy); + + src->callback(&msg); +} + +void imv_source_load_next_frame(struct imv_source *src) +{ + if (!src->vtable->load_next_frame) { + return; + } + + if (pthread_mutex_trylock(&src->busy)) { + return; + } + + struct imv_source_message msg = { + .source = src, + .user_data = src->callback_data + }; + + src->vtable->load_next_frame(src->private, &msg.image, &msg.frametime); + + pthread_mutex_unlock(&src->busy); + + src->callback(&msg); +} + +void imv_source_set_callback(struct imv_source *src, imv_source_callback callback, + void *data) +{ + src->callback = callback; + src->callback_data = data; +} diff --git a/src/source.h b/src/source.h index d55e560..f843d85 100644 --- a/src/source.h +++ b/src/source.h @@ -1,72 +1,47 @@ #ifndef IMV_SOURCE_H #define IMV_SOURCE_H -#include <pthread.h> -#include <stdbool.h> -#include "image.h" - -struct imv_source_message { - /* Pointer to sender of message */ - struct imv_source *source; - - /* User-supplied pointer */ - void *user_data; - - /* Receiver is responsible for destroying image */ - struct imv_image *image; - - /* If an animated gif, the frame's duration in milliseconds, else 0 */ - int frametime; - - /* Error message if image was NULL */ - const char *error; -}; - /* While imv_image represents a single frame of an image, be it a bitmap or * vector image, imv_source represents an open handle to an image file, which * can emit one or more imv_images. */ -struct imv_source { - /* usually the path of the image this is the source of */ - char *name; +struct imv_source; - /* source's image dimensions */ - int width; - int height; +struct imv_source_message; +struct imv_image; - /* Usually 1, more if animated */ - int num_frames; +/* Clean up a source. Blocks if the source is active in the background. Async + * version does not block, performing cleanup in another thread */ +void imv_source_async_free(struct imv_source *src); +void imv_source_free(struct imv_source *src); - /* Next frame to be loaded, 0-indexed */ - int next_frame; +/* Load the first frame. Silently aborts if source is already loading. Async + * version performs loading in background. */ +void imv_source_async_load_first_frame(struct imv_source *src); +void imv_source_load_first_frame(struct imv_source *src); - /* Attempted to be locked by load_first_frame or load_next_frame. - * If the mutex can't be locked, the call is aborted. - * Used to prevent the source from having multiple worker threads at once. - * Released by the source before calling the message callback with a result. - */ - pthread_mutex_t busy; +/* Load the next frame. Silently aborts if source is already loading. Async + * version performs loading in background. */ +void imv_source_async_load_next_frame(struct imv_source *src); +void imv_source_load_next_frame(struct imv_source *src); - /* Trigger loading of the first frame. Returns 0 on success. */ - void *(*load_first_frame)(struct imv_source *src); +typedef void (*imv_source_callback)(struct imv_source_message *message); - /* Trigger loading of next frame. Returns 0 on success. */ - void *(*load_next_frame)(struct imv_source *src); +/* Sets the callback function to be called when frame loading completes */ +void imv_source_set_callback(struct imv_source *src, imv_source_callback callback, void *data); - /* Safely free contents of this source. After this returns - * it is safe to dealocate/overwrite the imv_source instance. - */ - void (*free)(struct imv_source *src); - - /* User-specified callback for returning messages */ - void (*callback)(struct imv_source_message *message); +struct imv_source_message { + /* Pointer to sender of message */ + struct imv_source *source; - /* User-specified pointer, included in returned messages */ + /* User-supplied pointer */ void *user_data; - /* Implementation private data */ - void *private; -}; + /* Receiver is responsible for destroying image */ + struct imv_image *image; + /* If an animated gif, the frame's duration in milliseconds, else 0 */ + int frametime; +}; #endif diff --git a/src/source_private.h b/src/source_private.h new file mode 100644 index 0000000..39b41fe --- /dev/null +++ b/src/source_private.h @@ -0,0 +1,24 @@ +#ifndef IMV_SOURCE_PRIVATE_H +#define IMV_SOURCE_PRIVATE_H + +struct imv_image; +struct imv_source; + +struct imv_source_vtable { + /* Loads the first frame, if successful puts output in image and duration + * (in milliseconds) in frametime. If unsuccessful, image shall be NULL. A + * still image should use a frametime of 0. + */ + void (*load_first_frame)(void *private, struct imv_image **image, int *frametime); + /* Loads the next frame, if successful puts output in image and duration + * (in milliseconds) in frametime. If unsuccessful, image shall be NULL. + */ + void (*load_next_frame)(void *private, struct imv_image **image, int *frametime); + /* Cleans up the private section of a source */ + void (*free)(void *private); +}; + +/* Build a source given its vtable and a pointer to the private data */ +struct imv_source *imv_source_create(const struct imv_source_vtable *vt, void *private); + +#endif diff --git a/src/viewport.c b/src/viewport.c index 5b8b3c7..c894f26 100644 --- a/src/viewport.c +++ b/src/viewport.c @@ -1,5 +1,7 @@ #include "viewport.h" +#include <stdlib.h> + struct imv_viewport { double scale; struct { |