Support farbfeld as an intermediate format
authorsin <sin@2f30.org>
Wed, 18 Nov 2015 10:41:02 +0000 (10:41 +0000)
committerMarkus Teich <markus.teich@stusta.mhn.de>
Tue, 8 Dec 2015 19:01:36 +0000 (20:01 +0100)
Sent now uses farbfeld[0] as an intermediate format.  A series of
filters is specified in config.h that matches file extensions to
filter programs.  The programs will convert between formats such as
png to farbfeld.  Internally in sent we do not need to worry on how
to parse png or any other format.

This also works with jpg and gif and others.  The 2ff wrapper will
use imagemagick conversion tools.  This is temporary as jpg2ff and
gif2ff will also be implemented.

To make this work, you will have to clone[0] and put png2ff and 2ff
in your PATH.

[0] http://git.2f30.org/farbfeld/

LICENSE
README.md
config.def.h
config.mk
example
sent.c

diff --git a/LICENSE b/LICENSE
index d1da8fc42901ffe0be7222c1a79e1655b5c61a08..8d5665e74f4f431f7bafe19105dc69289c39552b 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -2,8 +2,6 @@ The MIT License (MIT)
 
 Copyright (c) 2014-2015 Markus Teich
 
-png handling stuff adapted from meh by John Hawthorn
-
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
index 007564b650c1ef2b5f67d259121abd52c60f0789..e213efbcbf56a8a93e4279a313cfa95a16d68af1 100644 (file)
--- a/README.md
+++ b/README.md
@@ -33,7 +33,6 @@ presentation file could look like this:
        
        depends on
        - Xlib
-       - libpng
        
        sent FILENAME
        one slide per paragraph
index 2dae620e0aaed922e8cb241ea5e3e50e7c2dfa1e..94ed09edc00b526705922fbe04c50acdedf8d9bd 100644 (file)
@@ -45,3 +45,8 @@ static Shortcut shortcuts[] = {
        { XK_n,           advance,        {.i = +1} },
        { XK_p,           advance,        {.i = -1} },
 };
+
+static Filter filters[] = {
+       { "\\.png$",       "png2ff" },
+       { "\\.(jpg|gif)$", "2ff" },
+};
index ed081991160bd01b8f434dc19bb6855e16ba1c69..52d5fb17d96a515078f99c24826a602266ed0266 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -12,7 +12,7 @@ X11LIB = /usr/X11R6/lib
 
 # includes and libs
 INCS = -I. -I/usr/include -I/usr/include/freetype2 -I${X11INC}
-LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11 -lpng
+LIBS = -L/usr/lib -lc -lm -L${X11LIB} -lXft -lfontconfig -lX11
 
 # flags
 CPPFLAGS = -DVERSION=\"${VERSION}\" -D_XOPEN_SOURCE=600
diff --git a/example b/example
index 39e02065aa8f92fe1b33aac82d09f3c48151b76e..d4b62d247225536bb54c193228e433c89a874a81 100644 (file)
--- a/example
+++ b/example
@@ -20,7 +20,6 @@ easy to use
 
 depends on
 ♽ Xlib
-☢ libpng
 
 ~1000 lines of code
 
diff --git a/sent.c b/sent.c
index e717a69c797f65d620063792cc8a839a0d647f82..eb3aadc2a4b77f161193173043d0719e4b763e8a 100644 (file)
--- a/sent.c
+++ b/sent.c
@@ -1,12 +1,17 @@
 /* See LICENSE for licence details. */
+#include <sys/types.h>
+#include <arpa/inet.h>
+
 #include <errno.h>
+#include <fcntl.h>
 #include <math.h>
-#include <png.h>
+#include <regex.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <X11/keysym.h>
 #include <X11/XKBlib.h>
 #include <X11/Xatom.h>
@@ -36,12 +41,15 @@ typedef struct {
        unsigned int bufwidth, bufheight;
        imgstate state;
        XImage *ximg;
-       FILE *f;
-       png_structp png_ptr;
-       png_infop info_ptr;
+       int fd;
        int numpasses;
 } Image;
 
