Back to the roots. - 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 <[email protected]> | |
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: | |
M FORMAT | 27 ++++++++++++--------------- | |
M config.mk | 7 ++----- | |
M farbfeld.5 | 3 ++- | |
M ff2png.c | 51 ++---------------------------… | |
M jpg2ff.c | 69 ++---------------------------… | |
M 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; | |
} |