diff options
author | FRIGN <dev@frign.de> | 2015-11-09 23:39:29 +0100 |
---|---|---|
committer | FRIGN <dev@frign.de> | 2015-11-09 23:40:46 +0100 |
commit | d5f6f70d351239ee37b3d864c9a94dc49d66c052 (patch) | |
tree | 47993f137fd273df86a4f24fe7fe22098c435e0a | |
parent | 0b0fcaa7136d7de2e1bc5dc05a576f5fe5608995 (diff) |
imagefile -> farbfeld
- Rename the format
- Change the format specification
- Drop old tools waiting to be fixed on a later date, just keep
fixed png for now
- Simplify other stuff
This is a direct consequence of my slcon2-talk on this topic.
At first I planned to have 64 bits per channel, but this is
overkill.
-rw-r--r-- | LICENSE | 2 | ||||
-rw-r--r-- | Makefile | 40 | ||||
-rw-r--r-- | SPECIFICATION | 32 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | config.mk | 2 | ||||
-rw-r--r-- | ff2png.c (renamed from if2png.c) | 39 | ||||
-rw-r--r-- | gif2if.c | 177 | ||||
-rw-r--r-- | jpg2if.c | 121 | ||||
-rw-r--r-- | png2ff.c (renamed from png2if.c) | 56 |
9 files changed, 86 insertions, 386 deletions
@@ -1,6 +1,6 @@ ISC-License -(c) 2014 Laslo Hunhold <dev@frign.de> +(c) 2014-2015 Laslo Hunhold <dev@frign.de> (c) 2014 sin <sin@2f30.org> (c) 2014 Hiltjo Posthuma <hiltjo@codemadness.org> @@ -1,15 +1,15 @@ -# imagefile - tools to convert between png and if +# imagefile - tools to convert between png and ff # See LICENSE file for copyright and license details include config.mk -SRC = png2if.c if2png.c jpg2if.c gif2if.c +SRC = png2ff.c ff2png.c OBJ = ${SRC:.c=.o} -all: options png2if if2png jpg2if gif2if +all: options png2ff ff2png options: - @echo imagefile build options: + @echo farbfeld build options: @echo "CFLAGS = ${CFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" @@ -20,40 +20,28 @@ options: ${OBJ}: config.mk -gif2if: gif2if.o +png2ff: png2ff.o @echo CC -o $@ - @${CC} -o $@ gif2if.o ${GIF_LIBS} ${LDFLAGS} + @${CC} -o $@ png2ff.o ${PNG_LIBS} ${LDFLAGS} -jpg2if: jpg2if.o +ff2png: ff2png.o @echo CC -o $@ - @${CC} -o $@ jpg2if.o ${JPEG_LIBS} ${LDFLAGS} - -png2if: png2if.o - @echo CC -o $@ - @${CC} -o $@ png2if.o ${PNG_LIBS} ${LDFLAGS} - -if2png: if2png.o - @echo CC -o $@ - @${CC} -o $@ if2png.o ${PNG_LIBS} ${LDFLAGS} + @${CC} -o $@ ff2png.o ${PNG_LIBS} ${LDFLAGS} clean: @echo cleaning - @rm -f png2if if2png jpg2if gif2if ${OBJ} + @rm -f png2ff ff2png ${OBJ} install: all @echo installing executable files to ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f png2if jpg2if if2png gif2if ${DESTDIR}${PREFIX}/bin - @chmod 755 ${DESTDIR}${PREFIX}/bin/png2if - @chmod 755 ${DESTDIR}${PREFIX}/bin/jpg2if - @chmod 755 ${DESTDIR}${PREFIX}/bin/if2png - @chmod 755 ${DESTDIR}${PREFIX}/bin/gif2if + @cp -f png2ff ff2png ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/png2ff + @chmod 755 ${DESTDIR}${PREFIX}/bin/ff2png uninstall: @echo removing executable files from ${DESTDIR}${PREFIX}/bin - @rm -f ${DESTDIR}${PREFIX}/bin/png2if - @rm -f ${DESTDIR}${PREFIX}/bin/jpg2if - @rm -f ${DESTDIR}${PREFIX}/bin/if2png - @rm -f ${DESTDIR}${PREFIX}/bin/gif2if + @rm -f ${DESTDIR}${PREFIX}/bin/png2ff + @rm -f ${DESTDIR}${PREFIX}/bin/ff2png .PHONY: all options clean install uninstall diff --git a/SPECIFICATION b/SPECIFICATION index 2ba71ff..837fe0e 100644 --- a/SPECIFICATION +++ b/SPECIFICATION @@ -1,18 +1,18 @@ -The imagefile-format is meant to be parsed easily +The farbfeld-format is meant to be parsed easily and used to pipe images losslessly. -# WHY IMAGEFILE? +# WHY FARBFELD? Most current image-formats have their compression incorporated in their format itself. This has some advantages, but reaches its limits with lossless formats (e.g. PNG). -The basic idea of the imagefile-format is to separate +The basic idea of the farbfeld-format is to separate these to and having a completely transparent image-format. Pattern resolution is done while compressing, not while converting the image. -For example, imagefile always stores an alpha-channel, +For example, farbfeld always stores an alpha-channel, even if the image doesn't have alpha-variation. This may sound like a big waste, but as soon as you compress an image of this kind, the bzip2-algorithm @@ -20,24 +20,30 @@ takes care of the easy pattern, that each 4th character has the same value. This leads to almost no overhead while keeping parsing really simple. +Same applies to the idea of having 64 bits per channel. +It sounds excessive, but if you only have 8 bits of +entropy, the compression will take care of it, while +you only need to do the endian-handling for 64 bit +chunks. # FORMAT: Bytes Description -9 imagefile -4 32 bit BE Integer (width) -4 32 bit BE Integer (height) -[1111] RGBA-row-aligned-pixel-array +8 farbfeld +4 32 bit BE Unsigned Integer (width) +4 32 bit BE Unsigned Integer (height) +[2222] RGBA-row-aligned-pixel-array + 16 bit BE Unsigned Integers (per channel) # EXAMPLES: encoding: -png2if < example.png > example.if -png2if < example.png | bzip2 > example.if.bz2 +png2ff < example.png > example.ff +png2ff < example.png | bzip2 > example.ff.bz2 decoding: -if2png < example.if > example.png -bzcat example.if.bz2 | if2png > example.png +ff2png < example.ff > example.png +bzcat example.ff.bz2 | ff2png > example.png # WHY BZ2? @@ -47,4 +53,4 @@ For normal pictures, the bz2-compression yields roughly the same sizes as png does. Always keep in mind that using PNG involves having to rely on libpng to decode the image for you, whereas -imagefile is a completely transparent format. +farbfeld is a completely transparent format. @@ -0,0 +1,3 @@ + o handle truecolor PNG's properly (libpng is painful to use) + o write simpler Makefile + o re-add the old imagefile-tools for gif, jpg, ... @@ -16,7 +16,7 @@ CPPFLAGS = #CFLAGS = -std=c99 -pedantic -Wall -Wextra -O0 -g -ggdb ${CPPFLAGS} #LDFLAGS = ${LIBS} # optimized -CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wextra -D_DEFAULT_SOURCE -Os ${CPPFLAGS} LDFLAGS = -s ${LIBS} # compiler and linker @@ -1,23 +1,22 @@ /* See LICENSE file for copyright and license details. */ -#include <arpa/inet.h> -#include <errno.h> +#include <endian.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - #include <png.h> + #include "arg.h" char *argv0; -#define HEADER_FORMAT "imagefile########" +#define HEADER_FORMAT "farbfeld########" static void usage(void) { fprintf(stderr, "usage: %s\n", argv0); - exit(EXIT_FAILURE); + exit(1); } int @@ -26,28 +25,29 @@ main(int argc, char *argv[]) png_structp png_struct_p; png_infop png_info_p; uint8_t hdr[17], *png_row; + uint16_t tmp16; png_uint_32 width, height, i; - png_size_t png_row_len; + png_size_t png_row_len, j; ARGBEGIN { default: usage(); } ARGEND; - if (argc != 0) + if (argc) usage(); /* header */ if (fread(hdr, 1, strlen(HEADER_FORMAT), stdin) != strlen(HEADER_FORMAT)) { fprintf(stderr, "failed to read from stdin or input too short\n"); - return EXIT_FAILURE; + return 1; } - if (memcmp("imagefile", hdr, 9)) { + if (memcmp("farbfeld", hdr, strlen("farbfeld"))) { fprintf(stderr, "invalid magic in header\n"); - return EXIT_FAILURE; + return 1; } - width = ntohl((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | (hdr[12] << 24)); - height = ntohl((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | (hdr[16] << 24)); + width = be32toh((hdr[9] << 0) | (hdr[10] << 8) | (hdr[11] << 16) | (hdr[12] << 24)); + height = be32toh((hdr[13] << 0) | (hdr[14] << 8) | (hdr[15] << 16) | (hdr[16] << 24)); /* load png */ png_struct_p = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); @@ -55,7 +55,7 @@ main(int argc, char *argv[]) if (!png_struct_p || !png_info_p || setjmp(png_jmpbuf(png_struct_p))) { fprintf(stderr, "failed to initialize libpng\n"); - return EXIT_FAILURE; + return 1; } png_init_io(png_struct_p, stdout); png_set_IHDR(png_struct_p, png_info_p, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, @@ -67,13 +67,16 @@ main(int argc, char *argv[]) png_row = malloc(png_row_len); if (!png_row) { fprintf(stderr, "failed to allocate row-buffer\n"); - return EXIT_FAILURE; + return 1; } for (i = 0; i < height; ++i) { - if (fread(png_row, 1, png_row_len, stdin) != png_row_len) { - fprintf(stderr, "unexpected EOF or row-skew at %lu\n", (unsigned long)i); - return EXIT_FAILURE; + for (j = 0; j < png_row_len; ++j) { + if (fread(&tmp16, 1, sizeof(uint16_t), stdin) != sizeof(uint16_t)) { + fprintf(stderr, "unexpected EOF or row-skew\n"); + return 1; + } + png_row[j] = be16toh(tmp16) / (1 << 8); } png_write_row(png_struct_p, png_row); } @@ -83,5 +86,5 @@ main(int argc, char *argv[]) png_free_data(png_struct_p, png_info_p, PNG_FREE_ALL, -1); png_destroy_write_struct(&png_struct_p, NULL); free(png_row); - return EXIT_SUCCESS; + return 0; } diff --git a/gif2if.c b/gif2if.c deleted file mode 100644 index 36d98d6..0000000 --- a/gif2if.c +++ /dev/null @@ -1,177 +0,0 @@ -/* See LICENSE file for copyright and license details. - * code borrowed from util/gif2rgb.c from giflib-5.0 */ -#include <arpa/inet.h> -#include <errno.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <gif_lib.h> -#include "arg.h" - -char *argv0; - -static void -usage(void) -{ - fprintf(stderr, "usage: %s\n", argv0); - exit(EXIT_FAILURE); -} - -static void -die(const char *s) { - fputs(s, stderr); - exit(EXIT_FAILURE); -} - -void * -emalloc(size_t size) { - void *p; - - if(!(p = malloc(size))) - die("can't malloc\n"); - return p; -} - -int -main(int argc, char *argv[]) -{ - GifRecordType recordtype; - GifFileType *giffile; - GifRowType * gifrows; - GifByteType *extension; - GifColorType *colormapentry; - ColorMapObject *colormap; - uint32_t width, height, rwidth, rheight, val_be; - uint32_t i, j, k; - size_t gif_row_len; - uint8_t *if_row; - int extcode, row, col, imagenum = 0; - int interlacedoffset[] = { 0, 4, 2, 1 }; - int interlacedjumps[] = { 8, 8, 4, 2 }; - - ARGBEGIN { - default: - usage(); - } ARGEND; - - if (argc != 0) - usage(); - - /* load gif */ -#if GIFLIB_MAJOR >= 5 - if ((giffile = DGifOpenFileHandle(0, NULL)) == NULL) - die("gif error\n"); -#else - if ((giffile = DGifOpenFileHandle(0)) == NULL) - die("gif error\n"); -#endif - - width = (uint32_t)giffile->SWidth; - height = (uint32_t)giffile->SHeight; - gif_row_len = giffile->SWidth * sizeof(GifPixelType); - gifrows = emalloc(giffile->SHeight * sizeof(GifRowType)); - gifrows[0] = emalloc(gif_row_len); - - /* set color to BackGround. */ - for (i = 0; i < width; i++) - gifrows[0][i] = giffile->SBackGroundColor; - for(i = 1; i < height; i++) { - gifrows[i] = emalloc(gif_row_len); - memcpy(gifrows[i], gifrows[0], gif_row_len); - } - - /* scan the content of the GIF file and load the image(s) in: */ - do { - if (DGifGetRecordType(giffile, &recordtype) == GIF_ERROR) - die("gif error\n"); - - switch (recordtype) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(giffile) == GIF_ERROR) - die("gif error\n"); - - /* image position relative to Screen. */ - row = giffile->Image.Top; - col = giffile->Image.Left; - rwidth = giffile->Image.Width; - rheight = giffile->Image.Height; - if (giffile->Image.Left + giffile->Image.Width > giffile->SWidth || - giffile->Image.Top + giffile->Image.Height > giffile->SHeight) { - fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n", imagenum); - exit(EXIT_FAILURE); - } - if (giffile->Image.Interlace) { - /* need to perform 4 passes on the images: */ - for (i = 0; i < 4; i++) { - for (j = row + interlacedoffset[i]; j < row + rheight; j += interlacedjumps[i]) { - if (DGifGetLine(giffile, &gifrows[j][col], rwidth) == GIF_ERROR) - die("gif error\n"); - } - } - } else { - for (i = 0; i < rheight; i++) { - if (DGifGetLine(giffile, &gifrows[row++][col], rwidth) == GIF_ERROR) - die("gif error\n"); - } - } - /* this is set to disallow multiple images */ - recordtype = TERMINATE_RECORD_TYPE; - imagenum++; - break; - case EXTENSION_RECORD_TYPE: - /* Skip any extension blocks in file: */ - if (DGifGetExtension(giffile, &extcode, &extension) == GIF_ERROR) - die("gif error\n"); - while (extension != NULL) { - if (DGifGetExtensionNext(giffile, &extension) == GIF_ERROR) - die("gif error\n"); - } - break; - case TERMINATE_RECORD_TYPE: - break; - default: - break; - } - } while (recordtype != TERMINATE_RECORD_TYPE); - - if(giffile->Image.ColorMap) - colormap = giffile->Image.ColorMap; - else - colormap = giffile->SColorMap; - - if(colormap == NULL) - die("Gif Image does not have a colormap\n"); - - /* write header with big endian width and height-values */ - fprintf(stdout, "imagefile"); - val_be = htonl(width); - fwrite(&val_be, sizeof(uint32_t), 1, stdout); - val_be = htonl(height); - fwrite(&val_be, sizeof(uint32_t), 1, stdout); - - gif_row_len = width * strlen("RGBA"); - if_row = emalloc(gif_row_len); - - /* write data */ - for (i = 0; i < height; i++) { - for(j = 0, k = 0; j < width; j++, k += 4) { - colormapentry = &colormap->Colors[gifrows[i][j]]; - if_row[k] = colormapentry->Red; - if_row[k+1] = colormapentry->Green; - if_row[k+2] = colormapentry->Blue; - if_row[k+3] = 255; /* TODO: mask? */ - } - if (fwrite(if_row, 1, gif_row_len, stdout) != gif_row_len) - die("fwrite() failed\n"); - } - - /* cleanup */ - for(i = 0; i < height; i++) - free(gifrows[i]); - free(gifrows); - free(if_row); - - return EXIT_SUCCESS; -} diff --git a/jpg2if.c b/jpg2if.c deleted file mode 100644 index 514c449..0000000 --- a/jpg2if.c +++ /dev/null @@ -1,121 +0,0 @@ -/* 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 <setjmp.h> -#include <jpeglib.h> -#include "arg.h" - -char *argv0; - -static jmp_buf setjmp_buffer; - -static void -usage(void) -{ - fprintf(stderr, "usage: %s\n", argv0); - exit(EXIT_FAILURE); -} - -METHODDEF(void) -if_jpeg_error(j_common_ptr cinfo) -{ - /* Always display the message. */ - /* We could postpone this until after returning, if we chose. */ - (*cinfo->err->output_message) (cinfo); - - /* Return control to the setjmp point */ - longjmp(setjmp_buffer, 1); -} - -int -main(int argc, char *argv[]) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - uint32_t width, height, val_be; - uint8_t *if_row = NULL; - size_t jpeg_row_len, if_row_len, i, dx, sx; - int status = EXIT_FAILURE; - JSAMPARRAY buffer; /* output row buffer */ - - ARGBEGIN { - default: - usage(); - } ARGEND; - - if (argc != 0) - usage(); - - /* load jpeg */ - cinfo.err = jpeg_std_error(&jerr); - - jerr.error_exit = if_jpeg_error; - /* Establish the setjmp return context for my_error_exit to use. */ - if (setjmp(setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. - * We need to clean up the JPEG object, close the input file, and return. */ - goto cleanup; - } - - jpeg_create_decompress(&cinfo); - jpeg_stdio_src(&cinfo, stdin); - - jpeg_read_header(&cinfo, TRUE); - width = cinfo.image_width; - height = cinfo.image_height; - - /* change output for imagefile */ - cinfo.output_components = 3; /* # of color components per pixel */ - cinfo.out_color_space = JCS_RGB; /* colorspace of input image */ - - jpeg_start_decompress(&cinfo); - jpeg_row_len = width * cinfo.output_components; - - /* Make a one-row-high sample array that will go away when done with image */ - buffer = (*cinfo.mem->alloc_sarray) - ((j_common_ptr) &cinfo, JPOOL_IMAGE, jpeg_row_len, 1); - if_row_len = strlen("RGBA") * width; - if(!(if_row = malloc(if_row_len))) { - fprintf(stderr, "Can't malloc\n"); - return EXIT_FAILURE; - } - - /* write header with big endian width and height-values */ - fprintf(stdout, "imagefile"); - val_be = htonl(width); - fwrite(&val_be, sizeof(uint32_t), 1, stdout); - val_be = htonl(height); - fwrite(&val_be, sizeof(uint32_t), 1, stdout); - - while (cinfo.output_scanline < cinfo.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. */ - (void)jpeg_read_scanlines(&cinfo, buffer, 1); - - for(i = 0, dx = 0, sx = 0; i < width; i++, sx += 3, dx += 4) { - if_row[dx] = buffer[0][sx]; - if_row[dx+1] = buffer[0][sx+1]; - if_row[dx+2] = buffer[0][sx+2]; - if_row[dx+3] = 255; - } - /* write data */ - if (fwrite(if_row, 1, if_row_len, stdout) != if_row_len) { - fprintf(stderr, "fwrite() failed\n"); - goto cleanup; - } - } - jpeg_finish_decompress(&cinfo); - status = EXIT_SUCCESS; - -cleanup: - free(if_row); - jpeg_destroy_decompress(&cinfo); - - return status; -} @@ -1,12 +1,10 @@ /* See LICENSE file for copyright and license details. */ -#include <arpa/inet.h> -#include <errno.h> +#include <endian.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> - #include <png.h> + #include "arg.h" char *argv0; @@ -14,8 +12,8 @@ char *argv0; static void usage(void) { - fprintf(stderr, "usage: %s\n", argv0); - exit(EXIT_FAILURE); + fprintf(stderr, "usage:%s\n", argv0); + exit(1); } int @@ -24,54 +22,54 @@ main(int argc, char *argv[]) png_structp png_struct_p; png_infop png_info_p; png_bytepp png_row_p; - png_uint_32 width, height, i; - png_size_t png_row_len; - uint32_t val_be; int depth, color, interlace; + uint32_t width, height, png_row_len, tmp32, r, i; + uint16_t tmp16; ARGBEGIN { default: usage(); - } ARGEND; + } ARGEND - if (argc != 0) + if (argc) usage(); /* load png */ - png_struct_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_struct_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); png_info_p = png_create_info_struct(png_struct_p); if (!png_struct_p || !png_info_p || setjmp(png_jmpbuf(png_struct_p))) { - fprintf(stderr, "failed to initialize libpng"); - return EXIT_FAILURE; + fprintf(stderr, "failed to initialize libpng\n"); + return 1; } png_init_io(png_struct_p, stdin); png_set_add_alpha(png_struct_p, 255, PNG_FILLER_AFTER); png_set_gray_to_rgb(png_struct_p); png_read_png(png_struct_p, png_info_p, PNG_TRANSFORM_STRIP_16 | - PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND , NULL); + PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL); png_get_IHDR(png_struct_p, png_info_p, &width, &height, &depth, - &color, &interlace, NULL, NULL); + &color, &interlace, NULL, NULL); png_row_len = png_get_rowbytes(png_struct_p, png_info_p); png_row_p = png_get_rows(png_struct_p, png_info_p); - /* write header with big endian width and height-values */ - fprintf(stdout, "imagefile"); - val_be = htonl(width); - fwrite(&val_be, sizeof(uint32_t), 1, stdout); - val_be = htonl(height); - fwrite(&val_be, sizeof(uint32_t), 1, stdout); + /* write header */ + fprintf(stdout, "farbfeld"); + tmp32 = htobe32(width); + fwrite(&tmp32, sizeof(uint32_t), 1, stdout); + tmp32 = htobe32(height); + fwrite(&tmp32, sizeof(uint32_t), 1, stdout); /* write data */ - for (i = 0; i < height; i++) { - if (fwrite(png_row_p[i], 1, png_row_len, stdout) != png_row_len) { - fprintf(stderr, "fwrite() failed\n"); - return EXIT_FAILURE; + /* TODO: allow 16 bit PNGs to be converted losslessly */ + for (r = 0; r < height; ++r) { + for (i = 0; i < png_row_len; i++) { + tmp16 = htobe16((uint16_t)png_row_p[r][i]); + fwrite(&tmp16, sizeof(uint16_t), 1, stdout); } } - /* clean up */ - png_free_data(png_struct_p, png_info_p, PNG_FREE_ALL, -1); + /* cleanup */ png_destroy_read_struct(&png_struct_p, &png_info_p, NULL); - return EXIT_SUCCESS; + return 0; } |