summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x2ff51
-rw-r--r--2ff.118
-rw-r--r--LICENSE2
-rw-r--r--Makefile68
-rw-r--r--README51
-rw-r--r--TODO3
-rw-r--r--config.mk31
-rw-r--r--farbfeld.5130
-rw-r--r--ff2jpg.134
-rw-r--r--ff2jpg.c139
-rw-r--r--ff2pam.17
-rw-r--r--ff2pam.c66
-rw-r--r--ff2png.18
-rw-r--r--ff2png.c102
-rw-r--r--ff2ppm.128
-rw-r--r--ff2ppm.c88
-rw-r--r--jpg2ff.15
-rw-r--r--jpg2ff.c109
-rw-r--r--png2ff.15
-rw-r--r--png2ff.c111
-rw-r--r--util.c238
-rw-r--r--util.h22
22 files changed, 756 insertions, 560 deletions
diff --git a/2ff b/2ff
index f958692..10e4320 100755
--- a/2ff
+++ b/2ff
@@ -1,41 +1,44 @@
#!/bin/sh
+
+# arguments
if [ "$#" -ne 0 ]; then
echo "usage: $0" >&2
exit 1
fi
+# write input into temporary file
TMP=$(mktemp)
trap 'rm "$TMP"' EXIT
-
cat > "$TMP"
-if [ "$(dd if="$TMP" bs=1 count=8 2>/dev/null)" = "farbfeld" ]; then
+# determine the mime-type
+if [ "$(dd if="$TMP" bs=1 count=8 2>/dev/null | tr -d '\0')" = "farbfeld" ]; then
cat "$TMP"
- exit 0
-fi
+else
+ MIME=$(file -ib "$TMP" | cut -d ";" -f 1)
-FORMAT=$(file -ib "$TMP" | cut -d ";" -f 1)
+ case "$MIME" in
+ image/png)
+ png2ff < "$TMP"
+ ;;
+ image/jpeg)
+ jpg2ff < "$TMP"
+ ;;
+ *)
+ if [ ! -x /usr/bin/convert ] ; then
+ printf "%s: cant convert from %s -- missing \`convert' program" "$0" "$FORMAT" >&2
+ printf "Run \`apt-get install imagemagick' to install it" >&2
+ exit 1
+ fi
-case "$FORMAT" in
-image/png)
- png2ff < "$TMP"
- ;;
-image/jpeg)
- jpg2ff < "$TMP"
- ;;
-*)
- if [ ! -x /usr/bin/convert ] ; then
- printf "%s: cant convert from %s -- missing \`convert' program" "$0" "$FORMAT" >&2
- printf "Run \`apt-get install imagemagick' to install it" >&2
- exit 1
- fi
- convert "$TMP" png:- 2>/dev/null | png2ff 2>/dev/null
- ;;
-esac
+ convert "$TMP" png:- | png2ff
+ ;;
+ esac
+fi
+# errors
if [ $? -ne 0 ]; then
- printf "%s: failed to convert from %s\n" "$0" "$FORMAT" >&2
exit 1
+else
+ exit 0
fi
-
-exit 0
diff --git a/2ff.1 b/2ff.1
index 44eed4e..3592071 100644
--- a/2ff.1
+++ b/2ff.1
@@ -1,4 +1,4 @@
-.Dd 2016-01-05
+.Dd 2017-04-14
.Dt 2FF 1
.Os suckless.org
.Sh NAME
@@ -13,9 +13,9 @@ reads an image from stdin, converts it to
and writes the result to stdout.
.Pp
.Nm
-is a wrapper script around the *2ff-tools
-with a soft fallback to obtaining a PNG from imagemagick's
-.Xr convert 1
+is a wrapper script around the farbfeld conversion tools
+with a fallback to obtaining a PNG using
+.Xr ImageMagick 1
and passing it through
.Xr png2ff 1 .
.Pp
@@ -27,9 +27,7 @@ writes a diagnostic message to stderr.
.It 0
Image processed successfully.
.It 1
-An error occurred or
-.Xr convert 1
-was not found in the fallback.
+An error occurred.
.El
.Sh EXAMPLES
$
@@ -40,12 +38,8 @@ $
.Nm
< image.* | bzip2 > image.ff.bz2
.Sh SEE ALSO
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr convert 1 ,
-.Xr ff2png 1 ,
-.Xr jpg2ff 1 ,
-.Xr png2ff 1 ,
+.Xr ImageMagick 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Laslo Hunhold Aq Mt dev@frign.de
diff --git a/LICENSE b/LICENSE
index fdc6d84..79a2d84 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
ISC-License
-(c) 2014-2016 Laslo Hunhold <dev@frign.de>
+(c) 2014-2017 Laslo Hunhold <dev@frign.de>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
index 72a5e3c..ca1949b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,52 +1,54 @@
-# farbfeld - suckless image format with conversion tools
# See LICENSE file for copyright and license details
+# farbfeld - suckless image format with conversion tools
+.POSIX:
+
include config.mk
-BIN = png2ff ff2png jpg2ff ff2jpg ff2pam ff2ppm
-SCRIPTS = 2ff
-SRC = ${BIN:=.c}
+REQ = util
HDR = arg.h
-MAN1 = 2ff.1 ${BIN:=.1}
+BIN = png2ff ff2png jpg2ff ff2jpg ff2pam ff2ppm
+SCR = 2ff
+MAN1 = 2ff.1 $(BIN:=.1)
MAN5 = farbfeld.5
-all: ${BIN}
+png2ff-LDFLAGS = $(PNG-LDFLAGS)
+ff2png-LDFLAGS = $(PNG-LDFLAGS)
+jpg2ff-LDFLAGS = $(JPG-LDFLAGS)
+ff2jpg-LDFLAGS = $(JPG-LDFLAGS)
-${BIN}: ${@:=.o}
+all: $(BIN)
-OBJ = ${SRC:.c=.o}
+$(BIN): $(REQ:=.o)
-${OBJ}: config.mk ${HDR}
+$(BIN:=.o): config.mk $(HDR) $(REQ:=.h)
.o:
- ${CC} ${CFLAGS} ${$*-LDFLAGS} -o $@ $<
-
-.c.o:
- ${CC} ${CFLAGS} ${$*-CFLAGS} ${CPPFLAGS} -c $<
+ $(CC) $(LDFLAGS) $($*-LDFLAGS) -o $@ $< $(REQ:=.o)
clean:
- rm -f ${BIN} ${OBJ}
+ rm -f $(BIN) $(BIN:=.o) $(REQ:=.o)
dist:
- rm -rf "farbfeld-${VERSION}"
- mkdir -p "farbfeld-${VERSION}"
- cp -R FORMAT LICENSE Makefile README TODO config.mk ${SCRIPTS} ${HDR} ${SRC} ${MAN1} ${MAN5} "farbfeld-${VERSION}"
- tar -cf - "farbfeld-${VERSION}" | gzip -c > "farbfeld-${VERSION}.tar.gz"
- rm -rf "farbfeld-${VERSION}"
+ rm -rf "farbfeld-$(VERSION)"
+ mkdir -p "farbfeld-$(VERSION)"
+ cp -R FORMAT LICENSE Makefile README config.mk $(SCR) \
+ $(HDR) $(BIN:=.c) $(REQ:=.c) $(REQ:=.h) \
+ $(MAN1) $(MAN5) "farbfeld-$(VERSION)"
+ tar -cf - "farbfeld-$(VERSION)" | gzip -c > "farbfeld-$(VERSION).tar.gz"
+ rm -rf "farbfeld-$(VERSION)"
install: all
- mkdir -p "${DESTDIR}${PREFIX}/bin"
- cp -f ${SCRIPTS} ${BIN} "${DESTDIR}${PREFIX}/bin"
- for f in $(BIN) $(SCRIPTS); do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done
- mkdir -p "${DESTDIR}${MANPREFIX}/man1"
- cp -f ${MAN1} "${DESTDIR}${MANPREFIX}/man1"
- for m in $(MAN1); do chmod 644 "${DESTDIR}${MANPREFIX}/man1/$$m"; done
- mkdir -p "${DESTDIR}${MANPREFIX}/man5"
- cp -f ${MAN5} "${DESTDIR}${MANPREFIX}/man5"
- for m in $(MAN5); do chmod 644 "${DESTDIR}${MANPREFIX}/man5/$$m"; done
+ mkdir -p "$(DESTDIR)$(PREFIX)/bin"
+ cp -f $(SCR) $(BIN) "$(DESTDIR)$(PREFIX)/bin"
+ for f in $(BIN) $(SCR); do chmod 755 "$(DESTDIR)$(PREFIX)/bin/$$f"; done
+ mkdir -p "$(DESTDIR)$(MANPREFIX)/man1"
+ cp -f $(MAN1) "$(DESTDIR)$(MANPREFIX)/man1"
+ for m in $(MAN1); do chmod 644 "$(DESTDIR)$(MANPREFIX)/man1/$$m"; done
+ mkdir -p "$(DESTDIR)$(MANPREFIX)/man5"
+ cp -f $(MAN5) "$(DESTDIR)$(MANPREFIX)/man5"
+ for m in $(MAN5); do chmod 644 "$(DESTDIR)$(MANPREFIX)/man5/$$m"; done
uninstall:
- for f in $(BIN) $(SCRIPTS); do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done
- for m in $(MAN1); do rm -f "${DESTDIR}${MANPREFIX}/man1/$$m"; done
- for m in $(MAN5); do rm -f "${DESTDIR}${MANPREFIX}/man5/$$m"; done
-
-.PHONY: all clean dist install uninstall
+ for f in $(BIN) $(SCR); do rm -f "$(DESTDIR)$(PREFIX)/bin/$$f"; done
+ for m in $(MAN1); do rm -f "$(DESTDIR)$(MANPREFIX)/man1/$$m"; done
+ for m in $(MAN5); do rm -f "$(DESTDIR)$(MANPREFIX)/man5/$$m"; done
diff --git a/README b/README
index 89123a5..1fe1125 100644
--- a/README
+++ b/README
@@ -1,16 +1,35 @@
- ███ ███ ██ ██ ███ ███ █ ██
- █ █ █ █ █ █ █ █ █ █ █ █
- ██ ███ ███ ███ ██ ██ █ █ █
- █ █ █ █ █ █ █ █ █ █ █ █
- █ █ █ █ █ ██ █ ███ ███ ██
+ ███ ███ ██ ██ ███ ███ █ ██
+ █ █ █ █ █ █ █ █ █ █ █ █
+ ██ ███ ██ ███ ██ ██ █ █ █
+ █ █ █ █ █ █ █ █ █ █ █ █
+ █ █ █ █ █ ██ █ ███ ███ ██
WHAT IS FARBFELD?
Farbfeld is a lossless image-format designed to be
- parsed and piped easily.
- It can be compressed easily and beats PNG's filesize
+ parsed and piped easily. It is probably the simplest
+ image-format you can find (see FORMAT).
+ It does not have integrated compression, but allows
+ compression algorithms to work with it easily by adding
+ little entropy to the image data itself. This beats PNG
in many cases.
+ Given the free choice of compression algorithms, it
+ is trivial to switch to better and faster ones as they
+ show up in the future.
+
+HOW DO I USE THE TOOLS?
+ encoding:
+ png2ff < example.png > example.ff
+ png2ff < example.png | bzip2 > example.ff.bz2
+
+ decoding:
+ ff2png < example.ff > example.png
+ bzcat example.ff.bz2 | ff2png > example.png
+
+ bzip2 is used in this example and a recommended
+ compression algorithm. Of course you are free
+ to use something else.
WHY FARBFELD?
Current image-formats have integrated compression,
@@ -32,13 +51,13 @@ WHY FARBFELD?
and farbfeld.
HOW DOES IT WORK?
- In Farbfeld, pattern resolution is not done while
+ In farbfeld, pattern resolution is not done while
converting, but while compressing the image.
For example, farbfeld always stores the alpha-channel,
even if the image doesn't have alpha-variation.
This may sound like a big waste at first, but as
soon as you compress an image of this kind, the
- compression-algorithm (e.g. bz2) recognizes the
+ compression-algorithm (e.g. bzip2) recognizes the
pattern that every 48 bits the 16 bits store the
same information.
And the compression-algorithms get better and better
@@ -49,19 +68,7 @@ HOW DOES IT WORK?
the same value, which is recognized by the compression
algorithm easily.
This effectively leads to filesizes you'd normally only
- reach with paletted images, and in some cases bz2 even
+ reach with paletted images, and in some cases bzip2 even
beats png's compression, for instance when you're dealing
with grayscale data, line drawings, decals and even
photographs.
-
-HOW DO I USE THE TOOLS?
- encoding:
- png2ff < example.png > example.ff
- png2ff < example.png | bzip2 > example.ff.bz2
-
- decoding:
- ff2png < example.ff > example.png
- bzcat example.ff.bz2 | ff2png > example.png
-
- bz2 is recommended for compression, but you can use
- any algorithm you want.
diff --git a/TODO b/TODO
deleted file mode 100644
index f3eea7b..0000000
--- a/TODO
+++ /dev/null
@@ -1,3 +0,0 @@
- o re-add the old imagefile-tools for gif, ...
- o flush fp's (fflush()) so we correctly report on truncation
- o write utility functions to solve duplicate tasks
diff --git a/config.mk b/config.mk
index 80228d1..b69a346 100644
--- a/config.mk
+++ b/config.mk
@@ -1,5 +1,5 @@
# farbfeld version
-VERSION = 2
+VERSION = 3
# Customize below to fit your system
@@ -7,33 +7,12 @@ VERSION = 2
PREFIX = /usr
MANPREFIX = ${PREFIX}/share/man
-PNGLIB = /usr/local/lib
-PNGINC = /usr/local/include
-
-JPGLIB = /usr/local/lib
-JPGINC = /usr/local/include
-
-INCS =
-LIBS =
-
# flags
CPPFLAGS += -D_DEFAULT_SOURCE
-CFLAGS += -std=c99 -pedantic -Wall -Os ${INCS}
-LDFLAGS += -s ${LIBS}
+CFLAGS += -std=c99 -pedantic -Wall -Wextra -Os
+LDFLAGS += -s
+PNG-LDFLAGS = -lpng
+JPG-LDFLAGS = -ljpeg
# compiler and linker
CC = cc
-
-# flags per tool.
-
-png2ff-CFLAGS := -I${PNGINC}
-png2ff-LDFLAGS := -L${PNGLIB} -lpng
-
-ff2png-CFLAGS := -I${PNGINC}
-ff2png-LDFLAGS := -L${PNGLIB} -lpng
-
-jpg2ff-CFLAGS := -I${JPGINC}
-jpg2ff-LDFLAGS := -L${JPGLIB} -ljpeg
-
-ff2jpg-CFLAGS := -I${JPGINC}
-ff2jpg-LDFLAGS := -L${JPGLIB} -ljpeg
diff --git a/farbfeld.5 b/farbfeld.5
index b9308b1..664075d 100644
--- a/farbfeld.5
+++ b/farbfeld.5
@@ -1,4 +1,4 @@
-.Dd 2016-01-04
+.Dd 2017-04-14
.Dt FARBFELD 5
.Os suckless.org
.Sh NAME
@@ -17,17 +17,24 @@ BYTES DESCRIPTION
4 32-Bit BE unsigned integer (height)
[2222] 4*16-Bit BE unsigned integers [RGBA] / pixel, row-major
.Ed
+.Pp
The RGB-data should be sRGB for best interoperability and not
alpha-premultiplied.
.Sh USAGE
.Nm
-provides
-.Xr png2ff 1 ,
-.Xr jpg2ff 1 ,
+provides the tools
.Xr 2ff 1 ,
-.Xr ff2png 1
-for
-.Em conversions ;
+.Xr jpg2ff 1 ,
+.Xr png2ff 1
+and
+.Xr ff2jpg 1 ,
+.Xr ff2pam 1 ,
+.Xr ff2png 1 ,
+.Xr ff2ppm 1
+to
+.Em convert
+to and from farbfeld images respectively.
+.Pp
.Xr bzip2 1
is recommended for
.Em compression ,
@@ -42,81 +49,128 @@ appended (e.g. ".ff.bz2").
.Nm
was created because the author was not satisfied with the boilerplate
and inherent complexity involved in handling common image formats
-(PNG, JPEG, GIF,...), having to rely on bloated libraries not being able
-to focus on the task at hand for a given problem.
+(PNG, JPEG, GIF,...), having to rely on bloated libraries while not being
+able to focus on the task at hand for a given image processing problem.
.Sh EXAMPLES
-Below is an example for a color inverter usable in a pipeline. No external
-libraries other than libc are needed to handle the image data:
+The following code listing
+.Em invert.c
+is a ready-to-use color inverter with all necessary error handling and
+reporting. This program can be integrated into a farbfeld pipeline as
+follows:
+.Pp
+$ png2ff < image.png | invert | ff2png > image-inverted.png
+.Pp
+It shall be noted here that due to the simplicity of the format no
+external libraries are needed to handle the farbfeld image data. The
+0BSD-License gives you the freedom to throw away the license block and
+just use the code as you wish. Happy hacking!
.Bd -literal -offset left
+/*
+ * 0BSD-License
+ *
+ * (c) 2017 Laslo Hunhold <dev@frign.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
#include <arpa/inet.h>
+#include <errno.h>
#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+#define LEN(x) (sizeof (x) / sizeof *(x))
+
+static void
+invert(uint16_t rgba[4])
+{
+ rgba[0] = UINT16_MAX - rgba[0];
+ rgba[1] = UINT16_MAX - rgba[1];
+ rgba[2] = UINT16_MAX - rgba[2];
+}
+
int
main(int argc, char *argv[])
{
- uint32_t width, height, i, j, k;
+ uint32_t hdr[4], width, height, i, j, k;
uint16_t rgba[4];
- uint8_t hdr[strlen("farbfeld") + 2 * sizeof(uint32_t)];
- if (argc > 1) {
+ /* arguments */
+ if (argc != 1) {
fprintf(stderr, "usage: %s\\n", argv[0]);
return 1;
}
- if (fread(hdr, 1, sizeof(hdr), stdin) != sizeof(hdr)) {
- fprintf(stderr, "incomplete header\\n");
- return 1;
+ /* read header */
+ if (fread(hdr, sizeof(*hdr), LEN(hdr), stdin) != LEN(hdr)) {
+ goto readerr;
}
- if (memcmp("farbfeld", hdr, strlen("farbfeld"))) {
- fprintf(stderr, "invalid magic\\n");
+ if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
+ fprintf(stderr, "%s: invalid magic value\\n", argv[0]);
return 1;
}
- width = ntohl(*((uint32_t *)(hdr + 8)));
- height = ntohl(*((uint32_t *)(hdr + 12)));
+ width = ntohl(hdr[2]);
+ height = ntohl(hdr[3]);
- if (fwrite(hdr, 1, sizeof(hdr), stdout) != sizeof(hdr)) {
- fprintf(stderr, "write error\\n");
- return 1;
+ /* write data */
+ if (fwrite(hdr, sizeof(*hdr), LEN(hdr), stdout) != 4) {
+ goto writerr;
}
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
- if (fread(rgba, sizeof(uint16_t), 4,
- stdin) != 4) {
- fprintf(stderr, "unexpected EOF\\n");
- return 1;
+ if (fread(rgba, sizeof(*rgba), LEN(rgba),
+ stdin) != LEN(rgba)) {
+ goto readerr;
}
for (k = 0; k < 4; k++) {
rgba[k] = ntohs(rgba[k]);
}
- /* invert colors */
- rgba[0] = 65535 - rgba[0];
- rgba[1] = 65535 - rgba[1];
- rgba[2] = 65535 - rgba[2];
+ invert(rgba);
for (k = 0; k < 4; k++) {
rgba[k] = htons(rgba[k]);
}
- if (fwrite(rgba, sizeof(uint16_t), 4,
- stdout) != 4) {
- fprintf(stderr, "write error\\n");
- return 1;
+ if (fwrite(rgba, sizeof(*rgba), LEN(rgba),
+ stdout) != LEN(rgba)) {
+ goto writerr;
}
}
}
+ /* clean up */
+ if (fclose(stdout)) {
+ fprintf(stderr, "%s: fclose: %s\\n", argv[0],
+ strerror(errno));
+ return 1;
+ }
+
return 0;
+readerr:
+ fprintf(stderr, "%s: fread: Unexpected EOF\\n", argv[0]);
+ return 1;
+writerr:
+ fprintf(stderr, "%s: fwrite: %s\\n", argv[0], strerror(errno));
+ return 1;
}
.Ed
.Sh SEE ALSO
.Xr 2ff 1 ,
-.Xr bzip2 1 ,
+.Xr ff2jpg 1 ,
+.Xr ff2pam 1 ,
.Xr ff2png 1 ,
+.Xr ff2ppm 1 ,
.Xr jpg2ff 1 ,
.Xr png2ff 1
.Sh AUTHORS
diff --git a/ff2jpg.1 b/ff2jpg.1
index d202988..bd9dd04 100644
--- a/ff2jpg.1
+++ b/ff2jpg.1
@@ -1,19 +1,19 @@
-.Dd 2016-03-23
+.Dd 2017-04-14
.Dt FF2JPG 1
.Os suckless.org
.Sh NAME
.Nm ff2jpg
-.Nd convert farbfeld to JPEG
+.Nd convert farbfeld to JPG
.Sh SYNOPSIS
.Nm
-.Op Fl b Ar color
+.Op Fl b Ar colour
.Op Fl o
.Op Fl q Ar quality
.Sh DESCRIPTION
.Nm
reads a
.Xr farbfeld 5
-image from stdin, converts it to a JPEG image (RGB) and writes the result to
+image from stdin, converts it to JPG (8-bit RGB) and writes the result to
stdout.
.Pp
In case of an error
@@ -21,18 +21,16 @@ In case of an error
writes a diagnostic message to stderr.
.Sh OPTIONS
.Bl -tag -width Ds
-.It Fl b Ar color
-.Ar color
-to mix with the background alpha channel, the default is white.
-.Pp
-The following formats are supported:
-"#rrggbb", "#rrrrggggbbbb" and the short-form "#rgb" which expands to "#rrggbb".
+.It Fl b Ar colour
+Blend the transparent colours with
+.Ar colour
+specified as rgb, rrggbb or rrrrggggbbbb. The default is fff.
.It Fl o
-Optimize Huffman table (smaller file, but slow compression).
+Optimize the Huffman table, which reduces the file size but takes longer.
.It Fl q Ar quality
-set JPEG output
+Set the output
.Ar quality
-(range 0-100), the default is 85.
+ranging from 0 to 100. The default is 85.
.El
.Sh EXIT STATUS
.Bl -tag -width Ds
@@ -43,14 +41,14 @@ An error occurred.
.El
.Sh EXAMPLES
$
-png2ff < test.png |
.Nm
--b '#00ff00' -q 85 > test.jpg
+< image.ff > image.jpg
+.Pp
+$ bunzip2 < image.ff.bz2 |
+.Nm
+-b 0f0 -q 90 > image.jpg
.Sh SEE ALSO
-.Xr 2ff 1 ,
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr png2ff 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org
diff --git a/ff2jpg.c b/ff2jpg.c
index 6f88a23..23a506b 100644
--- a/ff2jpg.c
+++ b/ff2jpg.c
@@ -11,10 +11,9 @@
#include <jpeglib.h>
#include "arg.h"
+#include "util.h"
-char *argv0;
-
-METHODDEF(void)
+static void
jpeg_error(j_common_ptr js)
{
fprintf(stderr, "%s: libjpeg: ", argv0);
@@ -23,129 +22,103 @@ jpeg_error(j_common_ptr js)
}
static void
+jpeg_setup_writer(struct jpeg_compress_struct *s, struct jpeg_error_mgr *e,
+ uint32_t w, uint32_t h, int quality, int opt)
+{
+ jpeg_create_compress(s);
+ e->error_exit = jpeg_error;
+ s->err = jpeg_std_error(e);
+
+ jpeg_stdio_dest(s, stdout);
+ s->image_width = w;
+ s->image_height = h;
+ s->input_components = 3; /* color components per pixel */
+ s->in_color_space = JCS_RGB; /* output color space */
+ jpeg_set_defaults(s);
+
+ if (opt) {
+ s->optimize_coding = 1;
+ }
+ jpeg_set_quality(s, quality, 1);
+
+ jpeg_start_compress(s, 1);
+}
+
+static void
usage(void)
{
- fprintf(stderr, "usage: %s [-b #rrggbb] [-o] [-q quality]\n", argv0);
+ fprintf(stderr, "usage: %s [-b colour] [-o] [-q quality]\n", argv0);
exit(1);
}
int
main(int argc, char *argv[])
{
- JSAMPROW row_pointer[1]; /* pointer to a single row */
- struct jpeg_compress_struct cinfo;
+ struct jpeg_compress_struct jcomp;
struct jpeg_error_mgr jerr;
size_t rowlen;
uint64_t a;
- uint32_t hdr[4], width, height, i, j, k, l;
+ uint32_t width, height, i, j, k, l;
uint16_t *row, mask[3] = { 0xffff, 0xffff, 0xffff };
uint8_t *rowout;
- char *color, colfmt[] = "%#x%#x%#x";
- unsigned int collen, col[3], colfac, quality = 85, optimize = 0;
+ int optimize = 0, quality = 85;
- argv0 = argv[0];
+ /* arguments */
ARGBEGIN {
case 'b':
- color = EARGF(usage());
- if (color[0] == '#') {
- color++;
- }
- collen = strlen(color);
- if (collen != 3 && collen != 6 && collen != 12) {
+ if (parse_mask(EARGF(usage()), mask)) {
usage();
}
- colfmt[1] = colfmt[4] = colfmt[7] = ((collen / 3) + '0');
- if (sscanf(color, colfmt, col, col + 1, col + 2) != 3) {
- usage();
- }
- /* UINT16_MAX / 255 = 257; UINT16_MAX / 15 = 4369 */
- colfac = (collen == 3) ? 4369 : (collen == 6) ? 257 : 1;
- for (i = 0; i < 3; i++) {
- mask[i] = col[i] * colfac;
- }
break;
case 'o':
optimize = 1;
break;
case 'q':
- if ((quality = atoi(EARGF(usage()))) > 100)
- usage();
+ quality = estrtonum(EARGF(usage()), 0, 100);
break;
default:
usage();
} ARGEND
- if (argc)
+ if (argc) {
usage();
-
- /* header */
- if (fread(hdr, sizeof(*hdr), 4, stdin) != 4) {
- goto readerr;
- }
- if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
- fprintf(stderr, "%s: invalid magic value\n", argv0);
- return 1;
}
- width = ntohl(hdr[2]);
- height = ntohl(hdr[3]);
- if (width > SIZE_MAX / ((sizeof("RGBA") - 1) * sizeof(uint16_t))) {
- fprintf(stderr, "%s: row length integer overflow\n", argv0);
- return 1;
- }
+ /* prepare */
+ ff_read_header(&width, &height);
+ jpeg_setup_writer(&jcomp, &jerr, width, height, quality, optimize);
+ row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t));
rowlen = width * (sizeof("RGBA") - 1);
- if (!(row = malloc(rowlen * sizeof(uint16_t)))) {
- fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno));
- return 1;
- }
- if (!(rowout = malloc(width * (sizeof("RGB") - 1) * sizeof(uint8_t)))) {
- fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno));
- return 1;
- }
- row_pointer[0] = rowout;
+ rowout = ereallocarray(NULL, width, (sizeof("RGB") - 1) * sizeof(uint8_t));
- jerr.error_exit = jpeg_error;
-
- jpeg_create_compress(&cinfo);
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_stdio_dest(&cinfo, stdout);
- cinfo.image_width = width;
- cinfo.image_height = height;
- cinfo.input_components = 3; /* color components per pixel */
- cinfo.in_color_space = JCS_RGB; /* output color space */
- jpeg_set_defaults(&cinfo);
- if (optimize)
- cinfo.optimize_coding = TRUE;
- jpeg_set_quality(&cinfo, quality, TRUE);
-
- jpeg_start_compress(&cinfo, TRUE);
-
- /* write rows */
+ /* write data */
for (i = 0; i < height; ++i) {
if (fread(row, sizeof(uint16_t), rowlen, stdin) != rowlen) {
- goto readerr;
+ if (ferror(stdin)) {
+ fprintf(stderr, "%s: fread: %s\n", argv0,
+ strerror(errno));
+ } else {
+ fprintf(stderr, "%s: unexpected end of file\n",
+ argv0);
+ }
+ return 1;
}
for (j = 0, k = 0; j < rowlen; j += 4, k += 3) {
a = ntohs(row[j + 3]);
for (l = 0; l < 3; l++) {
+ /* alpha blending and 8-bit-reduction */
rowout[k + l] = (a * ntohs(row[j + l]) +
- (65535 - a) * mask[l]) /
- (257 * 65535);
+ (UINT16_MAX - a) * mask[l]) /
+ (UINT16_MAX *
+ (UINT16_MAX / UINT8_MAX));
}
}
- jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ jpeg_write_scanlines(&jcomp, &rowout, 1);
}
- jpeg_finish_compress(&cinfo);
- jpeg_destroy_compress(&cinfo);
-
- return 0;
-readerr:
- if (ferror(stdin)) {
- fprintf(stderr, "%s: fread: %s\n", argv0, strerror(errno));
- } else {
- fprintf(stderr, "%s: unexpected end of file\n", argv0);
- }
+ /* clean up */
+ jpeg_finish_compress(&jcomp);
+ jpeg_destroy_compress(&jcomp);
- return 1;
+ return fshut(stdout, "<stdout>");
}
diff --git a/ff2pam.1 b/ff2pam.1
index 1153c5a..250372e 100644
--- a/ff2pam.1
+++ b/ff2pam.1
@@ -1,4 +1,4 @@
-.Dd 2017-01-09
+.Dd 2017-04-14
.Dt FF2PAM 1
.Os suckless.org
.Sh NAME
@@ -10,7 +10,7 @@
.Nm
reads a
.Xr farbfeld 5
-image from stdin, converts it to a 16-bit RGBA PAM and writes the result
+image from stdin, converts it to PAM (16-bit RGBA) and writes the result
to stdout.
.Pp
In case of an error
@@ -32,10 +32,7 @@ $ bunzip2 < image.ff.bz2 |
.Nm
> image.pam
.Sh SEE ALSO
-.Xr 2ff 1 ,
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr png2ff 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Mattias Andrée Aq Mt maandree@kth.se
diff --git a/ff2pam.c b/ff2pam.c
index 93bd26c..9a18e6b 100644
--- a/ff2pam.c
+++ b/ff2pam.c
@@ -5,38 +5,39 @@
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-static char *argv0;
+#include "util.h"
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s\n", argv0);
+ exit(1);
+}
int
main(int argc, char *argv[])
{
- uint32_t hdr[4], width, height;
- char buf[BUFSIZ];
- size_t n, t;
+ size_t rowlen;
+ uint32_t width, height, i;
+ uint16_t *row;
+ /* arguments */
argv0 = argv[0], argc--, argv++;
if (argc) {
- fprintf(stderr, "usage: %s\n", argv0);
- return 1;
+ usage();
}
- /* header */
- if (fread(hdr, sizeof(*hdr), 4, stdin) != 4) {
- fprintf(stderr, "%s: file too short\n", argv0);
- return 1;
- }
- if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
- fprintf(stderr, "%s: invalid magic value\n", argv0);
- return 1;
- }
- width = ntohl(hdr[2]);
- height = ntohl(hdr[3]);
+ /* prepare */
+ ff_read_header(&width, &height);
+ row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t));
+ rowlen = width * (sizeof("RGBA") - 1);
- /* write header */
+ /* write data */
printf("P7\n"
"WIDTH %" PRIu32 "\n"
"HEIGHT %" PRIu32 "\n"
@@ -46,29 +47,20 @@ main(int argc, char *argv[])
"ENDHDR\n",
width, height);
- /* write image */
- t = (size_t)width * (size_t)height * sizeof(uint16_t) * (sizeof("RGBA") - 1);
- for (; (n = fread(buf, 1, sizeof(buf) <= t ? sizeof(buf) : t, stdin)); ) {
- t -= n;
- fwrite(buf, 1, n, stdout);
-
- if (feof(stdin)) {
- break;
- }
- if (ferror(stdin)) {
- fprintf(stderr, "%s: read: %s\n", argv0, strerror(errno));
+ for (i = 0; i < height; i++) {
+ if (fread(row, sizeof(uint16_t), rowlen, stdin) != rowlen) {
+ if (ferror(stdin)) {
+ fprintf(stderr, "%s: fread: %s\n", argv0, strerror(errno));
+ } else {
+ fprintf(stderr, "%s: unexpected end of file\n", argv0);
+ }
return 1;
}
- if (ferror(stdout)) {
- fprintf(stderr, "%s: write: %s\n", argv0, strerror(errno));
+ if (fwrite(row, sizeof(uint16_t), rowlen, stdout) != rowlen) {
+ fprintf(stderr, "%s: fwrite: %s\n", argv0, strerror(errno));
return 1;
}
}
- if (t > 0) {
- fprintf(stderr, "%s: file too short\n", argv0);
- return 1;
- }
-
- return 0;
+ return fshut(stdout, "<stdout>");
}
diff --git a/ff2png.1 b/ff2png.1
index 9812d5b..6320d2f 100644
--- a/ff2png.1
+++ b/ff2png.1
@@ -1,4 +1,4 @@
-.Dd 2016-01-17
+.Dd 2017-04-14
.Dt FF2PNG 1
.Os suckless.org
.Sh NAME
@@ -10,7 +10,7 @@
.Nm
reads a
.Xr farbfeld 5
-image from stdin, converts it to a 16-Bit RGBA PNG and writes the result
+image from stdin, converts it to PNG (16-bit RGBA) and writes the result
to stdout.
.Pp
In case of an error
@@ -32,11 +32,7 @@ $ bunzip2 < image.ff.bz2 |
.Nm
> image.png
.Sh SEE ALSO
-.Xr 2ff 1 ,
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr jpg2ff 1 ,
-.Xr png2ff 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Laslo Hunhold Aq Mt dev@frign.de
diff --git a/ff2png.c b/ff2png.c
index 5731082..58b3d37 100644
--- a/ff2png.c
+++ b/ff2png.c
@@ -9,83 +9,79 @@
#include <png.h>
-static char *argv0;
+#include "util.h"
-void
-pngerr(png_structp pngs, const char *msg)
+static void
+png_err(png_struct *pngs, const char *msg)
{
+ (void)pngs;
fprintf(stderr, "%s: libpng: %s\n", argv0, msg);
exit(1);
}
+static void
+png_setup_writer(png_struct **s, png_info **i, uint32_t w, uint32_t h)
+{
+ *s = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_err, NULL);
+ *i = png_create_info_struct(*s);
+
+ if (!*s || !*i) {
+ fprintf(stderr, "%s: failed to initialize libpng\n", argv0);
+ exit(1);
+ }
+
+ png_init_io(*s, stdout);
+ png_set_IHDR(*s, *i, w, h, 16, PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+ png_write_info(*s, *i);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s\n", argv0);
+ exit(1);
+}
+
int
main(int argc, char *argv[])
{
- png_structp pngs;
- png_infop pngi;
+ png_struct *pngs;
+ png_info *pngi;
size_t rowlen;
- uint32_t hdr[4], width, height, i;
+ uint32_t width, height, i;
uint16_t *row;
+ /* arguments */
argv0 = argv[0], argc--, argv++;
if (argc) {
- fprintf(stderr, "usage: %s\n", argv0);
- return 1;
- }
-
- /* header */
- if (fread(hdr, sizeof(*hdr), 4, stdin) != 4) {
- goto readerr;
- }
- if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
- fprintf(stderr, "%s: invalid magic value\n", argv0);
- return 1;
+ usage();
}
- width = ntohl(hdr[2]);
- height = ntohl(hdr[3]);
-
- /* load png */
- pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, pngerr,
- NULL);
- pngi = png_create_info_struct(pngs);
-
- if (!pngs || !pngi) {
- fprintf(stderr, "%s: failed to initialize libpng\n", argv0);
- return 1;
- }
- png_init_io(pngs, stdout);
- png_set_IHDR(pngs, pngi, width, height, 16, PNG_COLOR_TYPE_RGB_ALPHA,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
- PNG_FILTER_TYPE_BASE);
- png_write_info(pngs, pngi);
- /* write rows */
- if (width > SIZE_MAX / ((sizeof("RGBA") - 1) * sizeof(uint16_t))) {
- fprintf(stderr, "%s: row length integer overflow\n", argv0);
- return 1;
- }
+ /* prepare */
+ ff_read_header(&width, &height);
+ png_setup_writer(&pngs, &pngi, width, height);
+ row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t));
rowlen = width * (sizeof("RGBA") - 1);
- if (!(row = malloc(rowlen * sizeof(uint16_t)))) {
- fprintf(stderr, "%s: malloc: out of memory\n", argv0);
- return 1;
- }
+
+ /* write data */
for (i = 0; i < height; ++i) {
if (fread(row, sizeof(uint16_t), rowlen, stdin) != rowlen) {
- goto readerr;
+ if (ferror(stdin)) {
+ fprintf(stderr, "%s: fread: %s\n", argv0, strerror(errno));
+ } else {
+ fprintf(stderr, "%s: unexpected end of file\n", argv0);
+ }
+ return 1;
}
png_write_row(pngs, (uint8_t *)row);
}
+
+ /* clean up */
png_write_end(pngs, NULL);
png_destroy_write_struct(&pngs, NULL);
- return 0;
-readerr:
- if (ferror(stdin)) {
- fprintf(stderr, "%s: fread: %s\n", argv0, strerror(errno));
- } else {
- fprintf(stderr, "%s: unexpected end of file\n", argv0);
- }
-
- return 1;
+ return fshut(stdout, "<stdout>");
}
diff --git a/ff2ppm.1 b/ff2ppm.1
index 7ada6c8..c611fba 100644
--- a/ff2ppm.1
+++ b/ff2ppm.1
@@ -1,17 +1,17 @@
-.Dd 2016-03-21
+.Dd 2017-04-14
.Dt FF2PPM 1
.Os suckless.org
.Sh NAME
.Nm ff2ppm
-.Nd convert farbfeld to PPM (binary)
+.Nd convert farbfeld to PPM
.Sh SYNOPSIS
.Nm
-.Op Fl b Ar color
+.Op Fl b Ar colour
.Sh DESCRIPTION
.Nm
reads a
.Xr farbfeld 5
-image from stdin, converts it to a PPM image (P6 binary format, RGB) and
+image from stdin, converts it to PPM (16-Bit RGB P6 binary format) and
writes the result to stdout.
.Pp
In case of an error
@@ -19,12 +19,10 @@ In case of an error
writes a diagnostic message to stderr.
.Sh OPTIONS
.Bl -tag -width Ds
-.It Fl b Ar color
-.Ar color
-to mix with the background alpha channel, the default is white.
-.Pp
-The following formats are supported:
-"#rrggbb", "#rrrrggggbbbb" and the short-form "#rgb" which expands to "#rrggbb".
+.It Fl b Ar colour
+Blend the transparent colours with
+.Ar colour
+specified as rgb, rrggbb or rrrrggggbbbb. The default is fff.
.El
.Sh EXIT STATUS
.Bl -tag -width Ds
@@ -35,14 +33,14 @@ An error occurred.
.El
.Sh EXAMPLES
$
-png2ff < test.png |
.Nm
--b '#00ff00' > test.ppm
+< image.ff > image.ppm
+.Pp
+$ bunzip2 < image.ff.bz2 |
+.Nm
+-b 0f0 > image.ppm
.Sh SEE ALSO
-.Xr 2ff 1 ,
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr png2ff 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org
diff --git a/ff2ppm.c b/ff2ppm.c
index 2a67eb2..b34d870 100644
--- a/ff2ppm.c
+++ b/ff2ppm.c
@@ -9,109 +9,75 @@
#include <string.h>
#include "arg.h"
-
-char *argv0;
+#include "util.h"
static void
usage(void)
{
- fprintf(stderr, "usage: %s [-b #rrggbb]\n", argv0);
+ fprintf(stderr, "usage: %s [-b colour]\n", argv0);
exit(1);
}
int
main(int argc, char *argv[])
{
- size_t rowlen;
+ size_t rowlen, rowoutlen;
uint64_t a;
- uint32_t hdr[4], width, height, i, j, k, l;
+ uint32_t width, height, i, j, k, l;
uint16_t *row, mask[3] = { 0xffff, 0xffff, 0xffff };
uint8_t *rowout;
- char *color, colfmt[] = "%#x%#x%#x";
- unsigned int collen, col[3], colfac;
- argv0 = argv[0];
+ /* arguments */
ARGBEGIN {
case 'b':
- color = EARGF(usage());
- if (color[0] == '#') {
- color++;
- }
- collen = strlen(color);
- if (collen != 3 && collen != 6 && collen != 12) {
- usage();
- }
- colfmt[1] = colfmt[4] = colfmt[7] = ((collen / 3) + '0');
- if (sscanf(color, colfmt, col, col + 1, col + 2) != 3) {
+ if (parse_mask(EARGF(usage()), mask)) {
usage();
}
- /* UINT16_MAX / 255 = 257; UINT16_MAX / 15 = 4369 */
- colfac = (collen == 3) ? 4369 : (collen == 6) ? 257 : 1;
- for (i = 0; i < 3; i++) {
- mask[i] = col[i] * colfac;
- }
break;
default:
usage();
} ARGEND
- if (argc)
+ if (argc) {
usage();
-
- /* header */
- if (fread(hdr, sizeof(*hdr), 4, stdin) != 4) {
- goto readerr;
- }
- if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
- fprintf(stderr, "%s: invalid magic value\n", argv0);
- return 1;
}
- width = ntohl(hdr[2]);
- height = ntohl(hdr[3]);
- if (width > SIZE_MAX / ((sizeof("RGBA") - 1) * sizeof(uint16_t))) {
- fprintf(stderr, "%s: row length integer overflow\n", argv0);
- return 1;
- }
+ /* prepare */
+ ff_read_header(&width, &height);
+ row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t));
+ rowout = ereallocarray(NULL, width, (sizeof("RGB") - 1) * sizeof(uint8_t));
rowlen = width * (sizeof("RGBA") - 1);
- if (!(row = malloc(rowlen * sizeof(uint16_t)))) {
- fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno));
- return 1;
- }
- if (!(rowout = malloc(width * (sizeof("RGB") - 1) * sizeof(uint8_t)))) {
- fprintf(stderr, "%s: malloc: %s\n", argv0, strerror(errno));
- return 1;
- }
+ rowoutlen = width * (sizeof("RGB") - 1);
- /* PPM binary */
+ /* write data */
printf("P6\n%" PRIu32 " %" PRIu32 "\n255\n", width, height);
- /* write rows */
for (i = 0; i < height; ++i) {
if (fread(row, sizeof(uint16_t), rowlen, stdin) != rowlen) {
- goto readerr;
+ if (ferror(stdin)) {
+ fprintf(stderr, "%s: fread: %s\n", argv0,
+ strerror(errno));
+ } else {
+ fprintf(stderr, "%s: unexpected end of file\n",
+ argv0);
+ }
+ return 1;
}
for (j = 0, k = 0; j < rowlen; j += 4, k += 3) {
a = ntohs(row[j + 3]);
for (l = 0; l < 3; l++) {
+ /* alpha blending and 8-bit-reduction */
rowout[k + l] = (a * ntohs(row[j + l]) +
- (65535 - a) * mask[l]) /
- (257 * 65535);
+ (UINT16_MAX - a) * mask[l]) /
+ (UINT16_MAX *
+ (UINT16_MAX / UINT8_MAX));
}
}
- if (fwrite(rowout, 3, width, stdout) != width) {
+ if (fwrite(rowout, sizeof(uint8_t), rowoutlen, stdout) != rowoutlen) {
fprintf(stderr, "%s: fwrite: %s\n", argv0, strerror(errno));
return 1;
}
}
- return 0;
-readerr:
- if (ferror(stdin)) {
- fprintf(stderr, "%s: fread: %s\n", argv0, strerror(errno));
- } else {
- fprintf(stderr, "%s: unexpected end of file\n", argv0);
- }
-
- return 1;
+ return fshut(stdout, "<stdout>");
}
diff --git a/jpg2ff.1 b/jpg2ff.1
index ada337d..d565fd8 100644
--- a/jpg2ff.1
+++ b/jpg2ff.1
@@ -1,4 +1,4 @@
-.Dd 2016-01-19
+.Dd 2017-04-14
.Dt JPG2FF 1
.Os suckless.org
.Sh NAME
@@ -32,10 +32,7 @@ $
< image.jpg | bzip2 > image.ff.bz2
.Sh SEE ALSO
.Xr 2ff 1 ,
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr ff2png 1 ,
-.Xr png2ff 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Laslo Hunhold Aq Mt dev@frign.de
diff --git a/jpg2ff.c b/jpg2ff.c
index 8ebc81d..af7b03c 100644
--- a/jpg2ff.c
+++ b/jpg2ff.c
@@ -5,12 +5,13 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <jpeglib.h>
-static char *argv0;
+#include "util.h"
-METHODDEF(void)
+static void
jpeg_error(j_common_ptr js)
{
fprintf(stderr, "%s: libjpeg: ", argv0);
@@ -18,86 +19,76 @@ jpeg_error(j_common_ptr js)
exit(1);
}
+static void
+jpeg_setup_reader(struct jpeg_decompress_struct *s, struct jpeg_error_mgr *e,
+ uint32_t *w, uint32_t *h)
+{
+ jpeg_create_decompress(s);
+ e->error_exit = jpeg_error;
+ s->err = jpeg_std_error(e);
+
+ jpeg_stdio_src(s, stdin);
+ jpeg_read_header(s, 1);
+ *w = s->image_width;
+ *h = s->image_height;
+ s->output_components = 3; /* color components per pixel */
+ s->out_color_space = JCS_RGB; /* input color space */
+
+ jpeg_start_decompress(s);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s\n", argv0);
+ exit(1);
+}
+
int
main(int argc, char *argv[])
{
struct jpeg_decompress_struct js;
struct jpeg_error_mgr jerr;
- uint32_t width, height, tmp32;
+ uint32_t width, height;
uint16_t *row;
+ uint8_t *rowin;
size_t rowlen, i;
- JSAMPARRAY jpgrow;
+ /* arguments */
argv0 = argv[0], argc--, argv++;
if (argc) {
- fprintf(stderr, "usage: %s\n", argv0);
- return 1;
+ usage();
}
- /* load jpg */
- js.err = jpeg_std_error(&jerr);
- jerr.error_exit = jpeg_error;
-
- jpeg_create_decompress(&js);
-
- jpeg_stdio_src(&js, stdin);
-
- jpeg_read_header(&js, 1);
- width = js.image_width;
- height = js.image_height;
-
- /* set output format */
- js.output_components = 3; /* color components per pixel */
- js.out_color_space = JCS_RGB; /* input color space */
-
- jpeg_start_decompress(&js);
-
- /* create output buffers */
- jpgrow = (*js.mem->alloc_sarray)((j_common_ptr)&js,
- JPOOL_IMAGE, width *
- js.output_components, 1);
+ /* prepare */
+ jpeg_setup_reader(&js, &jerr, &width, &height);
+ row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t));
rowlen = width * (sizeof("RGBA") - 1);
- if(!(row = malloc(rowlen * sizeof(uint16_t)))) {
- fprintf(stderr, "%s: malloc: out of memory\n", argv0);
- return 1;
- }
+ rowin = ereallocarray(NULL, width, (sizeof("RGB") - 1) * sizeof(uint8_t));
- /* write header */
- fputs("farbfeld", stdout);
- tmp32 = htonl(width);
- if (fwrite(&tmp32, sizeof(uint32_t), 1, stdout) != 1)
- goto writerr;
- tmp32 = htonl(height);
- if (fwrite(&tmp32, sizeof(uint32_t), 1, stdout) != 1)
- goto writerr;
+ /* write data */
+ ff_write_header(width, height);
while (js.output_scanline < js.output_height) {
- /* jpeg_read_scanlines expects an array of pointers to
- * scanlines.
- * Here the array is only one element long, but you could
- * ask for more than one scanline at a time if that's more
- * convenient. */
- jpeg_read_scanlines(&js, jpgrow, 1);
+ jpeg_read_scanlines(&js, &rowin, 1);
for (i = 0; i < width; ++i) {
- row[4*i + 0] = htons(jpgrow[0][3*i + 0] * 257);
- row[4*i + 1] = htons(jpgrow[0][3*i + 1] * 257);
- row[4*i + 2] = htons(jpgrow[0][3*i + 2] * 257);
- row[4*i + 3] = htons(65535);
+ row[4 * i + 0] = htons(rowin[3 * i + 0] * 257);
+ row[4 * i + 1] = htons(rowin[3 * i + 1] * 257);
+ row[4 * i + 2] = htons(rowin[3 * i + 2] * 257);
+ row[4 * i + 3] = htons(65535);
}
- /* write data */
- if (fwrite(row, sizeof(uint16_t), rowlen, stdout) != rowlen)
- goto writerr;
+ if (fwrite(row, sizeof(uint16_t), rowlen, stdout) != rowlen) {
+ fprintf(stderr, "%s: fwrite: %s\n", argv0, strerror(errno));
+ return 1;
+ }
}
+
+ /* clean up */
jpeg_finish_decompress(&js);
jpeg_destroy_decompress(&js);
- return 0;
-writerr:
- fprintf(stderr, "%s: fwrite: ", argv0);
- perror(NULL);
-
- return 1;
+ return fshut(stdout, "<stdout>");
}
diff --git a/png2ff.1 b/png2ff.1
index 9761ec5..a2e4275 100644
--- a/png2ff.1
+++ b/png2ff.1
@@ -1,4 +1,4 @@
-.Dd 2016-01-19
+.Dd 2017-04-14
.Dt PNG2FF 1
.Os suckless.org
.Sh NAME
@@ -32,10 +32,7 @@ $
< image.png | bzip2 > image.ff.bz2
.Sh SEE ALSO
.Xr 2ff 1 ,
-.Xr bunzip2 1 ,
.Xr bzip2 1 ,
-.Xr ff2png 1 ,
-.Xr jpg2ff 1 ,
.Xr farbfeld 5
.Sh AUTHORS
.An Laslo Hunhold Aq Mt dev@frign.de
diff --git a/png2ff.c b/png2ff.c
index 55456e3..c7237c4 100644
--- a/png2ff.c
+++ b/png2ff.c
@@ -5,77 +5,76 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <png.h>
-static char *argv0;
+#include "util.h"
-void
-pngerr(png_structp pngs, const char *msg)
+static void
+png_err(png_struct *pngs, const char *msg)
{
+ (void)pngs;
fprintf(stderr, "%s: libpng: %s\n", argv0, msg);
exit(1);
}
+static void
+png_setup_reader(png_struct **s, png_info **i, uint32_t *w, uint32_t *h)
+{
+ *s = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_err, NULL);
+ *i = png_create_info_struct(*s);
+
+ if (!*s || !*i) {
+ fprintf(stderr, "%s: failed to initialize libpng\n", argv0);
+ exit(1);
+ }
+
+ png_init_io(*s, stdin);
+ if (png_get_valid(*s, *i, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(*s);
+ }
+ png_set_add_alpha(*s, 255*257, PNG_FILLER_AFTER);
+ png_set_expand_gray_1_2_4_to_8(*s);
+ png_set_gray_to_rgb(*s);
+ png_set_packing(*s);
+ png_read_png(*s, *i, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL);
+ *w = png_get_image_width(*s, *i);
+ *h = png_get_image_height(*s, *i);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s\n", argv0);
+ exit(1);
+}
+
int
main(int argc, char *argv[])
{
- png_structp pngs;
- png_infop pngi;
- uint32_t width, height, rowlen, tmp32, r, i;
+ png_struct *pngs;
+ png_info *pngi;
+ uint32_t width, height, rowlen, r, i;
uint16_t *row;
uint8_t **pngrows;
+ /* arguments */
argv0 = argv[0], argc--, argv++;
if (argc) {
- fprintf(stderr, "usage: %s\n", argv0);
- return 1;
+ usage();
}
- /* load png */
- pngs = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, pngerr,
- NULL);
- pngi = png_create_info_struct(pngs);
-
- if (!pngs || !pngi) {
- fprintf(stderr, "%s: failed to initialize libpng\n", argv0);
- return 1;
- }
- png_init_io(pngs, stdin);
- if (png_get_valid(pngs, pngi, PNG_INFO_tRNS))
- png_set_tRNS_to_alpha(pngs);
- png_set_add_alpha(pngs, 255*257, PNG_FILLER_AFTER);
- png_set_expand_gray_1_2_4_to_8(pngs);
- png_set_gray_to_rgb(pngs);
- png_set_packing(pngs);
- png_read_png(pngs, pngi, PNG_TRANSFORM_PACKING |
- PNG_TRANSFORM_EXPAND, NULL);
- width = png_get_image_width(pngs, pngi);
- height = png_get_image_height(pngs, pngi);
- pngrows = png_get_rows(pngs, pngi);
-
- /* allocate output row buffer */
- if (width > SIZE_MAX / ((sizeof("RGBA") - 1) * sizeof(uint16_t))) {
- fprintf(stderr, "%s: row length integer overflow\n", argv0);
- return 1;
- }
+ /* prepare */
+ png_setup_reader(&pngs, &pngi, &width, &height);
+ row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t));
rowlen = width * (sizeof("RGBA") - 1);
- if (!(row = malloc(rowlen * sizeof(uint16_t)))) {
- fprintf(stderr, "%s: malloc: out of memory\n", argv0);
- return 1;
- }
-
- /* write header */
- fputs("farbfeld", stdout);
- tmp32 = htonl(width);
- if (fwrite(&tmp32, sizeof(uint32_t), 1, stdout) != 1)
- goto writerr;
- tmp32 = htonl(height);
- if (fwrite(&tmp32, sizeof(uint32_t), 1, stdout) != 1)
- goto writerr;
+ pngrows = png_get_rows(pngs, pngi);
/* write data */
+ ff_write_header(width, height);
+
switch(png_get_bit_depth(pngs, pngi)) {
case 8:
for (r = 0; r < height; ++r) {
@@ -84,7 +83,9 @@ main(int argc, char *argv[])
}
if (fwrite(row, sizeof(uint16_t), rowlen,
stdout) != rowlen) {
- goto writerr;
+ fprintf(stderr, "%s: fwrite: %s\n", argv0,
+ strerror(errno));
+ return 1;
}
}
break;
@@ -92,7 +93,9 @@ main(int argc, char *argv[])
for (r = 0; r < height; ++r) {
if (fwrite(pngrows[r], sizeof(uint16_t), rowlen,
stdout) != rowlen) {
- goto writerr;
+ fprintf(stderr, "%s: fwrite: %s\n", argv0,
+ strerror(errno));
+ return 1;
}
}
break;
@@ -101,12 +104,8 @@ main(int argc, char *argv[])
return 1;
}
+ /* clean up */
png_destroy_read_struct(&pngs, &pngi, NULL);
- return 0;
-writerr:
- fprintf(stderr, "%s: fwrite: ", argv0);
- perror(NULL);
-
- return 1;
+ return fshut(stdout, "<stdout>");
}
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..ba139b4
--- /dev/null
+++ b/util.c
@@ -0,0 +1,238 @@
+/* See LICENSE file for copyright and license details. */
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+char *argv0;
+
+void
+ff_read_header(uint32_t *width, uint32_t *height)
+{
+ uint32_t hdr[4];
+
+ if (fread(hdr, sizeof(*hdr), LEN(hdr), stdin) != LEN(hdr)) {
+ fprintf(stderr, "%s: fread: %s", argv0, strerror(errno));
+ exit(1);
+ }
+
+ if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) {
+ fprintf(stderr, "%s: invalid magic value\n", argv0);
+ exit(1);
+ }
+
+ *width = ntohl(hdr[2]);
+ *height = ntohl(hdr[3]);
+}
+
+void
+ff_write_header(uint32_t width, uint32_t height)
+{
+ uint32_t tmp;
+
+ fputs("farbfeld", stdout);
+
+ tmp = htonl(width);
+ if (fwrite(&tmp, sizeof(tmp), 1, stdout) != 1) {
+ fprintf(stderr, "%s: write: %s", argv0, strerror(errno));
+ exit(1);
+ }
+
+ tmp = htonl(height);
+ if (fwrite(&tmp, sizeof(tmp), 1, stdout) != 1) {
+ fprintf(stderr, "%s: write: %s", argv0, strerror(errno));
+ exit(1);
+ }
+}
+
+int
+parse_mask(const char *s, uint16_t mask[3])
+{
+ size_t slen, i;
+ unsigned int col[3], colfac;
+ char fmt[] = "%#x%#x%#x";
+
+ slen = strlen(s);
+ if (slen != 3 && slen != 6 && slen != 12) {
+ return 1;
+ }
+
+ fmt[1] = fmt[4] = fmt[7] = ((slen / 3) + '0');
+ if (sscanf(s, fmt, col, col + 1, col + 2) != 3) {
+ return 1;
+ }
+
+ colfac = (slen == 3) ? UINT16_MAX / 0xf :
+ (slen == 6) ? UINT16_MAX / 0xff :
+ UINT16_MAX / 0xffff;
+
+ for (i = 0; i < 3; i++) {
+ mask[i] = col[i] * colfac;
+ }
+
+ return 0;
+}
+
+int
+fshut(FILE *fp, const char *fname)
+{
+ int ret = 0;
+
+ /* fflush() is undefined for input streams by ISO C,
+ * but not POSIX 2008 if you ignore ISO C overrides.
+ * Leave it unchecked and rely on the following
+ * functions to detect errors.
+ */
+ fflush(fp);
+
+ if (ferror(fp) && !ret) {
+ fprintf(stderr, "%s: ferror %s: %s\n", argv0, fname,
+ strerror(errno));
+ ret = 1;
+ }
+
+ if (fclose(fp) && !ret) {
+ fprintf(stderr, "%s: fclose %s: %s\n", argv0, fname,
+ strerror(errno));
+ ret = 1;
+ }
+
+ return ret;
+}
+
+void *
+ereallocarray(void *optr, size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = reallocarray(optr, nmemb, size))) {
+ fprintf(stderr, "%s: reallocarray: out of memory\n", argv0);
+ exit(1);
+ }
+
+ return p;
+}
+
+long long
+estrtonum(const char *numstr, long long minval, long long maxval)
+{
+ const char *errstr;
+ long long ll;
+
+ ll = strtonum(numstr, minval, maxval, &errstr);
+ if (errstr) {
+ fprintf(stderr, "%s: strtonum %s: %s\n", argv0, numstr, errstr);
+ exit(1);
+ }
+
+ return ll;
+}
+
+/*
+ * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4))
+
+void *
+reallocarray(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(optr, size * nmemb);
+}
+
+/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#define INVALID 1
+#define TOOSMALL 2
+#define TOOLARGE 3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+ const char **errstrp)
+{
+ long long ll = 0;
+ int error = 0;
+ char *ep;
+ struct errval {
+ const char *errstr;
+ int err;
+ } ev[4] = {
+ { NULL, 0 },
+ { "invalid", EINVAL },
+ { "too small", ERANGE },
+ { "too large", ERANGE },
+ };
+
+ ev[0].err = errno;
+ errno = 0;
+ if (minval > maxval) {
+ error = INVALID;
+ } else {
+ ll = strtoll(numstr, &ep, 10);
+ if (numstr == ep || *ep != '\0')
+ error = INVALID;
+ else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+ error = TOOSMALL;
+ else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+ error = TOOLARGE;
+ }
+ if (errstrp != NULL)
+ *errstrp = ev[error].errstr;
+ errno = ev[error].err;
+ if (error)
+ ll = 0;
+
+ return (ll);
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..fcb5257
--- /dev/null
+++ b/util.h
@@ -0,0 +1,22 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdint.h>
+#include <stdio.h>
+
+extern char *argv0;
+
+#define LEN(x) (sizeof (x) / sizeof *(x))
+
+void ff_read_header(uint32_t *width, uint32_t *height);
+void ff_write_header(uint32_t width, uint32_t height);
+
+int parse_mask(const char *, uint16_t mask[3]);
+
+int fshut(FILE *, const char *);
+
+#undef reallocarray
+void *reallocarray(void *, size_t, size_t);
+void *ereallocarray(void *optr, size_t nmemb, size_t size);
+
+#undef strtonum
+long long strtonum(const char *, long long, long long, const char **);
+long long estrtonum(const char *, long long, long long);