aboutsummaryrefslogtreecommitdiff
path: root/src/backend_freeimage.c
diff options
context:
space:
mode:
authorHarry Jeffery <harry@exec64.co.uk>2019-01-20 21:20:59 +0000
committerHarry Jeffery <harry@exec64.co.uk>2019-01-29 22:25:05 +0000
commit090a0ceb7c2d5135b7286405d8bad5092d8387bb (patch)
treeddccddb1aeaecae11a53a00d223eabf5f852da8d /src/backend_freeimage.c
parentf4305c11aa617e4151385af13bd9e7f7e7ceafb0 (diff)
downloadimv-090a0ceb7c2d5135b7286405d8bad5092d8387bb.tar.gz
imv_source: Handle gifs
Diffstat (limited to 'src/backend_freeimage.c')
-rw-r--r--src/backend_freeimage.c114
1 files changed, 92 insertions, 22 deletions
diff --git a/src/backend_freeimage.c b/src/backend_freeimage.c
index 65a768e..596d450 100644
--- a/src/backend_freeimage.c
+++ b/src/backend_freeimage.c
@@ -12,21 +12,8 @@ struct private {
FREE_IMAGE_FORMAT format;
FIMULTIBITMAP *multibitmap;
FIBITMAP *last_frame;
- int raw_frame_time;
};
-static void time_passed(struct imv_source *src, double dt)
-{
- (void)src;
- (void)dt;
-}
-
-static double time_left(struct imv_source *src)
-{
- (void)src;
- return 0.0;
-}
-
static void source_free(struct imv_source *src)
{
free(src->name);
@@ -78,7 +65,7 @@ static void report_error(struct imv_source *src)
src->callback(&msg);
}
-static void send_bitmap(struct imv_source *src, FIBITMAP *fibitmap)
+static void send_bitmap(struct imv_source *src, FIBITMAP *fibitmap, int frametime)
{
if (!src->callback) {
fprintf(stderr, "imv_source(%s) has no callback configured. "
@@ -90,6 +77,7 @@ static void send_bitmap(struct imv_source *src, FIBITMAP *fibitmap)
msg.source = src;
msg.user_data = src->user_data;
msg.bitmap = to_imv_bitmap(fibitmap);
+ msg.frametime = frametime;
msg.error = NULL;
src->callback(&msg);
@@ -101,6 +89,8 @@ static void first_frame(struct imv_source *src)
struct private *private = src->private;
+ int frametime = 0;
+
if (private->format == FIF_GIF) {
private->multibitmap = FreeImage_OpenMultiBitmap(FIF_GIF, src->name,
/* don't create file */ 0,
@@ -120,9 +110,9 @@ static void first_frame(struct imv_source *src)
FITAG *tag = NULL;
FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTime", &tag);
if(FreeImage_GetTagValue(tag)) {
- private->raw_frame_time = *(int*)FreeImage_GetTagValue(tag);
+ frametime = *(int*)FreeImage_GetTagValue(tag);
} else {
- private->raw_frame_time = 100; /* default value for gifs */
+ frametime = 100; /* default value for gifs */
}
bmp = FreeImage_ConvertTo24Bits(frame);
FreeImage_UnlockPage(private->multibitmap, frame, 0);
@@ -140,8 +130,8 @@ static void first_frame(struct imv_source *src)
}
src->width = FreeImage_GetWidth(bmp);
- src->height = FreeImage_GetWidth(bmp);
- send_bitmap(src, bmp);
+ src->height = FreeImage_GetHeight(bmp);
+ send_bitmap(src, bmp, frametime);
private->last_frame = bmp;
}
@@ -149,11 +139,93 @@ static void next_frame(struct imv_source *src)
{
struct private *private = src->private;
if (src->num_frames == 1) {
- send_bitmap(src, private->last_frame);
+ send_bitmap(src, private->last_frame, 0);
return;
}
- // TODO gifs
+ 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 *frame32 = FreeImage_ConvertTo32Bits(frame);
+
+ /* First frame is always going to use the raw frame */
+ if(src->next_frame > 0) {
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "DisposalMethod", &tag);
+ if(FreeImage_GetTagValue(tag)) {
+ disposal_method = *(char*)FreeImage_GetTagValue(tag);
+ }
+ }
+
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameLeft", &tag);
+ if(FreeImage_GetTagValue(tag)) {
+ left = *(short*)FreeImage_GetTagValue(tag);
+ }
+
+ FreeImage_GetMetadata(FIMD_ANIMATION, frame, "FrameTop", &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);
+ }
+
+ /* some gifs don't provide a frame time at all */
+ 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);
+ FreeImage_Paste(expanded, frame32, left, top, 255);
+ FreeImage_Unload(frame32);
+ frame32 = expanded;
+ }
+
+ 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) {
+ FIBITMAP *bg_frame = FreeImage_ConvertTo24Bits(private->last_frame);
+ FreeImage_Unload(private->last_frame);
+ FIBITMAP *comp = FreeImage_Composite(frame32, 1, NULL, bg_frame);
+ FreeImage_Unload(bg_frame);
+ FreeImage_Unload(frame32);
+ private->last_frame = comp;
+ } else {
+ /* No previous frame, just render directly */
+ if(private->last_frame) {
+ FreeImage_Unload(private->last_frame);
+ }
+ private->last_frame = frame32;
+ }
+ break;
+ case 2: /* TODO - set to background, composite over that */
+ 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) {
+ FreeImage_Unload(private->last_frame);
+ }
+ private->last_frame = frame32;
+ break;
+ }
+
+ src->next_frame = (src->next_frame + 1) % src->num_frames;
+
+ send_bitmap(src, private->last_frame, frametime);
}
static enum backend_result open_path(const char *path, struct imv_source **src)
@@ -176,8 +248,6 @@ static enum backend_result open_path(const char *path, struct imv_source **src)
source->error_event_id = 0;
source->load_first_frame = &first_frame;
source->load_next_frame = &next_frame;
- source->time_passed = &time_passed;
- source->time_left = &time_left;
source->free = &source_free;
source->callback = NULL;
source->user_data = NULL;