farbfeld-4/0000755000175000017510000000000013263365172012007 5ustar frignnonetfarbfeld-4/ff2pam.10000644000175000017510000000111513263365172013242 0ustar frignnonet.Dd 2018-04-11 .Dt FF2PAM 1 .Os suckless.org .Sh NAME .Nm ff2pam .Nd convert farbfeld to PAM .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm reads a .Xr farbfeld 5 image from stdin, converts it to PAM (16-bit RGBA) and writes the result to stdout. .Pp In case of an error .Nm writes a diagnostic message to stderr. .Sh EXIT STATUS .Bl -tag -width Ds .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.ff > image.pam .Pp $ bunzip2 < image.ff.bz2 | .Nm > image.pam .Sh SEE ALSO .Xr bzip2 1 , .Xr farbfeld 5 .Sh AUTHORS .An Mattias Andrée Aq Mt maandree@kth.se farbfeld-4/LICENSE0000644000175000017510000000223013263365172013011 0ustar frignnonetISC-License Copyright 2014-2018 Laslo Hunhold Copyright 2004 Ted Unangst Copyright 2004 Todd C. Miller Copyright 2008 Otto Moerbeek Copyright 2014-2015 Dimitris Papastamos Copyright 2014-2016 Hiltjo Posthuma Copyright 2015 Willy Goiffon Copyright 2016 Alexander Krotov Copyright 2017 Mattias Andrée Permission to use, copy, modify, and/or 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. farbfeld-4/png2ff.10000644000175000017510000000110413263365172013247 0ustar frignnonet.Dd 2018-04-11 .Dt PNG2FF 1 .Os suckless.org .Sh NAME .Nm png2ff .Nd convert PNG to farbfeld .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm reads a PNG image from stdin, converts it to .Xr farbfeld 5 and writes the result to stdout. .Pp In case of an error .Nm writes a diagnostic message to stderr. .Sh EXIT STATUS .Bl -tag -width Ds .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.png > image.ff .Pp $ .Nm < image.png | bzip2 > image.ff.bz2 .Sh SEE ALSO .Xr 2ff 1 , .Xr bzip2 1 , .Xr farbfeld 5 .Sh AUTHORS .An Laslo Hunhold Aq Mt dev@frign.de farbfeld-4/Makefile0000644000175000017510000000374013263365172013453 0ustar frignnonet# See LICENSE file for copyright and license details # farbfeld - suckless image format with conversion tools .POSIX: include config.mk REQ = util HDR = arg.h BIN = png2ff ff2png jpg2ff ff2jpg ff2pam ff2ppm SCR = 2ff MAN1 = 2ff.1 $(BIN:=.1) MAN5 = farbfeld.5 all: $(BIN) png2ff-LDLIBS = $(PNG-LDLIBS) ff2png-LDLIBS = $(PNG-LDLIBS) jpg2ff-LDLIBS = $(JPG-LDLIBS) ff2jpg-LDLIBS = $(JPG-LDLIBS) png2ff: png2ff.o $(REQ:=.o) ff2png: ff2png.o $(REQ:=.o) jpg2ff: jpg2ff.o $(REQ:=.o) ff2jpg: ff2jpg.o $(REQ:=.o) ff2pam: ff2pam.o $(REQ:=.o) ff2ppm: ff2ppm.o $(REQ:=.o) png2ff.o: png2ff.c config.mk $(HDR) $(REQ:=.h) ff2png.o: ff2png.c config.mk $(HDR) $(REQ:=.h) jpg2ff.o: jpg2ff.c config.mk $(HDR) $(REQ:=.h) ff2jpg.o: ff2jpg.c config.mk $(HDR) $(REQ:=.h) ff2pam.o: ff2pam.c config.mk $(HDR) $(REQ:=.h) ff2ppm.o: ff2ppm.c config.mk $(HDR) $(REQ:=.h) .o: $(CC) -o $@ $(LDFLAGS) $< $(REQ:=.o) $($*-LDLIBS) .c.o: $(CC) -c $(CPPFLAGS) $(CFLAGS) $< clean: rm -f $(BIN) $(BIN:=.o) $(REQ:=.o) dist: 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 $(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) $(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 farbfeld-4/ff2png.c0000644000175000017510000000276613263365172013350 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include "util.h" static void png_err(png_struct *pngs, const char *msg) { (void)pngs; die("libpng: %s", msg); } 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) { die("Failed to initialize libpng"); } 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) { die("usage: %s", argv0); } int main(int argc, char *argv[]) { png_struct *pngs; png_info *pngi; size_t rowlen; uint32_t width, height, i; uint16_t *row; /* arguments */ argv0 = argv[0], argc--, argv++; if (argc) { usage(); } /* 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); /* write data */ for (i = 0; i < height; ++i) { efread(row, sizeof(uint16_t), rowlen, stdin); png_write_row(pngs, (uint8_t *)row); } /* clean up */ png_write_end(pngs, NULL); png_destroy_write_struct(&pngs, NULL); return fshut(stdout, ""); } farbfeld-4/farbfeld.50000644000175000017510000001031513263365172013642 0ustar frignnonet.Dd 2018-04-11 .Dt FARBFELD 5 .Os suckless.org .Sh NAME .Nm farbfeld .Nd suckless image format .Sh DESCRIPTION .Nm is a .Em lossless image format which is easy to parse, pipe and compress. It has the following format: .Bd -literal -offset left BYTES DESCRIPTION 8 "farbfeld" magic value 4 32-Bit BE unsigned integer (width) 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 the tools .Xr 2ff 1 , .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 , giving results comparable with PNG for photographs and much better results for other image types. .sp The .Em file extension is ".ff" and compression extensions shall be appended (e.g. ".ff.bz2"). .Sh MOTIVATION .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 while not being able to focus on the task at hand for a given image processing problem. .Sh EXAMPLES 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 * * 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 #include #include #include #include #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 hdr[4], width, height, i, j, k; uint16_t rgba[4]; /* arguments */ if (argc != 1) { fprintf(stderr, "usage: %s\\n", argv[0]); return 1; } /* read header */ if (fread(hdr, sizeof(*hdr), LEN(hdr), stdin) != LEN(hdr)) { goto readerr; } if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) { fprintf(stderr, "%s: invalid magic value\\n", argv[0]); return 1; } width = ntohl(hdr[2]); height = ntohl(hdr[3]); /* 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(*rgba), LEN(rgba), stdin) != LEN(rgba)) { goto readerr; } for (k = 0; k < 4; k++) { rgba[k] = ntohs(rgba[k]); } invert(rgba); for (k = 0; k < 4; k++) { rgba[k] = htons(rgba[k]); } 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 ff2jpg 1 , .Xr ff2pam 1 , .Xr ff2png 1 , .Xr ff2ppm 1 , .Xr jpg2ff 1 , .Xr png2ff 1 .Sh AUTHORS .An Laslo Hunhold Aq Mt dev@frign.de farbfeld-4/README0000644000175000017510000000563513263365172012700 0ustar frignnonet ███ ███ ██ ██ ███ ███ █ ██ █ █ █ █ █ █ █ █ █ █ █ █ ██ ███ ██ ███ ██ ██ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ██ █ ███ ███ ██ WHAT IS FARBFELD? Farbfeld is a lossless image-format designed to be 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, making it complicated to read the image data. One is forced to use complex libraries like libpng, libjpeg, libjpeg-turbo, giflib and others, read the documentation and write a lot of boilerplate in order to get started. Farbfeld leaves this behind and is designed to be as simple as possible, leaving the task of compression to outside tools. The simple design, which was the primary objective, implicitly lead to the very good compression characteristics, as it often happens when you go with the UNIX philosophy. Reading farbfeld images doesn't require any special libraries. The tools in this folder are just a toolbox to make it easy to convert between common image formats and farbfeld. HOW DOES IT WORK? 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. bzip2) recognizes the pattern that every 48 bits the 16 bits store the same information. And the compression-algorithms get better and better at this. Same applies to the idea of having 16 bits per channel. It sounds excessive, but if you for instance only have a greyscale image, the R, G and B channels will store 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 bzip2 even beats png's compression, for instance when you're dealing with grayscale data, line drawings, decals and even photographs. farbfeld-4/arg.h0000644000175000017510000000461313263365172012735 0ustar frignnonet/* * ISC-License * * Copyright 2017 Laslo Hunhold * * Permission to use, copy, modify, and/or 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. */ #ifndef ARG_H #define ARG_H extern char *argv0; /* int main(int argc, char *argv[]) */ #define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \ *argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \ int i_, argused_; \ if ((*argv)[1] == '-' && !(*argv)[2]) { \ argc--, argv++; \ break; \ } \ for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \ switch((*argv)[i_]) #define ARGEND if (argused_) { \ if ((*argv)[i_ + 1]) { \ break; \ } else { \ argc--, argv++; \ break; \ } \ } \ } \ } #define ARGC() ((*argv)[i_]) #define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \ (*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x)) #define EARGF(x) ARGF_(((x), exit(1), (char *)0)) #define ARGF() ARGF_((char *)0) #endif farbfeld-4/util.h0000644000175000017510000000142013263365172013132 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #define LEN(x) (sizeof (x) / sizeof *(x)) extern char *argv0; void warn(const char *, ...); void die(const char *, ...); 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 *); void efread(void *, size_t, size_t, FILE *); void efwrite(const void *, size_t, size_t, FILE *); #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); farbfeld-4/ff2jpg.c0000644000175000017510000000465513263365172013343 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include "arg.h" #include "util.h" static void jpeg_error(j_common_ptr js) { fprintf(stderr, "%s: libjpeg: ", argv0); (*js->err->output_message)(js); exit(1); } 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) { die("usage: %s [-b colour] [-o] [-q quality]", argv0); } int main(int argc, char *argv[]) { struct jpeg_compress_struct jcomp; struct jpeg_error_mgr jerr; size_t rowlen; uint64_t a; uint32_t width, height, i, j, k, l; uint16_t *row, mask[3] = { 0xffff, 0xffff, 0xffff }; uint8_t *rowout; int optimize = 0, quality = 85; /* arguments */ ARGBEGIN { case 'b': if (parse_mask(EARGF(usage()), mask)) { usage(); } break; case 'o': optimize = 1; break; case 'q': quality = estrtonum(EARGF(usage()), 0, 100); break; default: usage(); } ARGEND if (argc) { usage(); } /* 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); rowout = ereallocarray(NULL, width, (sizeof("RGB") - 1) * sizeof(uint8_t)); /* write data */ for (i = 0; i < height; ++i) { efread(row, sizeof(uint16_t), rowlen, stdin); 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]) + (UINT16_MAX - a) * mask[l]) / (UINT16_MAX * (UINT16_MAX / UINT8_MAX)); } } jpeg_write_scanlines(&jcomp, &rowout, 1); } /* clean up */ jpeg_finish_compress(&jcomp); jpeg_destroy_compress(&jcomp); return fshut(stdout, ""); } farbfeld-4/ff2ppm.c0000644000175000017510000000276613263365172013360 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include "arg.h" #include "util.h" static void usage(void) { die("usage: %s [-b colour]", argv0); } int main(int argc, char *argv[]) { size_t rowlen, rowoutlen; uint64_t a; uint32_t width, height, i, j, k, l; uint16_t *row, mask[3] = { 0xffff, 0xffff, 0xffff }; uint8_t *rowout; /* arguments */ ARGBEGIN { case 'b': if (parse_mask(EARGF(usage()), mask)) { usage(); } break; default: usage(); } ARGEND if (argc) { usage(); } /* 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); rowoutlen = width * (sizeof("RGB") - 1); /* write data */ printf("P6\n%" PRIu32 " %" PRIu32 "\n255\n", width, height); for (i = 0; i < height; ++i) { efread(row, sizeof(uint16_t), rowlen, stdin); 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]) + (UINT16_MAX - a) * mask[l]) / (UINT16_MAX * (UINT16_MAX / UINT8_MAX)); } } efwrite(rowout, sizeof(uint8_t), rowoutlen, stdout); } return fshut(stdout, ""); } farbfeld-4/2ff0000755000175000017510000000102013263365172012403 0ustar frignnonet#!/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" # determine the mime-type if [ "$(dd if="$TMP" bs=1 count=8 2>/dev/null | tr -d '\0')" = "farbfeld" ]; then cat "$TMP" else MIME=$(file -ib "$TMP" | cut -d ";" -f 1) case "$MIME" in image/png) png2ff < "$TMP" ;; image/jpeg) jpg2ff < "$TMP" ;; *) convert "$TMP" png:- | png2ff ;; esac fi # errors if [ $? -ne 0 ]; then exit 1 else exit 0 fi farbfeld-4/ff2png.10000644000175000017510000000111013263365172013244 0ustar frignnonet.Dd 2018-04-11 .Dt FF2PNG 1 .Os suckless.org .Sh NAME .Nm ff2png .Nd convert farbfeld to PNG .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm reads a .Xr farbfeld 5 image from stdin, converts it to PNG (16-bit RGBA) and writes the result to stdout. .Pp In case of an error .Nm writes a diagnostic message to stderr. .Sh EXIT STATUS .Bl -tag -width Ds .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.ff > image.png .Pp $ bunzip2 < image.ff.bz2 | .Nm > image.png .Sh SEE ALSO .Xr bzip2 1 , .Xr farbfeld 5 .Sh AUTHORS .An Laslo Hunhold Aq Mt dev@frign.de farbfeld-4/png2ff.c0000644000175000017510000000371413263365172013342 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include "util.h" static void png_err(png_struct *pngs, const char *msg) { (void)pngs; die("libpng: %s", msg); } 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) { die("Failed to initialize libpng"); } 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) { die("usage: %s", argv0); } int main(int argc, char *argv[]) { 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) { usage(); } /* prepare */ png_setup_reader(&pngs, &pngi, &width, &height); row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t)); rowlen = width * (sizeof("RGBA") - 1); 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) { for (i = 0; i < rowlen; i++) { row[i] = htons(257 * pngrows[r][i]); } efwrite(row, sizeof(uint16_t), rowlen, stdout); } break; case 16: for (r = 0; r < height; ++r) { efwrite(pngrows[r], sizeof(uint16_t), rowlen, stdout); } break; default: die("Invalid bit-depth"); } /* clean up */ png_destroy_read_struct(&pngs, &pngi, NULL); return fshut(stdout, ""); } farbfeld-4/ff2jpg.10000644000175000017510000000173513263365172013255 0ustar frignnonet.Dd 2018-04-11 .Dt FF2JPG 1 .Os suckless.org .Sh NAME .Nm ff2jpg .Nd convert farbfeld to JPG .Sh SYNOPSIS .Nm .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 JPG (8-bit RGB) and writes the result to stdout. .Pp In case of an error .Nm writes a diagnostic message to stderr. .Sh OPTIONS .Bl -tag -width Ds .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 the Huffman table, which reduces the file size but takes longer. .It Fl q Ar quality Set the output .Ar quality ranging from 0 to 100. The default is 85. .El .Sh EXIT STATUS .Bl -tag -width Ds .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.ff > image.jpg .Pp $ bunzip2 < image.ff.bz2 | .Nm -b 0f0 -q 90 > image.jpg .Sh SEE ALSO .Xr bzip2 1 , .Xr farbfeld 5 .Sh AUTHORS .An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org farbfeld-4/jpg2ff.10000644000175000017510000000110413263365172013243 0ustar frignnonet.Dd 2018-04-11 .Dt JPG2FF 1 .Os suckless.org .Sh NAME .Nm jpg2ff .Nd convert JPG to farbfeld .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm reads a JPG image from stdin, converts it to .Xr farbfeld 5 and writes the result to stdout. .Pp In case of an error .Nm writes a diagnostic message to stderr. .Sh EXIT STATUS .Bl -tag -width Ds .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.jpg > image.ff .Pp $ .Nm < image.jpg | bzip2 > image.ff.bz2 .Sh SEE ALSO .Xr 2ff 1 , .Xr bzip2 1 , .Xr farbfeld 5 .Sh AUTHORS .An Laslo Hunhold Aq Mt dev@frign.de farbfeld-4/FORMAT0000644000175000017510000000320513263365172012722 0ustar frignnonet FARBFELD IMAGE FORMAT SPECIFICATION ╔════════╤═════════════════════════════════════════════════════════╗ ║ Bytes │ Description ║ ╠════════╪═════════════════════════════════════════════════════════╣ ║ 8 │ "farbfeld" magic value ║ ╟────────┼─────────────────────────────────────────────────────────╢ ║ 4 │ 32-Bit BE unsigned integer (width) ║ ╟────────┼─────────────────────────────────────────────────────────╢ ║ 4 │ 32-Bit BE unsigned integer (height) ║ ╟────────┼─────────────────────────────────────────────────────────╢ ║ [2222] │ 4⋅16-Bit BE unsigned integers [RGBA] / pixel, row-major ║ ╚════════╧═════════════════════════════════════════════════════════╝ farbfeld-4/ff2pam.c0000644000175000017510000000203213263365172013323 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include "util.h" static void usage(void) { die("usage: %s", argv0); } int main(int argc, char *argv[]) { size_t rowlen; uint32_t width, height, i; uint16_t *row; /* arguments */ argv0 = argv[0], argc--, argv++; if (argc) { usage(); } /* prepare */ ff_read_header(&width, &height); row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t)); rowlen = width * (sizeof("RGBA") - 1); /* write data */ printf("P7\n" "WIDTH %" PRIu32 "\n" "HEIGHT %" PRIu32 "\n" "DEPTH 4\n" /* number of channels */ "MAXVAL 65535\n" "TUPLTYPE RGB_ALPHA\n" "ENDHDR\n", width, height); for (i = 0; i < height; i++) { efread(row, sizeof(uint16_t), rowlen, stdin); efwrite(row, sizeof(uint16_t), rowlen, stdout); } return fshut(stdout, ""); } farbfeld-4/util.c0000644000175000017510000001014513263365172013131 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include #include "util.h" char *argv0; static void verr(const char *fmt, va_list ap) { if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) { fprintf(stderr, "%s: ", argv0); } vfprintf(stderr, fmt, ap); if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { fputc(' ', stderr); perror(NULL); } else { fputc('\n', stderr); } } void warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(fmt, ap); va_end(ap); } void die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(fmt, ap); va_end(ap); exit(1); } void ff_read_header(uint32_t *width, uint32_t *height) { uint32_t hdr[4]; efread(hdr, sizeof(*hdr), LEN(hdr), stdin); if (memcmp("farbfeld", hdr, sizeof("farbfeld") - 1)) { die("Invalid magic value"); } *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); efwrite(&tmp, sizeof(tmp), 1, stdout); tmp = htonl(height); efwrite(&tmp, sizeof(tmp), 1, stdout); } 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) { warn("ferror '%s':", fname); ret = 1; } if (fclose(fp) && !ret) { warn("fclose '%s':", fname); ret = 1; } return ret; } void efread(void *p, size_t s, size_t n, FILE *f) { if (fread(p, s, n, f) != n) { if (ferror(f)) { die("fread:"); } else { die("fread: Unexpected end of file"); } } } void efwrite(const void *p, size_t s, size_t n, FILE *f) { if (fwrite(p, s, n, f) != n) { die("fwrite:"); } } void * ereallocarray(void *optr, size_t nmemb, size_t size) { void *p; if (!(p = reallocarray(optr, nmemb, size))) { die("reallocarray: Out of memory"); } 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) { die("strtonum '%s': %s", numstr, errstr); } return ll; } /* * 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); } #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); } farbfeld-4/jpg2ff.c0000644000175000017510000000360513263365172013335 0ustar frignnonet/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include "util.h" static void jpeg_error(j_common_ptr js) { fprintf(stderr, "%s: libjpeg: ", argv0); (*js->err->output_message)(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) { die("usage: %s", argv0); } int main(int argc, char *argv[]) { struct jpeg_decompress_struct js; struct jpeg_error_mgr jerr; uint32_t width, height; uint16_t *row; uint8_t *rowin; size_t rowlen, i; /* arguments */ argv0 = argv[0], argc--, argv++; if (argc) { usage(); } /* prepare */ jpeg_setup_reader(&js, &jerr, &width, &height); row = ereallocarray(NULL, width, (sizeof("RGBA") - 1) * sizeof(uint16_t)); rowlen = width * (sizeof("RGBA") - 1); rowin = ereallocarray(NULL, width, (sizeof("RGB") - 1) * sizeof(uint8_t)); /* write data */ ff_write_header(width, height); while (js.output_scanline < js.output_height) { jpeg_read_scanlines(&js, &rowin, 1); for (i = 0; i < width; ++i) { 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); } efwrite(row, sizeof(uint16_t), rowlen, stdout); } /* clean up */ jpeg_finish_decompress(&js); jpeg_destroy_decompress(&js); return fshut(stdout, ""); } farbfeld-4/ff2ppm.10000644000175000017510000000144013263365172013262 0ustar frignnonet.Dd 2018-04-11 .Dt FF2PPM 1 .Os suckless.org .Sh NAME .Nm ff2ppm .Nd convert farbfeld to PPM .Sh SYNOPSIS .Nm .Op Fl b Ar colour .Sh DESCRIPTION .Nm reads a .Xr farbfeld 5 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 .Nm writes a diagnostic message to stderr. .Sh OPTIONS .Bl -tag -width Ds .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 .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.ff > image.ppm .Pp $ bunzip2 < image.ff.bz2 | .Nm -b 0f0 > image.ppm .Sh SEE ALSO .Xr bzip2 1 , .Xr farbfeld 5 .Sh AUTHORS .An Hiltjo Posthuma Aq Mt hiltjo@codemadness.org farbfeld-4/2ff.10000644000175000017510000000134313263365172012547 0ustar frignnonet.Dd 2018-04-11 .Dt 2FF 1 .Os suckless.org .Sh NAME .Nm 2ff .Nd convert image to farbfeld .Sh SYNOPSIS .Nm .Sh DESCRIPTION .Nm reads an image from stdin, converts it to .Xr farbfeld 5 and writes the result to stdout. .Pp .Nm 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 In case of an error .Nm writes a diagnostic message to stderr. .Sh EXIT STATUS .Bl -tag -width Ds .It 0 Image processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES $ .Nm < image.* > image.ff .Pp $ .Nm < image.* | bzip2 > image.ff.bz2 .Sh SEE ALSO .Xr bzip2 1 , .Xr ImageMagick 1 , .Xr farbfeld 5 .Sh AUTHORS .An Laslo Hunhold Aq Mt dev@frign.de farbfeld-4/config.mk0000644000175000017510000000045413263365172013610 0ustar frignnonet# farbfeld version VERSION = 4 # Customize below to fit your system # paths PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man # flags CPPFLAGS = -D_DEFAULT_SOURCE CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os LDFLAGS = -s PNG-LDLIBS = -lpng JPG-LDLIBS = -ljpeg # compiler and linker CC = cc