farbfeld

suckless image format with conversion tools
git clone git://git.suckless.org/farbfeld
Log | Files | Refs | README | LICENSE

commit dd22f4087d5e7420929575d1bbaf767d76395e9c
parent e9feca5c2bda05b9a356617868fd4b08ec903d0d
Author: FRIGN <dev@frign.de>
Date:   Fri, 29 Jan 2016 23:34:24 +0100

Back to the roots.

Talking with even more people and weighing the benefits, I've made the
decision to remove color spaces from farbfeld again (as it currently
is handled in version 1) and recommend sRGB, not force it.
Thing is the following: We are not ready yet for this step. Neither the
people working with the images nor the hardware to display extended
colors. Using greater colorspaces also removes the "hackability" of
farbfeld.
As it was previously possible to easily create gradients in plain C,
you have to keep multiple things in mind with linear ProPhoto RGB.
The first thing is that not all colors are "real", and there are
imaginary colors. This doesn't happen with sRGB because all the colors
it describes are "real".
The second thing is the linear gamma curve, which makes the gradients
look perceptually inconsistent.

In the interest of creating a good and simple interchange format, we
can't only weigh in points of professionals. This little excursion has
shown that aiming for the 99% is still the way to go.
And as often as you could repeat that sRGB will meet its fate in the
future when screens become better, it is surprising how badly the
industry has caught up to it.
I also must honestly say that we can't solve the color space issue
using RGB and should look at other formats to explore (CIELUV, CIELAB),
which I will probably give a talk about at slcon3.

Before releasing version 2, my aim is to make the I/O a bit more
effective (as far as possible) so it's even easier to use.

I know people will be pissed off, but fuck it, I'm the maintainer!
Live with it.

Diffstat:
FORMAT | 27++++++++++++---------------
config.mk | 7++-----
farbfeld.5 | 3++-
ff2png.c | 51++-------------------------------------------------
jpg2ff.c | 69++++-----------------------------------------------------------------
png2ff.c | 77+++++++++--------------------------------------------------------------------
6 files changed, 31 insertions(+), 203 deletions(-)

