From d9e9f33c3210b71c07c4268415ff9b1555819b3f Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Sat, 13 Sep 2008 00:02:38 -0700 Subject: Major changes. Optional XShm support, bilinear scaling, caching WIP --- Makefile | 2 +- src/bmp.c | 24 ++++---- src/gif.c | 6 +- src/jpeg.c | 4 +- src/main.c | 199 +++++++++++++++++++++++++++++++------------------------------ src/meh.h | 19 +++++- src/png.c | 10 ++-- src/xlib.c | 182 +++++++++++++++++++++++++++++++++++++++---------------- 8 files changed, 269 insertions(+), 177 deletions(-) diff --git a/Makefile b/Makefile index 4622ca5..6d9243d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ SRCFILES := $(wildcard src/*.c) OBJFILES := $(SRCFILES:%.c=%.o) DEPFILES := $(OBJFILES:%.o=%.d) CLEANFILES := $(CLEANFILES) $(DEPFILES) $(OBJFILES) meh -LIBS := -lX11 -ljpeg -lpng -lgif +LIBS := -lX11 -lXext -ljpeg -lpng -lgif meh: $(OBJFILES) $(CC) -o $@ $(OBJFILES) $(LIBS) diff --git a/src/bmp.c b/src/bmp.c index 6d0daa2..4024521 100644 --- a/src/bmp.c +++ b/src/bmp.c @@ -55,13 +55,13 @@ struct image *bmp_open(FILE *f){ if(headersize == 12){ /* OS/2 v1 */ b->ncolors = 0; fseek(f, 18, SEEK_SET); - b->img.width = getshort(f); - b->img.height = getshort(f); + b->img.bufwidth = getshort(f); + b->img.bufheight = getshort(f); b->compression = 0; }else{ fseek(f, 18, SEEK_SET); - b->img.width = getlong(f); - b->img.height = getlong(f); + b->img.bufwidth = getlong(f); + b->img.bufheight = getlong(f); fseek(f, 28, SEEK_SET); b->bpp = getshort(f); @@ -83,7 +83,7 @@ struct image *bmp_open(FILE *f){ } if(b->bpp >= 16){ - b->rowwidth = b->img.width * b->bpp / 8; + b->rowwidth = b->img.bufwidth * b->bpp / 8; b->colours = NULL; }else{ int i; @@ -96,7 +96,7 @@ struct image *bmp_open(FILE *f){ if(headersize != 12) getc(f); } - b->rowwidth = (b->img.width * b->bpp + 7) / 8; + b->rowwidth = (b->img.bufwidth * b->bpp + 7) / 8; } if(b->rowwidth & 3){ b->rowwidth += 4 - (b->rowwidth & 3); @@ -116,7 +116,7 @@ static int readrow(struct image *img, unsigned char *row, unsigned char *buf){ struct bmp_t *b = (struct bmp_t *)img; unsigned int x, i = 0; if(b->bpp == 24 || b->bpp == 32){ - for(x = 0; x < img->width * 3; x+=3){ + for(x = 0; x < img->bufwidth * 3; x+=3){ buf[x + 2] = row[i++]; buf[x + 1] = row[i++]; buf[x + 0] = row[i++]; @@ -124,7 +124,7 @@ static int readrow(struct image *img, unsigned char *row, unsigned char *buf){ i++; } }else if(b->bpp == 16){ - for(x = 0; x < img->width * 3; x+=3){ + for(x = 0; x < img->bufwidth * 3; x+=3){ unsigned short c; c = row[i++]; c |= row[i++] << 8; @@ -134,7 +134,7 @@ static int readrow(struct image *img, unsigned char *row, unsigned char *buf){ int mask; int pixelsperbit = 8 / b->bpp; mask = ~((~0) << b->bpp); - for(x = 0; x < img->width; x++){ + for(x = 0; x < img->bufwidth; x++){ unsigned char c = ((row[i / pixelsperbit]) >> ((8 - ((i+1) % pixelsperbit) * b->bpp)) % 8) & mask; *buf++ = b->colours[c].r; *buf++ = b->colours[c].g; @@ -156,11 +156,11 @@ int bmp_read(struct image *img){ FILE *f = b->f; row = malloc(b->rowwidth); - dy = img->width * 3; - i = img->height * dy; + dy = img->bufwidth * 3; + i = img->bufheight * dy; fseek(f, b->bitmapoffset, SEEK_SET); - for(y = img->height; y; y--){ + for(y = img->bufheight; y; y--){ i -= dy; if(fread(row, 1, b->rowwidth, f) != b->rowwidth || readrow(img, row, &img->buf[i])){ free(row); diff --git a/src/gif.c b/src/gif.c index ae15418..8da6554 100644 --- a/src/gif.c +++ b/src/gif.c @@ -42,8 +42,8 @@ static struct image *gif_open(FILE *f){ g->f = f; g->gif = gif; - g->img.width = gif->SWidth; - g->img.height = gif->SHeight; + g->img.bufwidth = gif->SWidth; + g->img.bufheight = gif->SHeight; return (struct image *)g; } @@ -70,7 +70,7 @@ static int gif_read(struct image *img){ return 1; } - for(i = 0; i < img->width * img->height; i++){ + for(i = 0; i < img->bufwidth * img->bufheight; i++){ unsigned char idx = s->RasterBits[i]; img->buf[j++] = colormap[idx].Red; img->buf[j++] = colormap[idx].Green; diff --git a/src/jpeg.c b/src/jpeg.c index 797bdb4..5745541 100644 --- a/src/jpeg.c +++ b/src/jpeg.c @@ -53,8 +53,8 @@ static struct image *jpeg_open(FILE *f){ jpeg_calc_output_dimensions(&j->cinfo); - j->img.width = j->cinfo.output_width; - j->img.height = j->cinfo.output_height; + j->img.bufwidth = j->cinfo.output_width; + j->img.bufheight = j->cinfo.output_height; return (struct image *)j; } diff --git a/src/main.c b/src/main.c index a8d043b..99fedbb 100644 --- a/src/main.c +++ b/src/main.c @@ -41,38 +41,51 @@ void usage(){ exit(EXIT_FAILURE); } -struct image *imgopen(FILE *f){ +struct image *newimage(FILE *f){ struct image *img = NULL; struct imageformat **fmt = formats; for(fmt = formats; *fmt; fmt++){ if((img = (*fmt)->open(f))){ img->fmt = *fmt; + img->ximg = NULL; + img->redraw = 1; + img->state = NONE; return img; } } return NULL; } +struct image *imgopen(FILE *f){ + return newimage(f); +} + static int imageslen; static int imageidx; static char **images; -static char *filename = NULL; +//static char *filename = NULL; static int width = 0, height = 0; static struct image *img = NULL; -static XImage *ximg = NULL; -static int redraw = 0; + +void freeimage(struct image *img){ + if(img){ + if(img->ximg) + freeXImg(img->ximg); + if(img->buf) + free(img->buf); + free(img); + } +} void nextimage(){ if(++imageidx == imageslen) imageidx = 0; - filename = images[imageidx]; } void previmage(){ if(--imageidx < 0) imageidx = imageslen - 1; - filename = images[imageidx]; } static void (*direction)() = nextimage; @@ -85,7 +98,7 @@ void handlekeypress(XEvent *event){ exit(0); break; case XK_Return: - puts(filename); + // puts(filename); fflush(stdout); break; case XK_t: @@ -95,17 +108,12 @@ void handlekeypress(XEvent *event){ direction = key == XK_t ? nextimage : previmage; direction(); /* Pass through */ + freeimage(img); + img = NULL; + break; case XK_r: - if(img){ - if(img->buf) - free(img->buf); - free(img); - } - if(ximg) - XDestroyImage(ximg); - ximg = NULL; + freeimage(img); img = NULL; - redraw = 1; break; } } @@ -116,18 +124,30 @@ void handleevent(XEvent *event){ if(width != event->xconfigure.width || height != event->xconfigure.height){ width = event->xconfigure.width; height = event->xconfigure.height; - redraw = 1; - if(ximg) - XDestroyImage(ximg); - ximg = NULL; + if(img){ + if(img->ximg) + XDestroyImage(img->ximg); + img->ximg = NULL; + img->redraw = 1; + if(img->state == LINEARDRAWN) + img->state = LINEAR; + else if(img->state == BILINEARDRAWN) + img->state = BILINEAR; + } /* Some window managers need reminding */ if(img) - setaspect(img->width, img->height); + setaspect(img->bufwidth, img->bufheight); } break; case Expose: - redraw = 1; + if(img){ + img->redraw = 1; + if(img->state == LINEARDRAWN) + img->state = LINEAR; + else if(img->state == BILINEARDRAWN) + img->state = BILINEAR; + } break; case KeyPress: handlekeypress(event); @@ -135,51 +155,58 @@ void handleevent(XEvent *event){ } } -void doredraw(){ - if(!img){ - if(!filename) - return; - const char *firstimg = filename; - while(!img){ - FILE *f; - if((f = fopen(filename, "rb"))){ - if((img = imgopen(f))) - break; - else - fprintf(stderr, "Invalid format '%s'\n", filename); - }else{ - fprintf(stderr, "Cannot open '%s'\n", filename); +struct image *imageopen2(){ + char *filename = images[imageidx]; + struct image *i; + FILE *f; + if((f = fopen(filename, "rb"))){ + if((i = imgopen(f))) + return i; + else + fprintf(stderr, "Invalid format '%s'\n", filename); + }else{ + fprintf(stderr, "Cannot open '%s'\n", filename); + } + return NULL; +} + +int doredraw(struct image **i){ + if(!*i){ + int firstimg = imageidx; + while(!*i){ + if((*i = imageopen2(images[imageidx]))){ + break; } if(mode == MODE_CTL) - return; + return 0; direction(); - if(filename == firstimg){ + if(imageidx == firstimg){ fprintf(stderr, "No valid images to view\n"); exit(EXIT_FAILURE); } } - setaspect(img->width, img->height); + setaspect((*i)->bufwidth, (*i)->bufheight); - img->buf = malloc(3 * img->width * img->height); - if(img->fmt->read(img)){ + (*i)->buf = malloc(3 * (*i)->bufwidth * (*i)->bufheight); + if((*i)->fmt->read(*i)){ fprintf(stderr, "read error!\n"); } - img->fmt->close(img); - - /* Allow for some events to be read, read is slow */ - /*while(XPending(display)){ - XEvent event; - XNextEvent(display, &event); - handleevent(&event); - }*/ + (*i)->fmt->close(*i); + return 1; + }else if(width && height){ + if(!(*i)->ximg){ + (*i)->ximg = getimage(*i, width, height); + return 1; + }else if((*i)->redraw){ /* TODO */ + drawimage((*i)->ximg, width, height); + (*i)->redraw = 0; + return 1; + } } - if(!ximg) - ximg = getimage(img, width, height); - drawimage(ximg, width, height); + return 0; } - void run(){ int xfd; xfd = ConnectionNumber(display); @@ -187,8 +214,6 @@ void run(){ struct timeval tv0 = {0, 0}; struct timeval *tv = &tv0; - char buf[512]; - int bufidx = 0; for(;;){ FD_ZERO(&fds); FD_SET(xfd, &fds); @@ -200,48 +225,26 @@ void run(){ } if(FD_ISSET(0, &fds)){ assert(mode == MODE_CTL); - int n = read(0, buf, 512 - bufidx); - if(n == -1){ - perror("read failed"); - }else if(n == 0){ - fprintf(stderr, "done reading\n"); - exit(0); - } - char *p; - p = &buf[bufidx]; - bufidx+=n; - for(; n > 0; p++, n--){ - if(*p == '\n' || *p == '\0'){ - n--; - *p = '\0'; - strcpy(filename, buf); - for(;n && (*p == '\0' || *p == '\n'); p++, n--); - bufidx = n; - if(n) - memmove(buf, p, n); - if(img){ - if(img->buf) - free(img->buf); - free(img); - } - if(ximg) - XDestroyImage(ximg); - ximg = NULL; - img = NULL; - redraw = 1; - tv = &tv0; - } - } + exit(1); } - if(!XPending(display) && ret == 0 && redraw){ - doredraw(); - tv = NULL; - } - while(XPending(display)){ - tv = &tv0; - XEvent event; - XNextEvent(display, &event); - handleevent(&event); + if(XPending(display)){ + do{ + tv = &tv0; + XEvent event; + XNextEvent(display, &event); + handleevent(&event); + }while(XPending(display)); + }else if(ret == 0){ + if(!img || img->state != BILINEARDRAWN){ + doredraw(&img); + } + /*else if(!nextimg || !nextimg->ximg || !nextimg->redraw){ + doredraw(&nextimg); + }else if(!previmg || !previmg->ximg || !previmg->redraw){ + doredraw(&previmg); + }else{ + tv = &tv0; + }*/ } } } @@ -254,8 +257,7 @@ int main(int argc, char *argv[]){ if(argc != 2) usage(); mode = MODE_CTL; - filename = malloc(512); - filename[0] = '\0'; + exit(EXIT_FAILURE); }else if(!strcmp(argv[1], "-list")){ if(argc != 2) usage(); @@ -267,7 +269,6 @@ int main(int argc, char *argv[]){ images = &argv[1]; imageslen = argc-1; imageidx = 0; - filename = argv[1]; } xinit(); run(); diff --git a/src/meh.h b/src/meh.h index 521e4a9..d7f160e 100644 --- a/src/meh.h +++ b/src/meh.h @@ -1,6 +1,8 @@ #include +#include "X11/Xlib.h" + struct image; struct imageformat{ @@ -9,14 +11,25 @@ struct imageformat{ void (*close)(struct image *); }; +typedef enum{ + NONE, + IMG, + ALLOC, + LINEAR, + LINEARDRAWN, + BILINEAR, + BILINEARDRAWN, +} drawstate; + struct image{ unsigned char *buf; - unsigned int width, height; + unsigned int bufwidth, bufheight; struct imageformat *fmt; + int redraw; + drawstate state; + XImage *ximg; }; -#include "X11/Xlib.h" - XImage *ximage(struct image *img, unsigned int width, unsigned int height); void setaspect(unsigned int w, unsigned int h); void xinit(); diff --git a/src/png.c b/src/png.c index fa6e9f7..6f567b1 100644 --- a/src/png.c +++ b/src/png.c @@ -54,8 +54,8 @@ struct image *png_open(FILE *f){ png_read_info(p->png_ptr, p->info_ptr); - p->img.width = png_get_image_width(p->png_ptr, p->info_ptr); - p->img.height = png_get_image_height(p->png_ptr, p->info_ptr); + p->img.bufwidth = png_get_image_width(p->png_ptr, p->info_ptr); + p->img.bufheight = png_get_image_height(p->png_ptr, p->info_ptr); return (struct image *)p; } @@ -91,9 +91,9 @@ int png_read(struct image *img){ png_read_update_info(p->png_ptr, p->info_ptr); } - row_pointers = (png_bytepp)malloc(img->height * sizeof(png_bytep)); - for(y = 0; y < img->height; y++) - row_pointers[y] = img->buf + y * img->width * 3; + row_pointers = (png_bytepp)malloc(img->bufheight * sizeof(png_bytep)); + for(y = 0; y < img->bufheight; y++) + row_pointers[y] = img->buf + y * img->bufwidth * 3; png_read_image(p->png_ptr, row_pointers); free(row_pointers); diff --git a/src/xlib.c b/src/xlib.c index d36a931..511e740 100644 --- a/src/xlib.c +++ b/src/xlib.c @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "meh.h" @@ -16,80 +19,139 @@ int screen; Window window; GC gc; -static unsigned int getshift(unsigned int mask){ -#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) - return __builtin_ctz(mask); -#else - int i = 0; - while((mask & 1) == 0){ - i++; - mask >>= 1; +int xshm = 0; + +#define GETVAL(x, c) (ibuf[(x) * 3 + (c)]) +#define GETLVAL(x, y, u, v, c) (( \ + (GETVAL((x), (c)) * (1023^(u)) + GETVAL((x)+1, (c)) * (u)) * (1023^(v)) + \ + (GETVAL((x)+img->bufwidth, (c)) * (1023^(u)) + GETVAL((x)+img->bufwidth+1, (c)) * (u)) * (v)) >> 20) + +void scale(struct image *img, XImage *ximg){ + int x, y; + unsigned char * __restrict__ ibuf; + char* __restrict__ newBuf = ximg->data; + unsigned int jdy = ximg->bytes_per_line / 4 - ximg->width; + unsigned int dx = (img->bufwidth << 10) / ximg->width; + + struct timeval t0; + struct timeval t1; + gettimeofday(&t0, NULL); + + for(y = 0; y < ximg->height; y++){ + unsigned int bufy = (y << 10) * img->bufheight / ximg->height; + unsigned int v_ratio = (bufy & 1023); + unsigned int bufx = img->bufwidth / ximg->width; + unsigned char *ibuf = &img->buf[y * img->bufheight / ximg->height * img->bufwidth * 3]; + for(x = ximg->width; x; x--){ + unsigned int u_ratio = (bufx & 1023); + + *newBuf++ = GETLVAL(bufx >> 10, bufy >> 10, u_ratio, v_ratio, 2); + *newBuf++ = GETLVAL(bufx >> 10, bufy >> 10, u_ratio, v_ratio, 1); + *newBuf++ = GETLVAL(bufx >> 10, bufy >> 10, u_ratio, v_ratio, 0); + newBuf++; + + bufx += dx; + } + newBuf += jdy; } - return i; -#endif + + gettimeofday(&t1, NULL); + printf("%i ms\n", ((t1.tv_sec - t0.tv_sec) * 1000000 + t1.tv_usec - t0.tv_usec) / 1000); } + +void linearscale(struct image *img, XImage *ximg){ + unsigned int i; + int x, y; + unsigned int dx; + unsigned char * __restrict__ ibuf; + char* __restrict__ newBuf = ximg->data; + unsigned int jdy = ximg->bytes_per_line / 4 - ximg->width; + dx = (img->bufwidth << 10) / ximg->width; + + struct timeval t0; + struct timeval t1; + gettimeofday(&t0, NULL); + + for(y = 0; y < ximg->height; y++){ + i = 0; + ibuf = &img->buf[y * img->bufheight / ximg->height * img->bufwidth * 3]; + for(x = 0; x < ximg->width; x++){ + *newBuf++ = (ibuf[(i >> 10)*3+2]); + *newBuf++ = (ibuf[(i >> 10)*3+1]); + *newBuf++ = (ibuf[(i >> 10)*3]); + newBuf++; + + i += dx; + } + newBuf += jdy; + } + + gettimeofday(&t1, NULL); + printf("%i ms\n", ((t1.tv_sec - t0.tv_sec) * 1000000 + t1.tv_usec - t0.tv_usec) / 1000); +} + +XShmSegmentInfo *shminfo; XImage *ximage(struct image *img, unsigned int width, unsigned int height) { int depth; XImage *ximg = NULL; Visual *vis; - unsigned int rshift, gshift, bshift; - unsigned int i; - unsigned int x,y; - unsigned int j = 0; depth = DefaultDepth(display, screen); vis = DefaultVisual(display, screen); - rshift = getshift(vis->red_mask); - gshift = getshift(vis->green_mask); - bshift = getshift(vis->blue_mask); - - if (depth >= 24) { - unsigned char *ibuf; - unsigned int dx; - size_t numNewBufBytes = ((sizeof(u_int32_t)) * (width) * (height)); - u_int32_t *newBuf = malloc(numNewBufBytes); - - dx = 1024 * img->width / width; - for(y = 0; y < height; y++){ - i = 0; - ibuf = &img->buf[y * img->height / height * img->width * 3]; - for(x = 0; x < width; x++){ - unsigned int r, g, b; - r = (ibuf[(i >> 10)*3] << rshift) & vis->red_mask; - g = (ibuf[(i >> 10)*3+1] << gshift) & vis->green_mask; - b = (ibuf[(i >> 10)*3+2] << bshift) & vis->blue_mask; - - newBuf[j++] = r | g | b; - i += dx; + if(depth >= 24){ + if(xshm){ + shminfo = malloc(sizeof(XShmSegmentInfo)); + if(!(ximg = XShmCreateImage(display, + CopyFromParent, depth, + ZPixmap, + NULL, + shminfo, + width, height + ))){ + } + if((shminfo->shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT|0777)) == -1){ + fprintf(stderr, "XShm problems\n"); + exit(1); + } + if((shminfo->shmaddr = ximg->data = shmat(shminfo->shmid, 0, 0)) == (void *)-1){ + fprintf(stderr, "XShm problems\n"); + exit(1); + } + shminfo->readOnly = False; + if(!XShmAttach(display, shminfo)){ + fprintf(stderr, "XShm problems, falling back to to XImage\n"); + xshm = 0; } } - - ximg = XCreateImage (display, - CopyFromParent, depth, - ZPixmap, 0, - (char *) newBuf, - width, height, - 32, 0 - ); + if(!xshm){ + ximg = XCreateImage(display, + CopyFromParent, depth, + ZPixmap, 0, + NULL, + width, height, + 32, 0 + ); + ximg->data = malloc(ximg->bytes_per_line * ximg->height + /*HACK*/(ximg->bytes_per_line+1)); + XInitImage(ximg); + } + scale(img, ximg); }else{ /* TODO other depths */ - fprintf(stderr, "This program does not support display depths less than 24.\n"); + fprintf(stderr, "This program does not yet support display depths <24.\n"); exit(1); return NULL; } - XInitImage(ximg); - return ximg; } XImage *getimage(struct image *img, unsigned int width, unsigned int height){ - if(width * img->height > height * img->width){ - return ximage(img, img->width * height / img->height, height); + if(width * img->bufheight > height * img->bufwidth){ + return ximage(img, img->bufwidth * height / img->bufheight, height); }else{ - return ximage(img, width, img->height * width / img->width); + return ximage(img, width, img->bufheight * width / img->bufwidth); } } @@ -103,7 +165,7 @@ void drawimage(XImage *ximg, unsigned int width, unsigned int height){ if(xoffset){ rects[0].width = rects[1].width = xoffset; rects[0].height = rects[1].height = height; - rects[1].x = width-xoffset; + rects[1].x = width - xoffset; rects[1].y = 0; }else if(yoffset){ rects[0].width = rects[1].width = width; @@ -113,10 +175,26 @@ void drawimage(XImage *ximg, unsigned int width, unsigned int height){ } XFillRectangles(display, window, gc, rects, 2); } - XPutImage(display, window, gc, ximg, 0, 0, xoffset, yoffset, ximg->width, ximg->height); + if(xshm){ + XShmPutImage(display, window, gc, ximg, 0, 0, xoffset, yoffset, ximg->width, ximg->height, False); + }else{ + XPutImage(display, window, gc, ximg, 0, 0, xoffset, yoffset, ximg->width, ximg->height); + } XFlush(display); } +void freeXImg(XImage *ximg){ + if(xshm){ + XShmDetach(display, shminfo); + XDestroyImage(ximg); + shmdt(shminfo->shmaddr); + shmctl(shminfo->shmid, IPC_RMID, 0); + free(shminfo); + }else{ + XDestroyImage(ximg); + } +} + void setaspect(unsigned int w, unsigned int h){ XSizeHints *hints = XAllocSizeHints(); -- cgit v1.2.3