+typedef struct {
+       char *regex;
+       char *bin;
+} Filter;
+
 typedef struct {
        unsigned int linecount;
        char **lines;
@@ -79,12 +87,12 @@ typedef struct {
        const Arg arg;
 } Shortcut;
 
-static Image *pngopen(char *filename);
-static void pngfree(Image *img);
-static int pngread(Image *img);
-static int pngprepare(Image *img);
-static void pngscale(Image *img);
-static void pngdraw(Image *img);
+static Image *ffopen(char *filename);
+static void fffree(Image *img);
+static int ffread(Image *img);
+static int ffprepare(Image *img);
+static void ffscale(Image *img);
+static void ffdraw(Image *img);
 
 static void getfontsize(Slide *s, unsigned int *width, unsigned int *height);
 static void cleanup();
@@ -128,56 +136,87 @@ static void (*handler[LASTEvent])(XEvent *) = {
        [KeyPress] = kpress,
 };
 
-Image *pngopen(char *filename)
+int
+filter(int fd, const char *cmd)
+{
+       int fds[2];
+
+       if (pipe(fds) < 0)
+               eprintf("pipe:");
+
+       switch (fork()) {
+       case -1:
+               eprintf("fork:");
+       case 0:
+               dup2(fd, 0);
+               dup2(fds[1], 1);
+               close(fds[0]);
+               close(fds[1]);
+               execlp(cmd, cmd, (char *)0);
+               eprintf("execlp %s:", cmd);
+       }
+       close(fds[1]);
+       return fds[0];
+}
+
+Image *ffopen(char *filename)
 {
-       FILE *f;
-       unsigned char buf[8];
+       unsigned char hdr[16];
+       char *bin;
+       regex_t regex;
        Image *img;
+       size_t i;
+       int tmpfd, fd;
+
+       for (bin = NULL, i = 0; i < LEN(filters); i++) {
+               if (regcomp(&regex, filters[i].regex,
+                           REG_NOSUB | REG_EXTENDED | REG_ICASE))
+                       continue;
+               if (!regexec(&regex, filename, 0, NULL, 0)) {
+                       bin = filters[i].bin;
+                       break;
+               }
+       }
 
-       if (!(f = fopen(filename, "rb"))) {
+       if ((fd = open(filename, O_RDONLY)) < 0) {
                eprintf("Unable to open file %s:", filename);
                return NULL;
        }
 
-       if (fread(buf, 1, 8, f) != 8 || png_sig_cmp(buf, 1, 8))
-               return NULL;
+       tmpfd = fd;
+       fd = filter(fd, bin);
+       if (fd < 0)
+               eprintf("could not filter %s:", filename);
+       close(tmpfd);
 
-       img = malloc(sizeof(Image));
-       memset(img, 0, sizeof(Image));
-       if (!(img->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
-                                       NULL, NULL))) {
-               free(img);
+       if (read(fd, hdr, 16) != 16)
                return NULL;
-       }
-       if (!(img->info_ptr = png_create_info_struct(img->png_ptr))
-           || setjmp(png_jmpbuf(img->png_ptr))) {
-               pngfree(img);
+
+       if (memcmp("farbfeld", hdr, 8))
                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);
+       img = calloc(1, sizeof(Image));
+       img->fd = fd;
+       img->bufwidth = ntohl(*(uint32_t *)&hdr[8]);
+       img->bufheight = ntohl(*(uint32_t *)&hdr[12]);
 
        return img;
 }
 
-void pngfree(Image *img)
+void fffree(Image *img)
 {
-       png_destroy_read_struct(&img->png_ptr, img->info_ptr ? &img->info_ptr : NULL, NULL);
        free(img->buf);
        if (img->ximg)
                XDestroyImage(img->ximg);
        free(img);
 }
 
-int pngread(Image *img)
+int ffread(Image *img)
 {
-       unsigned int y;
-       png_bytepp row_pointers;
+       uint32_t y, x;
+       uint16_t *row;
+       size_t rowlen, off, nbytes;
+       ssize_t r;
 
        if (!img)
                return 0;
@@ -187,59 +226,42 @@ int pngread(Image *img)
 
        if (img->buf)
                free(img->buf);
+       /* internally the image is stored in 888 format */
        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);
+       /* scratch buffer to read row by row */
+       rowlen = img->bufwidth * 2 * strlen("RGBA");
+       row = malloc(rowlen);
+       if (!row) {
+               free(img->buf);
+               img->buf = 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);
+       for (off = 0, y = 0; y < img->bufheight; y++) {
+               nbytes = 0;
+               while (nbytes < rowlen) {
+                       r = read(img->fd, (char *)row + nbytes, rowlen - nbytes);
+                       if (r < 0)
+                               eprintf("read:");
+                       nbytes += r;
+               }
+               for (x = 0; x < rowlen / 2; x += 4) {
+                       img->buf[off++] = ntohs(row[x + 0]) / 257;
+                       img->buf[off++] = ntohs(row[x + 1]) / 257;
+                       img->buf[off++] = ntohs(row[x + 2]) / 257;
+               }
        }
 
-       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);
+       free(row);
+       close(img->fd);
        img->state |= LOADED;
 
        return 1;
 }
 
