summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FORMAT23
-rw-r--r--config.mk7
-rw-r--r--farbfeld.56
-rw-r--r--ff2png.c47
-rw-r--r--jpg2ff.c58
-rw-r--r--png2ff.c96
6 files changed, 195 insertions, 42 deletions
diff --git a/FORMAT b/FORMAT
index 5966df8..6821ab2 100644
--- a/FORMAT
+++ b/FORMAT
@@ -1,14 +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, row-aligned |
- +--------+-----------------------------------------------------------+
+ +--------+-------------------------------------------------------+
+ | 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, ProPhoto RGB, not alpha-premultiplied |
+ +--------+-------------------------------------------------------+
diff --git a/config.mk b/config.mk
index 37ad05f..55264b0 100644
--- a/config.mk
+++ b/config.mk
@@ -13,8 +13,11 @@ PNGINC = /usr/local/include
JPGLIB = /usr/local/lib
JPGINC = /usr/local/include
-INCS = -I${PNGINC} -I${JPGINC}
-LIBS = -L${PNGLIB} -L${JPGLIB} -lpng -ljpeg
+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
# flags
CPPFLAGS = -D_DEFAULT_SOURCE
diff --git a/farbfeld.5 b/farbfeld.5
index 996c532..92cdec0 100644
--- a/farbfeld.5
+++ b/farbfeld.5
@@ -15,11 +15,9 @@ 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-aligned
+[2222] 4*16-Bit BE unsigned integers [RGBA] / pixel
+ pixels in rows, ProPhoto RGB, not alpha-premultiplied
.Ed
-.sp
-The RGB-data should be sRGB for best interoperability and not
-alpha-premultiplied.
.Sh USAGE
.Nm
provides
diff --git a/ff2png.c b/ff2png.c
index a1276d7..94a6b95 100644
--- a/ff2png.c
+++ b/ff2png.c
@@ -7,12 +7,21 @@
#include <stdlib.h>
#include <string.h>
+#include <lcms2.h>
#include <png.h>
#define HEADER "farbfeld########"
static char *argv0;
+/* ProPhoto RGB */
+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 png_struct_p, png_const_charp msg)
{
@@ -23,12 +32,17 @@ pngerr(png_structp png_struct_p, png_const_charp msg)
int
main(int argc, char *argv[])
{
+ cmsContext icc_context;
+ cmsHPROFILE out_profile;
+ cmsMLU *mlu1, *mlu2;
+ cmsToneCurve *gamma18, *out_curves[3];
png_structp png_struct_p;
png_infop png_info_p;
png_size_t png_row_len, j;
png_uint_32 width, height, i;
+ uint32_t icclen;
uint16_t tmp16, *png_row;
- uint8_t hdr[16];
+ uint8_t hdr[16], *icc;
argv0 = argv[0], argc--, argv++;
@@ -49,6 +63,32 @@ main(int argc, char *argv[])
width = ntohl(*((uint32_t *)(hdr + 8)));
height = ntohl(*((uint32_t *)(hdr + 12)));
+ /* icc profile (ProPhoto RGB) */
+ if (!(icc_context = cmsCreateContext(NULL, NULL)))
+ goto lcmserr;
+ if (!(gamma18 = cmsBuildGamma(icc_context, 1.8)))
+ goto lcmserr;
+ out_curves[0] = out_curves[1] = out_curves[2] = gamma18;
+ if (!(out_profile = cmsCreateRGBProfileTHR(icc_context, cmsD50_xyY(),
+ &primaries, out_curves)))
+ goto lcmserr;
+ cmsSetHeaderFlags(out_profile, cmsEmbeddedProfileTrue | cmsUseAnywhere);
+ cmsSetHeaderRenderingIntent(out_profile, INTENT_RELATIVE_COLORIMETRIC);
+ cmsSetDeviceClass(out_profile, cmsSigColorSpaceClass);
+ if (!(mlu1 = cmsMLUalloc(NULL, 1)) || !(mlu2 = cmsMLUalloc(NULL, 1)))
+ goto lcmserr;
+ cmsMLUsetASCII(mlu1, "en", "US", "Public Domain");
+ cmsWriteTag(out_profile, cmsSigCopyrightTag, mlu1);
+ cmsMLUsetASCII(mlu2, "en", "US", "ProPhoto RGB");
+ cmsWriteTag(out_profile, cmsSigProfileDescriptionTag, mlu2);
+ cmsWriteTag(out_profile, cmsSigDeviceModelDescTag, mlu2);
+ cmsSaveProfileToMem(out_profile, NULL, &icclen);
+ if (!(icc = malloc(icclen))) {
+ fprintf(stderr, "%s: malloc: out of memory\n", argv0);
+ return 1;
+ }
+ cmsSaveProfileToMem(out_profile, icc, &icclen);
+
/* load png */
png_struct_p = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
pngerr, NULL);
@@ -62,6 +102,7 @@ main(int argc, char *argv[])
png_set_IHDR(png_struct_p, png_info_p, width, height, 16,
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ png_set_iCCP(png_struct_p, png_info_p, "ProPhoto RGB", 0, icc, icclen);
png_write_info(png_struct_p, png_info_p);
/* write rows */
@@ -84,4 +125,8 @@ main(int argc, char *argv[])
png_destroy_write_struct(&png_struct_p, NULL);
return 0;
+lcmserr:
+ fprintf(stderr, "%s: lcms error\n", argv0);
+
+ return 1;
}
diff --git a/jpg2ff.c b/jpg2ff.c
index eaed74b..aec6dd0 100644
--- a/jpg2ff.c
+++ b/jpg2ff.c
@@ -8,9 +8,19 @@
#include <string.h>
#include <jpeglib.h>
+#include <lcms2.h>
static char *argv0;
+/* ProPhoto RGB */
+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)
{
@@ -22,6 +32,9 @@ jpeg_error(j_common_ptr cinfo)
int
main(int argc, char *argv[])
{
+ cmsHPROFILE in_profile, out_profile;
+ cmsHTRANSFORM transform;
+ cmsToneCurve *gamma18, *out_curves[3];
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
uint32_t width, height, val_be;
@@ -57,12 +70,26 @@ main(int argc, char *argv[])
/* create output buffers */
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo,
JPOOL_IMAGE, jpeg_row_len, 1);
- ff_row_len = strlen("RGBA") * sizeof(uint16_t) * width;
- if(!(ff_row = malloc(ff_row_len))) {
+ ff_row_len = strlen("RGBA") * width;
+ if(!(ff_row = malloc(ff_row_len * sizeof(uint16_t)))) {
fprintf(stderr, "%s: malloc: out of memory\n", argv0);
return 1;
}
+ /* icc profile (output ProPhoto RGB) */
+ if (!(in_profile = cmsCreate_sRGBProfile()))
+ goto lcmserr;
+ if (!(gamma18 = cmsBuildGamma(NULL, 1.8)))
+ goto lcmserr;
+ out_curves[0] = out_curves[1] = out_curves[2] = gamma18;
+ 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);
@@ -78,15 +105,25 @@ main(int argc, char *argv[])
* more than one scanline at a time if that's more convenient. */
jpeg_read_scanlines(&cinfo, buffer, 1);
- for(i = 0, dx = 0, sx = 0; i < width; i++, sx += 3, dx += 4) {
- 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);
+ 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);
+
+ 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]);
}
/* write data */
- if (fwrite(ff_row, 1, ff_row_len, stdout) != ff_row_len)
+ if (fwrite(ff_row, 2, ff_row_len, stdout) != ff_row_len)
goto writerr;
}
jpeg_finish_decompress(&cinfo);
@@ -96,5 +133,10 @@ main(int argc, char *argv[])
writerr:
fprintf(stderr, "%s: fwrite: ", argv0);
perror(NULL);
+
+ return 1;
+lcmserr:
+ fprintf(stderr, "%s: lcms error\n", argv0);
+
return 1;
}
diff --git a/png2ff.c b/png2ff.c
index 4963e86..aea7a50 100644
--- a/png2ff.c
+++ b/png2ff.c
@@ -5,11 +5,21 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <lcms2.h>
#include <png.h>
static char *argv0;
+/* ProPhoto RGB */
+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 png_struct_p, png_const_charp msg)
{
@@ -20,11 +30,16 @@ pngerr(png_structp png_struct_p, png_const_charp msg)
int
main(int argc, char *argv[])
{
+ cmsHPROFILE in_profile, out_profile;
+ cmsHTRANSFORM transform;
+ cmsToneCurve *gamma18, *out_curves[3];
png_structp png_struct_p;
png_infop png_info_p;
- png_bytepp png_row_p;
- uint32_t width, height, png_row_len, tmp32, r, i;
- uint16_t tmp16;
+ png_bytepp png_row_p, icc_data = NULL;
+ png_charpp icc_name = NULL;
+ uint32_t width, height, icc_len, outrowlen, tmp32, r, i;
+ uint16_t *inrow, *outrow;
+ int icc_compression;
argv0 = argv[0], argc--, argv++;
@@ -53,9 +68,33 @@ main(int argc, char *argv[])
PNG_TRANSFORM_EXPAND, NULL);
width = png_get_image_width(png_struct_p, png_info_p);
height = png_get_image_height(png_struct_p, png_info_p);
- png_row_len = png_get_rowbytes(png_struct_p, png_info_p);
png_row_p = png_get_rows(png_struct_p, png_info_p);
+ /* icc profile (output ProPhoto RGB) */
+ if (png_get_valid(png_struct_p, png_info_p, PNG_INFO_iCCP)) {
+ png_get_iCCP(png_struct_p, png_info_p, icc_name,
+ &icc_compression, icc_data, &icc_len);
+ if (!(in_profile = cmsOpenProfileFromMem(icc_data,
+ icc_len)))
+ goto lcmserr;
+ } else {
+ if (!(in_profile = cmsCreate_sRGBProfile()))
+ goto lcmserr;
+ }
+ if (!(gamma18 = cmsBuildGamma(NULL, 1.8)))
+ goto lcmserr;
+ out_curves[0] = out_curves[1] = out_curves[2] = gamma18;
+ if (!(out_profile = cmsCreateRGBProfile(cmsD50_xyY(), &primaries,
+ out_curves)))
+ goto lcmserr;
+
+ /* allocate row buffer */
+ outrowlen = width * strlen("RGBA");
+ if (!(outrow = malloc(outrowlen * sizeof(uint16_t)))) {
+ fprintf(stderr, "%s: malloc: out of memory\n", argv0);
+ return 1;
+ }
+
/* write header */
fputs("farbfeld", stdout);
tmp32 = htonl(width);
@@ -68,25 +107,46 @@ main(int argc, char *argv[])
/* write data */
switch(png_get_bit_depth(png_struct_p, png_info_p)) {
case 8:
+ if (!(transform = cmsCreateTransform(in_profile,
+ TYPE_RGBA_8, out_profile, TYPE_RGBA_16,
+ INTENT_RELATIVE_COLORIMETRIC, 0)))
+ goto lcmserr;
for (r = 0; r < height; ++r) {
- for (i = 0; i < png_row_len; i++) {
- /* ((2^16-1) / 255) == 257 */
- tmp16 = htons(257 * png_row_p[r][i]);
- if (fwrite(&tmp16, sizeof(uint16_t), 1,
- stdout) != 1)
- goto writerr;
+ cmsDoTransform(transform, 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]);
}
+ if (fwrite(outrow, sizeof(uint16_t), outrowlen,
+ stdout) != outrowlen)
+ goto writerr;
}
break;
case 16:
+ if (!(transform = cmsCreateTransform(in_profile,
+ TYPE_RGBA_16, out_profile, TYPE_RGBA_16,
+ INTENT_RELATIVE_COLORIMETRIC, 0)))
+ goto lcmserr;
for (r = 0; r < height; ++r) {
- for (i = 0; i < png_row_len / 2; i++) {
- tmp16 = *((uint16_t *)(png_row_p[r] +
- 2 * i));
- if (fwrite(&tmp16, sizeof(uint16_t), 1,
- stdout) != 1)
- goto writerr;
+ inrow = (uint16_t *)png_row_p[r];
+ for (i = 0; i < outrowlen; i++) {
+ /* swap endianness to LE */
+ inrow[i] = ntohs(inrow[i]);
+ }
+ cmsDoTransform(transform, 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]);
}
+ if (fwrite(outrow, sizeof(uint16_t), outrowlen,
+ stdout) != outrowlen)
+ goto writerr;
}
break;
default:
@@ -102,4 +162,8 @@ writerr:
perror(NULL);
return 1;
+lcmserr:
+ fprintf(stderr, "%s: lcms error\n", argv0);
+
+ return 1;
}