lel

farbfeld image viewer
git clone git://git.2f30.org/lel
Log | Files | Refs | README | LICENSE

commit 049c5f73a2b1174d54d033e10bd5d34373f77884
parent 0bcb47ab3451309490d3f4a856b755ea43ff282e
Author: sin <sin@2f30.org>
Date:   Fri,  1 Aug 2014 19:06:32 +0100

Add primitive support for multiple images

Signed-off-by: Hiltjo Posthuma <hiltjo@codemadness.org>

Diffstat:
MLICENSE | 1+
MTODO | 1-
Mlel.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 <hiltjo@codemadness.org> +(c) 2014 sin <sin@2f30.org> 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_FORMAT)) return -1; if(memcmp(hdr, "imagefile", 9)) return -1; - imgwidth = ntohl((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | (hdr[12] << 24)); - imgheight = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | (hdr[16] << 24)); - if(imgwidth <= 0 || imgheight <= 0) + img->width = ntohl((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | (hdr[12] << 24)); + img->height = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | (hdr[16] << 24)); + 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 int bytesperline, 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, winheight); 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->view.zoomfact); 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->xconfigure.height) { 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; }