diff options
-rw-r--r-- | FORMAT | 23 | ||||
-rw-r--r-- | config.mk | 7 | ||||
-rw-r--r-- | farbfeld.5 | 6 | ||||
-rw-r--r-- | ff2png.c | 47 | ||||
-rw-r--r-- | jpg2ff.c | 58 | ||||
-rw-r--r-- | png2ff.c | 96 |
6 files changed, 195 insertions, 42 deletions
@@ -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 | + +--------+-------------------------------------------------------+ @@ -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 @@ -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 @@ -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; } @@ -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; } @@ -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; } |