Revised handling of GIF animations
- New option `-a`: Play animations at startup - Ctrl-Space toggles animation for all GIF files - Infinite loop for all animations
This commit is contained in:
parent
9c92de8584
commit
5e481912ec
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
VERSION = git-20140723
|
VERSION = git-20140725
|
||||||
|
|
||||||
PREFIX = /usr/local
|
PREFIX = /usr/local
|
||||||
MANPREFIX = $(PREFIX)/share/man
|
MANPREFIX = $(PREFIX)/share/man
|
||||||
|
|
|
@ -68,6 +68,7 @@ of small previews is displayed, making it easy to choose an image to open.
|
||||||
|
|
||||||
**Command line options:**
|
**Command line options:**
|
||||||
|
|
||||||
|
-a Play animations of multi-frame images
|
||||||
-b Do not show info bar on bottom of window
|
-b Do not show info bar on bottom of window
|
||||||
-c Remove all orphaned cache files from thumbnail cache and exit
|
-c Remove all orphaned cache files from thumbnail cache and exit
|
||||||
-f Start in fullscreen mode
|
-f Start in fullscreen mode
|
||||||
|
@ -117,7 +118,7 @@ of small previews is displayed, making it easy to choose an image to open.
|
||||||
p,Backspace Go [count] images backward
|
p,Backspace Go [count] images backward
|
||||||
[,] Go [count] * 10 images backward/forward
|
[,] Go [count] * 10 images backward/forward
|
||||||
Ctrl-n,p Go to the next/previous frame of a multi-frame image
|
Ctrl-n,p Go to the next/previous frame of a multi-frame image
|
||||||
Ctrl-Space Play/pause animation of a multi-frame image
|
Ctrl-Space Play/stop animations of multi-frame images
|
||||||
h,j,k,l Scroll image 1/5 of window width/height or [count] pixels
|
h,j,k,l Scroll image 1/5 of window width/height or [count] pixels
|
||||||
left/down/up/right (also with arrow keys)
|
left/down/up/right (also with arrow keys)
|
||||||
H,J,K,L Scroll to left/bottom/top/right image edge
|
H,J,K,L Scroll to left/bottom/top/right image edge
|
||||||
|
|
12
commands.c
12
commands.c
|
@ -282,13 +282,17 @@ bool ci_navigate_frame(arg_t a)
|
||||||
|
|
||||||
bool ci_toggle_animation(arg_t a)
|
bool ci_toggle_animation(arg_t a)
|
||||||
{
|
{
|
||||||
|
bool dirty = false;
|
||||||
|
|
||||||
|
img.multi.animate = !img.multi.animate;
|
||||||
|
|
||||||
if (img.multi.animate) {
|
if (img.multi.animate) {
|
||||||
reset_timeout(animate);
|
dirty = img_frame_animate(&img, true);
|
||||||
img.multi.animate = false;
|
|
||||||
} else if (img_frame_animate(&img, true)) {
|
|
||||||
set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
|
set_timeout(animate, img.multi.frames[img.multi.sel].delay, true);
|
||||||
|
} else {
|
||||||
|
reset_timeout(animate);
|
||||||
}
|
}
|
||||||
return true;
|
return dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ci_scroll(arg_t a)
|
bool ci_scroll(arg_t a)
|
||||||
|
|
|
@ -34,13 +34,6 @@ static const float zoom_levels[] = {
|
||||||
/* default slideshow delay (in sec, overwritten via -S option): */
|
/* default slideshow delay (in sec, overwritten via -S option): */
|
||||||
enum { SLIDESHOW_DELAY = 5 };
|
enum { SLIDESHOW_DELAY = 5 };
|
||||||
|
|
||||||
/* default settings for multi-frame gif images: */
|
|
||||||
enum {
|
|
||||||
GIF_DELAY = 100, /* delay time (in ms) */
|
|
||||||
GIF_AUTOPLAY = 1, /* autoplay when loaded [0/1] */
|
|
||||||
GIF_LOOP = 0 /* loop? [0: no, 1: endless, -1: as specified in file] */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* gamma correction: the user-visible ranges [-GAMMA_RANGE, 0] and
|
/* gamma correction: the user-visible ranges [-GAMMA_RANGE, 0] and
|
||||||
* (0, GAMMA_RANGE] are mapped to the ranges [0, 1], and (1, GAMMA_MAX].
|
* (0, GAMMA_RANGE] are mapped to the ranges [0, 1], and (1, GAMMA_MAX].
|
||||||
* */
|
* */
|
||||||
|
|
39
image.c
39
image.c
|
@ -35,7 +35,7 @@
|
||||||
|
|
||||||
#if HAVE_GIFLIB
|
#if HAVE_GIFLIB
|
||||||
#include <gif_lib.h>
|
#include <gif_lib.h>
|
||||||
enum { MIN_GIF_DELAY = 25 };
|
enum { DEF_GIF_DELAY = 75 };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float zoom_min;
|
float zoom_min;
|
||||||
|
@ -85,8 +85,8 @@ void img_init(img_t *img, win_t *win)
|
||||||
img->aa = ANTI_ALIAS;
|
img->aa = ANTI_ALIAS;
|
||||||
img->alpha = ALPHA_LAYER;
|
img->alpha = ALPHA_LAYER;
|
||||||
img->multi.cap = img->multi.cnt = 0;
|
img->multi.cap = img->multi.cnt = 0;
|
||||||
img->multi.animate = false;
|
img->multi.animate = options->animate;
|
||||||
img->multi.length = img->multi.repeat = 0;
|
img->multi.length = 0;
|
||||||
|
|
||||||
img->cmod = imlib_create_color_modifier();
|
img->cmod = imlib_create_color_modifier();
|
||||||
img->gamma = MIN(MAX(options->gamma, -GAMMA_RANGE), GAMMA_RANGE);
|
img->gamma = MIN(MAX(options->gamma, -GAMMA_RANGE), GAMMA_RANGE);
|
||||||
|
@ -160,7 +160,7 @@ bool img_load_gif(img_t *img, const fileinfo_t *file)
|
||||||
s_malloc(sizeof(img_frame_t) * img->multi.cap);
|
s_malloc(sizeof(img_frame_t) * img->multi.cap);
|
||||||
}
|
}
|
||||||
img->multi.cnt = img->multi.sel = 0;
|
img->multi.cnt = img->multi.sel = 0;
|
||||||
img->multi.length = img->multi.repeat = 0;
|
img->multi.length = 0;
|
||||||
|
|
||||||
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
|
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5
|
||||||
gif = DGifOpenFileName(file->path, NULL);
|
gif = DGifOpenFileName(file->path, NULL);
|
||||||
|
@ -194,16 +194,7 @@ bool img_load_gif(img_t *img, const fileinfo_t *file)
|
||||||
transp = -1;
|
transp = -1;
|
||||||
|
|
||||||
delay = 10 * ((unsigned int) ext[3] << 8 | (unsigned int) ext[2]);
|
delay = 10 * ((unsigned int) ext[3] << 8 | (unsigned int) ext[2]);
|
||||||
if (delay)
|
|
||||||
delay = MAX(delay, MIN_GIF_DELAY);
|
|
||||||
|
|
||||||
disposal = (unsigned int) ext[1] >> 2 & 0x7;
|
disposal = (unsigned int) ext[1] >> 2 & 0x7;
|
||||||
} else if (ext_code == APPLICATION_EXT_FUNC_CODE) {
|
|
||||||
if (ext[0] == 11 && memcmp(ext+1, "NETSCAPE2.0", 11) == 0) {
|
|
||||||
DGifGetExtensionNext(gif, &ext);
|
|
||||||
if (ext && ext[0] == 3 && ext[1] == 1)
|
|
||||||
img->multi.repeat = ((int) ext[3] << 8 | (int) ext[2]) - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ext = NULL;
|
ext = NULL;
|
||||||
DGifGetExtensionNext(gif, &ext);
|
DGifGetExtensionNext(gif, &ext);
|
||||||
|
@ -289,7 +280,7 @@ bool img_load_gif(img_t *img, const fileinfo_t *file)
|
||||||
img->multi.cap * sizeof(img_frame_t));
|
img->multi.cap * sizeof(img_frame_t));
|
||||||
}
|
}
|
||||||
img->multi.frames[img->multi.cnt].im = im;
|
img->multi.frames[img->multi.cnt].im = im;
|
||||||
img->multi.frames[img->multi.cnt].delay = delay ? delay : GIF_DELAY;
|
img->multi.frames[img->multi.cnt].delay = delay > 0 ? delay : DEF_GIF_DELAY;
|
||||||
img->multi.length += img->multi.frames[img->multi.cnt].delay;
|
img->multi.length += img->multi.frames[img->multi.cnt].delay;
|
||||||
img->multi.cnt++;
|
img->multi.cnt++;
|
||||||
}
|
}
|
||||||
|
@ -308,12 +299,10 @@ bool img_load_gif(img_t *img, const fileinfo_t *file)
|
||||||
imlib_context_set_image(img->im);
|
imlib_context_set_image(img->im);
|
||||||
imlib_free_image();
|
imlib_free_image();
|
||||||
img->im = img->multi.frames[0].im;
|
img->im = img->multi.frames[0].im;
|
||||||
img->multi.animate = GIF_AUTOPLAY;
|
|
||||||
} else if (img->multi.cnt == 1) {
|
} else if (img->multi.cnt == 1) {
|
||||||
imlib_context_set_image(img->multi.frames[0].im);
|
imlib_context_set_image(img->multi.frames[0].im);
|
||||||
imlib_free_image();
|
imlib_free_image();
|
||||||
img->multi.cnt = 0;
|
img->multi.cnt = 0;
|
||||||
img->multi.animate = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imlib_context_set_image(img->im);
|
imlib_context_set_image(img->im);
|
||||||
|
@ -841,21 +830,15 @@ bool img_frame_animate(img_t *img, bool restart)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (img->multi.sel + 1 >= img->multi.cnt) {
|
if (img->multi.sel + 1 >= img->multi.cnt) {
|
||||||
if (restart || GIF_LOOP == 1) {
|
|
||||||
img_frame_goto(img, 0);
|
img_frame_goto(img, 0);
|
||||||
} else if (GIF_LOOP == -1 && img->multi.repeat != 0) {
|
img->dirty = true;
|
||||||
if (img->multi.repeat > 0)
|
return true;
|
||||||
img->multi.repeat--;
|
|
||||||
img_frame_goto(img, 0);
|
|
||||||
} else {
|
|
||||||
img->multi.animate = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (!restart) {
|
} else if (!restart) {
|
||||||
img_frame_goto(img, img->multi.sel + 1);
|
img_frame_goto(img, img->multi.sel + 1);
|
||||||
}
|
|
||||||
img->multi.animate = true;
|
|
||||||
img->dirty = true;
|
img->dirty = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
image.h
2
image.h
|
@ -36,7 +36,6 @@ typedef struct {
|
||||||
int sel;
|
int sel;
|
||||||
bool animate;
|
bool animate;
|
||||||
int length;
|
int length;
|
||||||
int repeat;
|
|
||||||
} multi_img_t;
|
} multi_img_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -51,7 +50,6 @@ typedef struct {
|
||||||
scalemode_t scalemode;
|
scalemode_t scalemode;
|
||||||
float zoom;
|
float zoom;
|
||||||
|
|
||||||
bool re;
|
|
||||||
bool checkpan;
|
bool checkpan;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
bool aa;
|
bool aa;
|
||||||
|
|
|
@ -33,7 +33,7 @@ const options_t *options = (const options_t*) &_options;
|
||||||
|
|
||||||
void print_usage(void)
|
void print_usage(void)
|
||||||
{
|
{
|
||||||
printf("usage: sxiv [-bcfhioqrtvZ] [-G GAMMA] [-g GEOMETRY] [-n NUM] "
|
printf("usage: sxiv [-abcfhioqrtvZ] [-G GAMMA] [-g GEOMETRY] [-n NUM] "
|
||||||
"[-N NAME] [-S DELAY] [-s MODE] [-z ZOOM] FILES...\n");
|
"[-N NAME] [-S DELAY] [-s MODE] [-z ZOOM] FILES...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ void parse_options(int argc, char **argv)
|
||||||
|
|
||||||
_options.scalemode = SCALE_DOWN;
|
_options.scalemode = SCALE_DOWN;
|
||||||
_options.zoom = 1.0;
|
_options.zoom = 1.0;
|
||||||
|
_options.animate = false;
|
||||||
_options.gamma = 0;
|
_options.gamma = 0;
|
||||||
_options.slideshow = 0;
|
_options.slideshow = 0;
|
||||||
|
|
||||||
|
@ -67,11 +68,14 @@ void parse_options(int argc, char **argv)
|
||||||
_options.thumb_mode = false;
|
_options.thumb_mode = false;
|
||||||
_options.clean_cache = false;
|
_options.clean_cache = false;
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "bcfG:g:hin:N:oqrS:s:tvZz:")) != -1) {
|
while ((opt = getopt(argc, argv, "abcfG:g:hin:N:oqrS:s:tvZz:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case '?':
|
case '?':
|
||||||
print_usage();
|
print_usage();
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
case 'a':
|
||||||
|
_options.animate = true;
|
||||||
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
_options.hide_bar = true;
|
_options.hide_bar = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -34,6 +34,7 @@ typedef struct {
|
||||||
/* image: */
|
/* image: */
|
||||||
scalemode_t scalemode;
|
scalemode_t scalemode;
|
||||||
float zoom;
|
float zoom;
|
||||||
|
bool animate;
|
||||||
int gamma;
|
int gamma;
|
||||||
int slideshow;
|
int slideshow;
|
||||||
|
|
||||||
|
|
7
sxiv.1
7
sxiv.1
|
@ -3,7 +3,7 @@
|
||||||
sxiv \- Simple X Image Viewer
|
sxiv \- Simple X Image Viewer
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B sxiv
|
.B sxiv
|
||||||
.RB [ \-bcfhioqrtvZ ]
|
.RB [ \-abcfhioqrtvZ ]
|
||||||
.RB [ \-G
|
.RB [ \-G
|
||||||
.IR GAMMA ]
|
.IR GAMMA ]
|
||||||
.RB [ \-g
|
.RB [ \-g
|
||||||
|
@ -31,6 +31,9 @@ Please note, that the fullscreen mode requires an EWMH/NetWM compliant window
|
||||||
manager.
|
manager.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
|
.B \-a
|
||||||
|
Play animations of multi-frame images.
|
||||||
|
.TP
|
||||||
.B \-b
|
.B \-b
|
||||||
Do not show info bar on bottom of window.
|
Do not show info bar on bottom of window.
|
||||||
.TP
|
.TP
|
||||||
|
@ -208,7 +211,7 @@ Go to the next frame of a multi-frame image.
|
||||||
Go to the previous frame of a multi-frame image.
|
Go to the previous frame of a multi-frame image.
|
||||||
.TP
|
.TP
|
||||||
.B Ctrl-Space
|
.B Ctrl-Space
|
||||||
Play/pause animation of a multi-frame image.
|
Play/stop animations of multi-frame images.
|
||||||
.SS Panning
|
.SS Panning
|
||||||
.TP
|
.TP
|
||||||
.BR h ", " Left
|
.BR h ", " Left
|
||||||
|
|
Loading…
Reference in New Issue