Add primitive support for multiple images - lel - Farbfeld image viewer | |
git clone git://git.codemadness.org/lel | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 049c5f73a2b1174d54d033e10bd5d34373f77884 | |
parent 0bcb47ab3451309490d3f4a856b755ea43ff282e | |
Author: sin <[email protected]> | |
Date: Fri, 1 Aug 2014 19:06:32 +0100 | |
Add primitive support for multiple images | |
Signed-off-by: Hiltjo Posthuma <[email protected]> | |
Diffstat: | |
M LICENSE | 1 + | |
M TODO | 1 - | |
M lel.c | 264 ++++++++++++++++++++---------… | |
3 files changed, 176 insertions(+), 90 deletions(-) | |
--- | |
diff --git a/LICENSE b/LICENSE | |
@@ -1,6 +1,7 @@ | |
MIT/X Consortium License | |
(c) 2014 Hiltjo Posthuma <[email protected]> | |
+(c) 2014 sin <[email protected]> | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
diff --git a/TODO b/TODO | |
@@ -5,7 +5,6 @@ | |
[ ] enter key or some other key should print the current filename to stdout. | |
[ ] for pictures which use an alpha mask use a checked pattern, similar to: | |
http://www.modejong.com/blog/Media/Ghost_TransparentBG_400x300.jpg | |
-[ ] support multiple filenames as arguments, use hotkeys to switch image. | |
[ ] zooming | |
[ ] improve performance (cull only the visible part?), use XGetImage()… | |
[ ] improve zoomfact "steps". | |
diff --git a/lel.c b/lel.c | |
@@ -1,3 +1,4 @@ | |
+/* See LICENSE file for copyright and license details. */ | |
#include <unistd.h> | |
#include <stdarg.h> | |
#include <stdint.h> | |
@@ -24,6 +25,23 @@ enum { NONE = 0, LOADED = 1, SCALED = 2, DRAWN = 4 }; | |
/* View mode. */ | |
enum { ASPECT = 0, FULL_ASPECT, FULL_STRETCH }; | |
+struct img { | |
+ char *filename; | |
+ FILE *fp; | |
+ int state; | |
+ int width; | |
+ int height; | |
+ uint8_t *buf; | |
+ struct view { | |
+ int panxoffset; | |
+ int panyoffset; | |
+ float zoomfact; | |
+ } view; | |
+}; | |
+ | |
+static struct img *imgs; | |
+static struct img *cimg; | |
+static size_t nimgs; | |
static int viewmode = ASPECT; | |
static char *wintitle = APP_NAME; | |
static XImage *ximg = NULL; | |
@@ -33,12 +51,11 @@ static Window win; | |
static GC gc; | |
static int screen, xfd; | |
static int running = 1; | |
-static int imgstate = NONE; | |
-static int imgwidth, imgheight; | |
-static uint8_t *imgbuf; | |
static int winx, winy, winwidth = 320, winheight = 240; | |
-static int panxoffset = 0, panyoffset = 0; | |
-static float zoomfact = 1.0, zoominc = 0.25; | |
+static float zoominc = 0.25; | |
+static int tflag; | |
+static int wflag; | |
+static int hflag; | |
void | |
die(const char *fmt, ...) | |
@@ -72,53 +89,70 @@ usage(void) | |
} | |
int | |
-if_open(FILE *f) | |
+if_open(struct img *img) | |
{ | |
uint8_t hdr[17]; | |
- if (fread(hdr, 1, strlen(HEADER_FORMAT), f) != strlen(HEADER_FORMAT)) | |
+ if (img->state & LOADED) | |
+ return 0; | |
+ | |
+ if (fread(hdr, 1, strlen(HEADER_FORMAT), img->fp) != strlen(HEADER_FOR… | |
return -1; | |
if(memcmp(hdr, "imagefile", 9)) | |
return -1; | |
- imgwidth = ntohl((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | (h… | |
- imgheight = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | … | |
- if(imgwidth <= 0 || imgheight <= 0) | |
+ img->width = ntohl((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | … | |
+ img->height = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) … | |
+ if(img->width <= 0 || img->height <= 0) | |
return -1; | |
+ if(!(img->buf = malloc(img->width * img->height * 4))) | |
+ die("can't malloc\n"); | |
+ | |
return 0; | |
} | |
int | |
-if_read(FILE *f) | |
+if_read(struct img *img) | |
{ | |
int i, j, off, row_len; | |
uint8_t *row; | |
- row_len = imgwidth * strlen("RGBA"); | |
+ if (img->state & LOADED) | |
+ return 0; | |
+ | |
+ row_len = img->width * strlen("RGBA"); | |
if(!(row = malloc(row_len))) | |
- return 1; | |
+ return -1; | |
- for(off = 0, i = 0; i < imgheight; ++i) { | |
- if(fread(row, 1, (size_t)row_len, f) != (size_t)row_len) { | |
+ for(off = 0, i = 0; i < img->height; ++i) { | |
+ if(fread(row, 1, (size_t)row_len, img->fp) != (size_t)row_len)… | |
free(row); | |
die("unexpected EOF or row-skew at %d\n", i); | |
} | |
for(j = 0; j < row_len; j += 4, off += 4) { | |
- imgbuf[off] = row[j]; | |
- imgbuf[off + 1] = row[j + 1]; | |
- imgbuf[off + 2] = row[j + 2]; | |
- imgbuf[off + 3] = row[j + 3]; | |
+ img->buf[off] = row[j]; | |
+ img->buf[off + 1] = row[j + 1]; | |
+ img->buf[off + 2] = row[j + 2]; | |
+ img->buf[off + 3] = row[j + 3]; | |
} | |
} | |
free(row); | |
- imgstate |= LOADED; | |
+ img->state |= LOADED; | |
return 0; | |
} | |
+void | |
+if_close(struct img *img) | |
+{ | |
+ img->state &= ~LOADED; | |
+ rewind(img->fp); | |
+ free(img->buf); | |
+} | |
+ | |
/* NOTE: will be removed later, for debugging alpha mask */ | |
#if 0 | |
void | |
@@ -137,6 +171,58 @@ normalsize(char *newbuf) | |
} | |
#endif | |
+void | |
+loadimg(void) | |
+{ | |
+ if(if_open(cimg)) | |
+ die("can't open image (invalid format?)\n"); | |
+ if(if_read(cimg)) | |
+ die("can't read image\n"); | |
+ if(!wflag) | |
+ winwidth = cimg->width; | |
+ if(!hflag) | |
+ winheight = cimg->height; | |
+ if(!tflag) | |
+ wintitle = cimg->filename; | |
+} | |
+ | |
+void | |
+reloadimg(void) | |
+{ | |
+ loadimg(); | |
+ XResizeWindow(dpy, win, winwidth, winheight); | |
+ XStoreName(dpy, win, wintitle); | |
+ XFlush(dpy); | |
+} | |
+ | |
+void | |
+nextimg(void) | |
+{ | |
+ struct img *tmp = cimg; | |
+ | |
+ cimg++; | |
+ if (cimg >= &imgs[nimgs]) | |
+ cimg = &imgs[0]; | |
+ if (tmp != cimg) { | |
+ if_close(tmp); | |
+ reloadimg(); | |
+ } | |
+} | |
+ | |
+void | |
+previmg(void) | |
+{ | |
+ struct img *tmp = cimg; | |
+ | |
+ cimg--; | |
+ if (cimg < &imgs[0]) | |
+ cimg = &imgs[nimgs - 1]; | |
+ if (tmp != cimg) { | |
+ if_close(tmp); | |
+ reloadimg(); | |
+ } | |
+} | |
+ | |
/* scales imgbuf data to newbuf (ximg->data), nearest neighbour. */ | |
void | |
scale(unsigned int width, unsigned int height, unsigned int bytesperline, | |
@@ -146,10 +232,10 @@ scale(unsigned int width, unsigned int height, unsigned i… | |
unsigned int jdy, dx, bufx, x, y; | |
jdy = bytesperline / 4 - width; | |
- dx = (imgwidth << 10) / width; | |
+ dx = (cimg->width << 10) / width; | |
for(y = 0; y < height; y++) { | |
- bufx = imgwidth / width; | |
- ibuf = &imgbuf[y * imgheight / height * imgwidth * 4]; | |
+ bufx = cimg->width / width; | |
+ ibuf = &cimg->buf[y * cimg->height / height * cimg->width * 4]; | |
for(x = 0; x < width; x++) { | |
*newbuf++ = (ibuf[(bufx >> 10)*4+2]); | |
@@ -195,17 +281,17 @@ scaleview(void) | |
ximage(winwidth, winheight); | |
break; | |
case FULL_ASPECT: | |
- if(winwidth * imgheight > winheight * imgwidth) | |
- ximage(imgwidth * winheight / imgheight, winheight); | |
+ if(winwidth * cimg->height > winheight * cimg->width) | |
+ ximage(cimg->width * winheight / cimg->height, winheig… | |
else | |
- ximage(winwidth, imgheight * winwidth / imgwidth); | |
+ ximage(winwidth, cimg->height * winwidth / cimg->width… | |
break; | |
case ASPECT: | |
default: | |
- ximage(imgwidth * zoomfact, imgheight * zoomfact); | |
+ ximage(cimg->width * cimg->view.zoomfact, cimg->height * cimg-… | |
break; | |
} | |
- imgstate |= SCALED; | |
+ cimg->state |= SCALED; | |
} | |
void | |
@@ -218,8 +304,8 @@ draw(void) | |
xoffset = (winwidth - ximg->width) / 2; | |
yoffset = (winheight - ximg->height) / 2; | |
/* pan offset */ | |
- xoffset -= panxoffset; | |
- yoffset -= panyoffset; | |
+ xoffset -= cimg->view.panxoffset; | |
+ yoffset -= cimg->view.panyoffset; | |
} | |
XSetForeground(dpy, gc, BlackPixel(dpy, 0)); | |
XFillRectangle(dpy, xpix, gc, 0, 0, winwidth, winheight); | |
@@ -227,17 +313,17 @@ draw(void) | |
XCopyArea(dpy, xpix, win, gc, 0, 0, winwidth, winheight, 0, 0); | |
XFlush(dpy); | |
- imgstate |= DRAWN; | |
+ cimg->state |= DRAWN; | |
} | |
void | |
update(void) | |
{ | |
- if(!(imgstate & LOADED)) | |
+ if(!(cimg->state & LOADED)) | |
return; | |
- if(!(imgstate & SCALED)) | |
+ if(!(cimg->state & SCALED)) | |
scaleview(); | |
- if(!(imgstate & DRAWN)) | |
+ if(!(cimg->state & DRAWN)) | |
draw(); | |
} | |
@@ -247,36 +333,36 @@ setview(int mode) | |
if(viewmode == mode) | |
return; | |
viewmode = mode; | |
- imgstate &= ~(DRAWN | SCALED); | |
+ cimg->state &= ~(DRAWN | SCALED); | |
update(); | |
} | |
void | |
pan(int x, int y) | |
{ | |
- panxoffset -= x; | |
- panyoffset -= y; | |
- imgstate &= ~(DRAWN | SCALED); | |
+ cimg->view.panxoffset -= x; | |
+ cimg->view.panyoffset -= y; | |
+ cimg->state &= ~(DRAWN | SCALED); | |
update(); | |
} | |
void | |
inczoom(float f) | |
{ | |
- if((zoomfact + f) <= 0) | |
+ if((cimg->view.zoomfact + f) <= 0) | |
return; | |
- zoomfact += f; | |
- imgstate &= ~(DRAWN | SCALED); | |
+ cimg->view.zoomfact += f; | |
+ cimg->state &= ~(DRAWN | SCALED); | |
update(); | |
} | |
void | |
zoom(float f) | |
{ | |
- if(f == zoomfact) | |
+ if(f == cimg->view.zoomfact) | |
return; | |
- zoomfact = f; | |
- imgstate &= ~(DRAWN | SCALED); | |
+ cimg->view.zoomfact = f; | |
+ cimg->state &= ~(DRAWN | SCALED); | |
update(); | |
} | |
@@ -352,9 +438,19 @@ keypress(XEvent *ev) | |
zoom(1.0); | |
setview(ASPECT); /* fallthrough */ | |
case XK_r: | |
- panxoffset = 0; | |
- panyoffset = 0; | |
- imgstate &= ~(DRAWN | SCALED); | |
+ cimg->view.panxoffset = 0; | |
+ cimg->view.panyoffset = 0; | |
+ cimg->state &= ~(DRAWN | SCALED); | |
+ update(); | |
+ break; | |
+ case XK_n: | |
+ nextimg(); | |
+ cimg->state &= ~(DRAWN | SCALED); | |
+ update(); | |
+ break; | |
+ case XK_p: | |
+ previmg(); | |
+ cimg->state &= ~(DRAWN | SCALED); | |
update(); | |
break; | |
} | |
@@ -377,11 +473,11 @@ handleevent(XEvent *ev) | |
if(winwidth != ev->xconfigure.width || winheight != ev->xconfi… | |
winwidth = ev->xconfigure.width; | |
winheight = ev->xconfigure.height; | |
- imgstate &= ~(SCALED); | |
+ cimg->state &= ~(SCALED); | |
} | |
break; | |
case Expose: | |
- imgstate &= ~(DRAWN); | |
+ cimg->state &= ~(DRAWN); | |
update(); | |
break; | |
case KeyPress: | |
@@ -421,18 +517,14 @@ run(void) | |
{ | |
XEvent ev; | |
- while(running && !XNextEvent(dpy, &ev)) { | |
+ while(running && !XNextEvent(dpy, &ev)) | |
handleevent(&ev); | |
- } | |
} | |
int | |
main(int argc, char *argv[]) { | |
- char *filename = ""; | |
- FILE *fp = NULL; | |
- int tflag = 0; | |
- int wflag = 0; | |
- int hflag = 0; | |
+ FILE *fp; | |
+ int i, j; | |
ARGBEGIN { | |
case 'a': | |
@@ -466,44 +558,38 @@ main(int argc, char *argv[]) { | |
break; | |
} ARGEND; | |
- if(argc >= 1) { | |
- filename = argv[0]; | |
- if(!(fp = fopen(filename, "rb"))) { | |
- die("can't read %s:", filename); | |
- return EXIT_FAILURE; | |
- } | |
+ if(argc == 0) { | |
+ imgs = calloc(1, sizeof(*imgs)); | |
+ if (!imgs) | |
+ die("can't calloc\n"); | |
+ nimgs = 1; | |
+ imgs[0].filename = "<stdin>"; | |
+ imgs[0].fp = stdin; | |
+ imgs[0].view.zoomfact = 1.0; | |
} else { | |
- filename = "<stdin>"; | |
- fp = stdin; | |
+ imgs = calloc(argc, sizeof(*imgs)); | |
+ if(!imgs) | |
+ die("can't calloc\n"); | |
+ for(i = 0, j = 0; j < argc; j++) { | |
+ fp = fopen(argv[j], "rb"); | |
+ if (!fp) { | |
+ fprintf(stderr, "can't open %s\n", argv[j]); | |
+ continue; | |
+ } | |
+ imgs[i].filename = argv[j]; | |
+ imgs[i].fp = fp; | |
+ imgs[i].view.zoomfact = 1.0; | |
+ i++; | |
+ } | |
+ if (i == 0) | |
+ return EXIT_FAILURE; | |
+ nimgs = i; | |
} | |
- if(!tflag) | |
- wintitle = filename; | |
- | |
- if(if_open(fp)) | |
- die("can't open image (invalid format?)\n"); | |
- if(!(imgbuf = malloc((imgwidth) * (imgheight) * 4))) | |
- die("can't malloc\n"); | |
- if_read(fp); | |
- | |
- if(!wflag) | |
- winwidth = imgwidth; | |
- if(!hflag) | |
- winheight = imgheight; | |
+ cimg = imgs; | |
+ loadimg(); | |
setup(); | |
run(); | |
- if(fp && fp != stdin) | |
- fclose(fp); | |
- | |
- free(imgbuf); | |
- | |
- if(ximg) | |
- XDestroyImage(ximg); | |
- if(xpix) | |
- XFreePixmap(dpy, xpix); | |
- if(dpy) | |
- XCloseDisplay(dpy); | |
- | |
return EXIT_SUCCESS; | |
} |