-int pngprepare(Image *img)
+int ffprepare(Image *img)
 {
        int depth = DefaultDepth(xw.dpy, xw.scr);
        int width = xw.uw;
@@ -276,12 +298,12 @@ int pngprepare(Image *img)
                return 0;
        }
 
-       pngscale(img);
+       ffscale(img);
        img->state |= SCALED;
        return 1;
 }
 
-void pngscale(Image *img)
+void ffscale(Image *img)
 {
        unsigned int x, y;
        unsigned int width = img->ximg->width;
@@ -306,7 +328,7 @@ void pngscale(Image *img)
        }
 }
 
-void pngdraw(Image *img)
+void ffdraw(Image *img)
 {
        int xoffset = (xw.w - img->ximg->width) / 2;
        int yoffset = (xw.h - img->ximg->height) / 2;
@@ -364,7 +386,7 @@ void cleanup()
                                free(slides[i].lines[j]);
                        free(slides[i].lines);
                        if (slides[i].img)
-                               pngfree(slides[i].img);
+                               fffree(slides[i].img);
                }
                free(slides);
                slides = NULL;
@@ -443,7 +465,7 @@ void load(FILE *fp)
                        /* only make image slide if first line of a slide starts with @ */
                        if (s->linecount == 0 && s->lines[0][0] == '@') {
                                memmove(s->lines[0], &s->lines[0][1], blen);
-                               s->img = pngopen(s->lines[0]);
+                               s->img = ffopen(s->lines[0]);
                        }
 
                        if (s->lines[s->linecount][0] == '\\')
@@ -465,10 +487,10 @@ void advance(const Arg *arg)
                        slides[idx].img->state &= ~(DRAWN | SCALED);
                idx = new_idx;
                xdraw();
-               if (slidecount > idx + 1 && slides[idx + 1].img && !pngread(slides[idx + 1].img))
-                       die("Unable to read image %s.", slides[idx + 1].lines[0]);
-               if (0 < idx && slides[idx - 1].img && !pngread(slides[idx - 1].img))
-                       die("Unable to read image %s.", slides[idx - 1].lines[0]);
+               if (slidecount > idx + 1 && slides[idx + 1].img && !ffread(slides[idx + 1].img))
+                       die("Unable to read image %s", slides[idx + 1].lines[0]);
+               if (0 < idx && slides[idx - 1].img && !ffread(slides[idx - 1].img))
+                       die("Unable to read image %s", slides[idx - 1].lines[0]);
        }
 }
 
@@ -532,12 +554,12 @@ void xdraw()
                                 slides[idx].lines[i],
                                 0);
                drw_map(d, xw.win, 0, 0, xw.w, xw.h);
-       } else if (!(im->state & LOADED) && !pngread(im)) {
-               eprintf("Unable to read image %s.", slides[idx].lines[0]);
-       } else if (!(im->state & SCALED) && !pngprepare(im)) {
-               eprintf("Unable to prepare image %s for drawing.", slides[idx].lines[0]);
+       } else if (!(im->state & LOADED) && !ffread(im)) {
+               eprintf("Unable to read image %s", slides[idx].lines[0]);
+       } else if (!(im->state & SCALED) && !ffprepare(im)) {
+               eprintf("Unable to prepare image %s for drawing", slides[idx].lines[0]);
        } else if (!(im->state & DRAWN)) {
-               pngdraw(im);
+               ffdraw(im);
        }
 }