diff --git a/FORMAT b/FORMAT @@ -1,18 +1,15 @@ 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 | - | | - pixels in rows | - | | - linear ROMM RGB (ISO 22028-2:2013) | - | | (= linear ProPhoto RGB = Melissa RGB) | - | | - no alpha premultiplication | - +--------+-----------------------------------------------+ + +--------+----------------------------------------------+ + | 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 | + | | pixels in rows, not alpha-premultiplied | + +--------+----------------------------------------------+ diff --git a/config.mk b/config.mk @@ -13,11 +13,8 @@ PNGINC = /usr/local/include JPGLIB = /usr/local/lib JPGINC = /usr/local/include -LCMSLIB = /usr/local/lib -LCMSINC = /usr/local/include - -INCS = -I${PNGINC} -I${JPGINC} -I${LCMSINC} -LIBS = -L${PNGLIB} -L${JPGLIB} -L${LCMSLIB} -lpng -ljpeg -llcms2 +INCS = -I${PNGINC} -I${JPGINC} +LIBS = -L${PNGLIB} -L${JPGLIB} -lpng -ljpeg # flags CPPFLAGS = -D_DEFAULT_SOURCE diff --git a/farbfeld.5 b/farbfeld.5 @@ -16,8 +16,9 @@ BYTES DESCRIPTION 4 32-Bit BE unsigned integer (width) 4 32-Bit BE unsigned integer (height) [2222] 4*16-Bit BE unsigned integers [RGBA] / pixel - pixels in rows, ProPhoto RGB, not alpha-premultiplied + pixels in rows, not alpha-premultiplied .Ed +The RGB-data should be in sRGB. .Sh USAGE .Nm provides diff --git a/ff2png.c b/ff2png.c @@ -7,21 +7,12 @@ #include <stdlib.h> #include <string.h> -#include <lcms2.h> #include <png.h> #define HEADER "farbfeld########" static char *argv0; -/* ROMM RGB primaries (ISO 22028-2:2013) */ -static cmsCIExyYTRIPLE primaries = { - /* x, y, Y */ - { 0.7347, 0.2653, 0.288040 }, /* red */ - { 0.1596, 0.8404, 0.711874 }, /* green */ - { 0.0366, 0.0001, 0.000086 }, /* blue */ -}; - void pngerr(png_structp pngs, const char *msg) { @@ -32,16 +23,12 @@ pngerr(png_structp pngs, const char *msg) int main(int argc, char *argv[]) { - cmsContext icc_context; - cmsHPROFILE out_prof; - cmsMLU *mlu1, *mlu2, *mlu3; - cmsToneCurve *gamma10, *out_curve[3]; png_structp pngs; png_infop pngi; size_t png_row_len, j; - uint32_t icclen, width, height, i; + uint32_t width, height, i; uint16_t tmp16, *png_row; - uint8_t hdr[16], *icc; + uint8_t hdr[16]; argv0 = argv[0], argc--, argv++; @@ -62,34 +49,6 @@ main(int argc, char *argv[]) width = ntohl(*((uint32_t *)(hdr + 8))); height = ntohl(*((uint32_t *)(hdr + 12))); - /* icc profile (linear ROMM RGB (ISO 22028-2:2013)) */ - if (!(icc_context = cmsCreateContext(NULL, NULL))) - goto lcmserr; - if (!(gamma10 = cmsBuildGamma(icc_context, 1.0))) - goto lcmserr; - out_curve[0] = out_curve[1] = out_curve[2] = gamma10; - if (!(out_prof = cmsCreateRGBProfileTHR(icc_context, cmsD50_xyY(), - &primaries, out_curve))) - goto lcmserr; - cmsSetHeaderFlags(out_prof, cmsEmbeddedProfileTrue | cmsUseAnywhere); - cmsSetHeaderRenderingIntent(out_prof, INTENT_RELATIVE_COLORIMETRIC); - cmsSetDeviceClass(out_prof, cmsSigColorSpaceClass); - if (!(mlu1 = cmsMLUalloc(NULL, 1)) || !(mlu2 = cmsMLUalloc(NULL, 1)) || - !(mlu3 = cmsMLUalloc(NULL, 1))) - goto lcmserr; - cmsMLUsetASCII(mlu1, "en", "US", "Public Domain"); - cmsWriteTag(out_prof, cmsSigCopyrightTag, mlu1); - cmsMLUsetASCII(mlu2, "en", "US", "aka Linear ProPhoto RGB, Melissa RGB"); - cmsWriteTag(out_prof, cmsSigDeviceModelDescTag, mlu2); - cmsMLUsetASCII(mlu3, "en", "US", "Linear ROMM RGB (ISO 22028-2:2013)"); - cmsWriteTag(out_prof, cmsSigProfileDescriptionTag, mlu3); - cmsSaveProfileToMem(out_prof, NULL, &icclen); - if (!(icc = malloc(icclen))) { - fprintf(stderr, "%s: malloc: out of memory\n", argv0); - return 1; - } - cmsSaveProfileToMem(out_prof, icc, &icclen); - /* load png */ pngs = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, pngerr, NULL); @@ -103,8 +62,6 @@ main(int argc, char *argv[]) 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_set_iCCP(pngs, pngi, "Linear ROMM RGB (ISO 22028-2:2013)", 0, - icc, icclen); png_write_info(pngs, pngi); /* write rows */ @@ -127,8 +84,4 @@ main(int argc, char *argv[]) png_destroy_write_struct(&pngs, NULL); return 0; -lcmserr: - fprintf(stderr, "%s: lcms error\n", argv0); - - return 1; } diff --git a/jpg2ff.c b/jpg2ff.c @@ -8,18 +8,9 @@ #include <string.h> #include <jpeglib.h> -#include <lcms2.h> static char *argv0; -/* ROMM RGB primaries (ISO 22028-2:2013) */ -static cmsCIExyYTRIPLE primaries = { - /* x, y, Y */ - { 0.7347, 0.2653, 0.288040 }, /* red */ - { 0.1596, 0.8404, 0.711874 }, /* green */ - { 0.0366, 0.0001, 0.000086 }, /* blue */ -}; - METHODDEF(void) jpeg_error(j_common_ptr cinfo) { @@ -31,11 +22,7 @@ jpeg_error(j_common_ptr cinfo) int main(int argc, char *argv[]) { - cmsHPROFILE in_profile = NULL, out_profile; - cmsHTRANSFORM transform; - cmsToneCurve *gamma10, *out_curves[3]; struct jpeg_decompress_struct cinfo; - jpeg_saved_marker_ptr marker; struct jpeg_error_mgr jerr; uint32_t width, height, val_be; uint16_t *ff_row; @@ -55,9 +42,6 @@ main(int argc, char *argv[]) jpeg_create_decompress(&cinfo); - jpeg_save_markers(&cinfo, JPEG_APP0 + 1, 0xffff); /* exif data */ - jpeg_save_markers(&cinfo, JPEG_APP0 + 2, 0xffff); /* icc data */ - jpeg_stdio_src(&cinfo, stdin); jpeg_read_header(&cinfo, TRUE); @@ -68,23 +52,6 @@ main(int argc, char *argv[]) cinfo.output_components = 3; /* color components per pixel */ cinfo.out_color_space = JCS_RGB; /* input color space */ - /* extract metadata */ - marker = cinfo.marker_list; - for(; marker; marker = marker->next) { - if (!marker->data || !marker->data_length) - continue; - if (marker->marker == JPEG_APP0 + 1) { - /* exif data marker */ - /* todo: Should we handle icc data from exif? */ - } else if (marker->marker == JPEG_APP0 + 2) { - /* icc data marker */ - if (!(in_profile = cmsOpenProfileFromMem( - marker->data + 14, - marker->data_length - 14))) - goto lcmserr; - } - } - jpeg_start_decompress(&cinfo); jpeg_row_len = width * cinfo.output_components; @@ -97,20 +64,6 @@ main(int argc, char *argv[]) return 1; } - /* icc profile (output linear ROMM RGB (ISO 22028-2:2013)) */ - if (!in_profile && !(in_profile = cmsCreate_sRGBProfile())) - goto lcmserr; - if (!(gamma10 = cmsBuildGamma(NULL, 1.0))) - goto lcmserr; - out_curves[0] = out_curves[1] = out_curves[2] = gamma10; - if (!(out_profile = cmsCreateRGBProfile(cmsD50_xyY(), &primaries, - out_curves))) - goto lcmserr; - if (!(transform = cmsCreateTransform(in_profile, TYPE_RGBA_16, - out_profile, TYPE_RGBA_16, - INTENT_RELATIVE_COLORIMETRIC, 0))) - goto lcmserr; - /* write header */ fprintf(stdout, "farbfeld"); val_be = htonl(width); @@ -127,20 +80,10 @@ main(int argc, char *argv[]) jpeg_read_scanlines(&cinfo, buffer, 1); for (i = 0, dx = 0, sx = 0; i < width; i++, sx += 3, dx += 4) { - ff_row[dx] = buffer[0][sx] * 257; - ff_row[dx+1] = buffer[0][sx+1] * 257; - ff_row[dx+2] = buffer[0][sx+2] * 257; - ff_row[dx+3] = 65535; - } - - cmsDoTransform(transform, ff_row, ff_row, ff_row_len / 4); - - for (i = 0; i < ff_row_len; i++) { - /* re-add alpha */ - if (i >= 3 && (i - 3) % 4 == 0) - ff_row[i] = 65535; - /* swap endianness to BE */ - ff_row[i] = htons(ff_row[i]); + ff_row[dx] = htons(buffer[0][sx] * 257); + ff_row[dx+1] = htons(buffer[0][sx+1] * 257); + ff_row[dx+2] = htons(buffer[0][sx+2] * 257); + ff_row[dx+3] = htons(65535); } /* write data */ @@ -156,8 +99,4 @@ writerr: perror(NULL); return 1; -lcmserr: - fprintf(stderr, "%s: lcms error\n", argv0); - - return 1; } diff --git a/png2ff.c b/png2ff.c @@ -7,19 +7,10 @@ #include <stdlib.h> #include <string.h> -#include <lcms2.h> #include <png.h> static char *argv0; -/* ROMM RGB primaries (ISO 22028-2:2013) */ -static cmsCIExyYTRIPLE primaries = { - /* x, y, Y */ - { 0.7347, 0.2653, 0.288040 }, /* red */ - { 0.1596, 0.8404, 0.711874 }, /* green */ - { 0.0366, 0.0001, 0.000086 }, /* blue */ -}; - void pngerr(png_structp pngs, const char *msg) { @@ -30,16 +21,11 @@ pngerr(png_structp pngs, const char *msg) int main(int argc, char *argv[]) { - cmsHPROFILE in_prof, out_prof; - cmsHTRANSFORM trans; - cmsToneCurve *gamma10, *out_curves[3]; png_structp pngs; png_infop pngi; - int icc_compression; - uint32_t width, height, icc_len, outrowlen, tmp32, r, i; - uint16_t *inrow, *outrow; - uint8_t **png_row_p, *icc_data; - char *icc_name; + uint32_t width, height, outrowlen, tmp32, r, i; + uint16_t *outrow; + uint8_t **png_row_p; argv0 = argv[0], argc--, argv++; @@ -70,24 +56,6 @@ main(int argc, char *argv[]) height = png_get_image_height(pngs, pngi); png_row_p = png_get_rows(pngs, pngi); - /* icc profile (output linear ROMM RGB (ISO 22028-2:2013)) */ - if (png_get_valid(pngs, pngi, PNG_INFO_iCCP)) { - png_get_iCCP(pngs, pngi, &icc_name, - &icc_compression, &icc_data, &icc_len); - if (!(in_prof = cmsOpenProfileFromMem(icc_data, - icc_len))) - goto lcmserr; - } else { - if (!(in_prof = cmsCreate_sRGBProfile())) - goto lcmserr; - } - if (!(gamma10 = cmsBuildGamma(NULL, 1.0))) - goto lcmserr; - out_curves[0] = out_curves[1] = out_curves[2] = gamma10; - if (!(out_prof = cmsCreateRGBProfile(cmsD50_xyY(), &primaries, - out_curves))) - goto lcmserr; - /* allocate output row buffer */ outrowlen = width * strlen("RGBA"); if (!(outrow = malloc(outrowlen * sizeof(uint16_t)))) { @@ -107,48 +75,25 @@ main(int argc, char *argv[]) /* write data */ switch(png_get_bit_depth(pngs, pngi)) { case 8: - if (!(trans = cmsCreateTransform(in_prof, TYPE_RGBA_8, - out_prof, TYPE_RGBA_16, - INTENT_RELATIVE_COLORIMETRIC, - 0))) - goto lcmserr; for (r = 0; r < height; ++r) { - cmsDoTransform(trans, png_row_p[r], outrow, width); for (i = 0; i < outrowlen; i++) { - /* re-add alpha */ - if (i >= 3 && (i - 3) % 4 == 0) - outrow[i] = 257 * png_row_p[r][i]; - /* swap endiannes to BE */ - outrow[i] = htons(outrow[i]); + outrow[i] = htons(257 * png_row_p[r][i]); } if (fwrite(outrow, sizeof(uint16_t), outrowlen, - stdout) != outrowlen) + stdout) != outrowlen) { goto writerr; + } } break; case 16: - if (!(trans = cmsCreateTransform(in_prof, TYPE_RGBA_16, - out_prof, TYPE_RGBA_16, - INTENT_RELATIVE_COLORIMETRIC, - 0))) - goto lcmserr; for (r = 0; r < height; ++r) { - inrow = (uint16_t *)png_row_p[r]; - for (i = 0; i < outrowlen; i++) { - /* swap endianness to LE */ - inrow[i] = ntohs(inrow[i]); - } - cmsDoTransform(trans, png_row_p[r], outrow, width); for (i = 0; i < outrowlen; ++i) { - /* re-add alpha */ - if (i >= 3 && (i - 3) % 4 == 0) - outrow[i] = inrow[i]; - /* swap endianness to BE */ - outrow[i] = htons(outrow[i]); + outrow[i] = ((uint16_t *)png_row_p[r])[i]; } if (fwrite(outrow, sizeof(uint16_t), outrowlen, - stdout) != outrowlen) + stdout) != outrowlen) { goto writerr; + } } break; default: @@ -164,8 +109,4 @@ writerr: perror(NULL); return 1; -lcmserr: - fprintf(stderr, "%s: lcms error\n", argv0); - - return 1; }