add png support
This commit is contained in:
parent
501a86cb94
commit
5b4a35ead5
4
LICENSE
4
LICENSE
@ -1,6 +1,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014 markus.teich@stusta.mhn.de
|
Copyright (c) 2014 Markus Teich
|
||||||
|
|
||||||
|
png handling stuff adapted from meh by John Hawthorn
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib
|
|||||||
|
|
||||||
# includes and libs
|
# includes and libs
|
||||||
INCS = -I. -I/usr/include -I${X11INC}
|
INCS = -I. -I/usr/include -I${X11INC}
|
||||||
LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lX11
|
LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lX11 -lpng
|
||||||
|
|
||||||
# flags
|
# flags
|
||||||
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_XOPEN_SOURCE=600
|
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_XOPEN_SOURCE=600
|
||||||
|
4
example
4
example
@ -1,9 +1,11 @@
|
|||||||
sent
|
sent
|
||||||
why?
|
why?
|
||||||
|
@nyan.png
|
||||||
easy to use
|
easy to use
|
||||||
few dependencies (X11)
|
depends on Xlib, libpng
|
||||||
no bloat
|
no bloat
|
||||||
how?
|
how?
|
||||||
sent FILENAME
|
sent FILENAME
|
||||||
one slide per line
|
one slide per line
|
||||||
|
@FILE.png
|
||||||
thanks / questions?
|
thanks / questions?
|
||||||
|
261
sent.c
261
sent.c
@ -1,8 +1,10 @@
|
|||||||
/* See LICENSE for licence details. */
|
/* See LICENSE for licence details. */
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <png.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
@ -19,8 +21,27 @@ char *argv0;
|
|||||||
#define LEN(a) (sizeof(a) / sizeof(a)[0])
|
#define LEN(a) (sizeof(a) / sizeof(a)[0])
|
||||||
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
|
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NONE = 0,
|
||||||
|
LOADED = 1,
|
||||||
|
SCALED = 2,
|
||||||
|
DRAWN = 4
|
||||||
|
} imgstate;
|
||||||
|
|
||||||
|
struct image {
|
||||||
|
unsigned char *buf;
|
||||||
|
unsigned int bufwidth, bufheight;
|
||||||
|
imgstate state;
|
||||||
|
XImage *ximg;
|
||||||
|
FILE *f;
|
||||||
|
png_structp png_ptr;
|
||||||
|
png_infop info_ptr;
|
||||||
|
int numpasses;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *text;
|
char *text;
|
||||||
|
struct image *img;
|
||||||
} Slide;
|
} Slide;
|
||||||
|
|
||||||
/* Purely graphic info */
|
/* Purely graphic info */
|
||||||
@ -32,6 +53,7 @@ typedef struct {
|
|||||||
XSetWindowAttributes attrs;
|
XSetWindowAttributes attrs;
|
||||||
int scr;
|
int scr;
|
||||||
int w, h;
|
int w, h;
|
||||||
|
int uw, uh; /* usable dimensions for drawing text and images */
|
||||||
} XWindow;
|
} XWindow;
|
||||||
|
|
||||||
/* Drawing Context linked list*/
|
/* Drawing Context linked list*/
|
||||||
@ -60,12 +82,11 @@ typedef struct {
|
|||||||
const Arg arg;
|
const Arg arg;
|
||||||
} Shortcut;
|
} Shortcut;
|
||||||
|
|
||||||
/* function definitions used in config.h */
|
static struct image *pngopen(char *filename);
|
||||||
static void advance(const Arg *);
|
static int pngread(struct image *img);
|
||||||
static void quit(const Arg *);
|
static int pngprepare(struct image *img);
|
||||||
|
static void pngscale(struct image *img);
|
||||||
/* config.h for applying patches and the configuration. */
|
static void pngdraw(struct image *img);
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
static Bool xfontisscalable(char *name);
|
static Bool xfontisscalable(char *name);
|
||||||
static XFontStruct *xloadqueryscalablefont(char *name, int size);
|
static XFontStruct *xloadqueryscalablefont(char *name, int size);
|
||||||
@ -75,6 +96,7 @@ static void eprintf(const char *, ...);
|
|||||||
static void load(FILE *fp);
|
static void load(FILE *fp);
|
||||||
static void advance(const Arg *arg);
|
static void advance(const Arg *arg);
|
||||||
static void quit(const Arg *arg);
|
static void quit(const Arg *arg);
|
||||||
|
static void resize(int width, int height);
|
||||||
static void run();
|
static void run();
|
||||||
static void usage();
|
static void usage();
|
||||||
static void xdraw();
|
static void xdraw();
|
||||||
@ -86,7 +108,10 @@ static void bpress(XEvent *);
|
|||||||
static void cmessage(XEvent *);
|
static void cmessage(XEvent *);
|
||||||
static void expose(XEvent *);
|
static void expose(XEvent *);
|
||||||
static void kpress(XEvent *);
|
static void kpress(XEvent *);
|
||||||
static void resize(XEvent *);
|
static void configure(XEvent *);
|
||||||
|
|
||||||
|
/* config.h for applying patches and the configuration. */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
/* Globals */
|
/* Globals */
|
||||||
static Slide *slides = NULL;
|
static Slide *slides = NULL;
|
||||||
@ -100,11 +125,193 @@ static char *opt_font = NULL;
|
|||||||
static void (*handler[LASTEvent])(XEvent *) = {
|
static void (*handler[LASTEvent])(XEvent *) = {
|
||||||
[ButtonPress] = bpress,
|
[ButtonPress] = bpress,
|
||||||
[ClientMessage] = cmessage,
|
[ClientMessage] = cmessage,
|
||||||
[ConfigureNotify] = resize,
|
[ConfigureNotify] = configure,
|
||||||
[Expose] = expose,
|
[Expose] = expose,
|
||||||
[KeyPress] = kpress,
|
[KeyPress] = kpress,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct image *pngopen(char *filename)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
unsigned char buf[8];
|
||||||
|
struct image *img;
|
||||||
|
|
||||||
|
if (!(f = fopen(filename, "rb"))) {
|
||||||
|
eprintf("could not open file %s:", filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(buf, 1, 8, f) != 8 || png_sig_cmp(buf, 1, 8))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
img = malloc(sizeof(struct image));
|
||||||
|
if (!(img->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||||
|
NULL, NULL))) {
|
||||||
|
free(img);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!(img->info_ptr = png_create_info_struct(img->png_ptr))) {
|
||||||
|
png_destroy_read_struct(&img->png_ptr, NULL, NULL);
|
||||||
|
free(img);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (setjmp(png_jmpbuf(img->png_ptr))) {
|
||||||
|
png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL);
|
||||||
|
free(img);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
img->f = f;
|
||||||
|
rewind(f);
|
||||||
|
png_init_io(img->png_ptr, f);
|
||||||
|
png_read_info(img->png_ptr, img->info_ptr);
|
||||||
|
img->bufwidth = png_get_image_width(img->png_ptr, img->info_ptr);
|
||||||
|
img->bufheight = png_get_image_height(img->png_ptr, img->info_ptr);
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pngread(struct image *img)
|
||||||
|
{
|
||||||
|
unsigned int y;
|
||||||
|
png_bytepp row_pointers;
|
||||||
|
|
||||||
|
if (!img)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (img->state & LOADED)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
if (img->buf)
|
||||||
|
free(img->buf);
|
||||||
|
if (!(img->buf = malloc(3 * img->bufwidth * img->bufheight)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(img->png_ptr))) {
|
||||||
|
png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int color_type = png_get_color_type(img->png_ptr, img->info_ptr);
|
||||||
|
int bit_depth = png_get_bit_depth(img->png_ptr, img->info_ptr);
|
||||||
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
||||||
|
png_set_expand(img->png_ptr);
|
||||||
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||||
|
png_set_expand(img->png_ptr);
|
||||||
|
if (png_get_valid(img->png_ptr, img->info_ptr, PNG_INFO_tRNS))
|
||||||
|
png_set_expand(img->png_ptr);
|
||||||
|
if (bit_depth == 16)
|
||||||
|
png_set_strip_16(img->png_ptr);
|
||||||
|
if (color_type == PNG_COLOR_TYPE_GRAY
|
||||||
|
|| color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
|
png_set_gray_to_rgb(img->png_ptr);
|
||||||
|
|
||||||
|
png_color_16 my_background = {.red = 0xff, .green = 0xff, .blue = 0xff};
|
||||||
|
png_color_16p image_background;
|
||||||
|
|
||||||
|
if (png_get_bKGD(img->png_ptr, img->info_ptr, &image_background))
|
||||||
|
png_set_background(img->png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
|
||||||
|
else
|
||||||
|
png_set_background(img->png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 2, 1.0);
|
||||||
|
|
||||||
|
if (png_get_interlace_type(img->png_ptr, img->info_ptr) == PNG_INTERLACE_ADAM7)
|
||||||
|
img->numpasses = png_set_interlace_handling(img->png_ptr);
|
||||||
|
else
|
||||||
|
img->numpasses = 1;
|
||||||
|
png_read_update_info(img->png_ptr, img->info_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(img->png_ptr, row_pointers);
|
||||||
|
free(row_pointers);
|
||||||
|
|
||||||
|
png_destroy_read_struct(&img->png_ptr, &img->info_ptr, NULL);
|
||||||
|
fclose(img->f);
|
||||||
|
img->state |= LOADED;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pngprepare(struct image *img)
|
||||||
|
{
|
||||||
|
int depth = DefaultDepth(xw.dpy, xw.scr);
|
||||||
|
int width = xw.uw;
|
||||||
|
int height = xw.uh;
|
||||||
|
|
||||||
|
if (xw.uw * img->bufheight > xw.uh * img->bufwidth)
|
||||||
|
width = img->bufwidth * xw.uh / img->bufheight;
|
||||||
|
else
|
||||||
|
height = img->bufheight * xw.uw / img->bufwidth;
|
||||||
|
|
||||||
|
if (depth < 24) {
|
||||||
|
eprintf("display depths <24 not supported.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(img->ximg = XCreateImage(xw.dpy, CopyFromParent, depth, ZPixmap, 0,
|
||||||
|
NULL, width, height, 32, 0))) {
|
||||||
|
eprintf("could not create XImage");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(img->ximg->data = malloc(img->ximg->bytes_per_line * height))) {
|
||||||
|
eprintf("could not alloc data section for XImage");
|
||||||
|
XDestroyImage(img->ximg);
|
||||||
|
img->ximg = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!XInitImage(img->ximg)) {
|
||||||
|
eprintf("could not init XImage");
|
||||||
|
free(img->ximg->data);
|
||||||
|
XDestroyImage(img->ximg);
|
||||||
|
img->ximg = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pngscale(img);
|
||||||
|
img->state |= SCALED;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pngscale(struct image *img)
|
||||||
|
{
|
||||||
|
unsigned int x, y;
|
||||||
|
unsigned int width = img->ximg->width;
|
||||||
|
unsigned int height = img->ximg->height;
|
||||||
|
char* __restrict__ newBuf = img->ximg->data;
|
||||||
|
unsigned char * __restrict__ ibuf;
|
||||||
|
unsigned int jdy = img->ximg->bytes_per_line / 4 - width;
|
||||||
|
unsigned int dx = (img->bufwidth << 10) / width;
|
||||||
|
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
unsigned int bufx = img->bufwidth / width;
|
||||||
|
ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];
|
||||||
|
|
||||||
|
for (x = 0; x < width; x++) {
|
||||||
|
*newBuf++ = (ibuf[(bufx >> 10)*3+2]);
|
||||||
|
*newBuf++ = (ibuf[(bufx >> 10)*3+1]);
|
||||||
|
*newBuf++ = (ibuf[(bufx >> 10)*3+0]);
|
||||||
|
newBuf++;
|
||||||
|
bufx += dx;
|
||||||
|
}
|
||||||
|
newBuf += jdy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pngdraw(struct image *img)
|
||||||
|
{
|
||||||
|
int xoffset = (xw.w - img->ximg->width) / 2;
|
||||||
|
int yoffset = (xw.h - img->ximg->height) / 2;
|
||||||
|
XPutImage(xw.dpy, xw.win, dc.gc, img->ximg, 0, 0,
|
||||||
|
xoffset, yoffset, img->ximg->width, img->ximg->height);
|
||||||
|
XFlush(xw.dpy);
|
||||||
|
img->state |= DRAWN;
|
||||||
|
}
|
||||||
|
|
||||||
Bool xfontisscalable(char *name)
|
Bool xfontisscalable(char *name)
|
||||||
{
|
{
|
||||||
@ -177,8 +384,7 @@ struct DC *getfontsize(char *str, size_t len, int *width, int *height)
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
XTextExtents(cur->font, str, len, &unused, &unused, &unused, &info);
|
XTextExtents(cur->font, str, len, &unused, &unused, &unused, &info);
|
||||||
if (info.width > usablewidth * xw.w
|
if (info.width > xw.uw || info.ascent + info.descent > xw.uh)
|
||||||
|| info.ascent + info.descent > usableheight * xw.h)
|
|
||||||
break;
|
break;
|
||||||
pre = cur;
|
pre = cur;
|
||||||
} while ((cur = cur->next));
|
} while ((cur = cur->next));
|
||||||
@ -245,6 +451,8 @@ void load(FILE *fp)
|
|||||||
*p = '\0';
|
*p = '\0';
|
||||||
if (!(slides[i].text = strdup(buf)))
|
if (!(slides[i].text = strdup(buf)))
|
||||||
eprintf("cannot strdup %u bytes:", strlen(buf)+1);
|
eprintf("cannot strdup %u bytes:", strlen(buf)+1);
|
||||||
|
if (slides[i].text[0] == '@')
|
||||||
|
slides[i].img = pngopen(slides[i].text + 1);
|
||||||
}
|
}
|
||||||
if (slides)
|
if (slides)
|
||||||
slides[i].text = NULL;
|
slides[i].text = NULL;
|
||||||
@ -256,8 +464,14 @@ void advance(const Arg *arg)
|
|||||||
int new_idx = idx + arg->i;
|
int new_idx = idx + arg->i;
|
||||||
LIMIT(new_idx, 0, slidecount-1);
|
LIMIT(new_idx, 0, slidecount-1);
|
||||||
if (new_idx != idx) {
|
if (new_idx != idx) {
|
||||||
|
if (slides[idx].img)
|
||||||
|
slides[idx].img->state &= ~(DRAWN | SCALED);
|
||||||
idx = new_idx;
|
idx = new_idx;
|
||||||
xdraw();
|
xdraw();
|
||||||
|
if (slidecount > idx + 1 && slides[idx + 1].img && !pngread(slides[idx + 1].img))
|
||||||
|
eprintf("could not read image %s", slides[idx + 1].text + 1);
|
||||||
|
if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1].img))
|
||||||
|
eprintf("could not read image %s", slides[idx - 1].text + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +480,14 @@ void quit(const Arg *arg)
|
|||||||
running = 0;
|
running = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resize(int width, int height)
|
||||||
|
{
|
||||||
|
xw.w = width;
|
||||||
|
xw.h = height;
|
||||||
|
xw.uw = usablewidth * width;
|
||||||
|
xw.uh = usableheight * height;
|
||||||
|
}
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
XEvent ev;
|
XEvent ev;
|
||||||
@ -274,8 +496,7 @@ void run()
|
|||||||
while (1) {
|
while (1) {
|
||||||
XNextEvent(xw.dpy, &ev);
|
XNextEvent(xw.dpy, &ev);
|
||||||
if (ev.type == ConfigureNotify) {
|
if (ev.type == ConfigureNotify) {
|
||||||
xw.w = ev.xconfigure.width;
|
resize(ev.xconfigure.width, ev.xconfigure.height);
|
||||||
xw.h = ev.xconfigure.height;
|
|
||||||
} else if (ev.type == MapNotify) {
|
} else if (ev.type == MapNotify) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -300,10 +521,19 @@ void xdraw()
|
|||||||
int height;
|
int height;
|
||||||
int width;
|
int width;
|
||||||
struct DC *dc = getfontsize(slides[idx].text, line_len, &width, &height);
|
struct DC *dc = getfontsize(slides[idx].text, line_len, &width, &height);
|
||||||
|
struct image *im = slides[idx].img;
|
||||||
|
|
||||||
XClearWindow(xw.dpy, xw.win);
|
XClearWindow(xw.dpy, xw.win);
|
||||||
|
|
||||||
|
if (!im)
|
||||||
XDrawString(xw.dpy, xw.win, dc->gc, (xw.w - width)/2, (xw.h + height)/2,
|
XDrawString(xw.dpy, xw.win, dc->gc, (xw.w - width)/2, (xw.h + height)/2,
|
||||||
slides[idx].text, line_len);
|
slides[idx].text, line_len);
|
||||||
|
else if (!(im->state & LOADED) && !pngread(im))
|
||||||
|
eprintf("could not read image %s", slides[idx].text + 1);
|
||||||
|
else if (!(im->state & SCALED) && !pngprepare(im))
|
||||||
|
eprintf("could not prepare image %s for drawing", slides[idx].text + 1);
|
||||||
|
else if (!(im->state & DRAWN))
|
||||||
|
pngdraw(im);
|
||||||
}
|
}
|
||||||
|
|
||||||
void xhints()
|
void xhints()
|
||||||
@ -427,10 +657,11 @@ void kpress(XEvent *e)
|
|||||||
shortcuts[i].func(&(shortcuts[i].arg));
|
shortcuts[i].func(&(shortcuts[i].arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize(XEvent *e)
|
void configure(XEvent *e)
|
||||||
{
|
{
|
||||||
xw.w = e->xconfigure.width;
|
resize(e->xconfigure.width, e->xconfigure.height);
|
||||||
xw.h = e->xconfigure.height;
|
if (slides[idx].img)
|
||||||
|
slides[idx].img->state &= ~(DRAWN | SCALED);
|
||||||
xdraw();
|
xdraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user