aboutsummaryrefslogtreecommitdiffstats
path: root/src/cg-icc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cg-icc.c')
-rw-r--r--src/cg-icc.c288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/cg-icc.c b/src/cg-icc.c
new file mode 100644
index 0000000..7ee390a
--- /dev/null
+++ b/src/cg-icc.c
@@ -0,0 +1,288 @@
+/**
+ * cg-tools -- Cooperative gamma-enabled tools
+ * Copyright (C) 2016 Mattias Andrée (maandree@kth.se)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "cg-base.h"
+
+#include <libclut.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+/**
+ * Magic number for dual-byte precision lookup table based profiles
+ */
+#define MLUT_TAG 0x6D4C5554L
+
+/**
+ * Magic number for gamma–brightness–contrast based profiles
+ * and for variable precision lookup table profiles
+ */
+#define VCGT_TAG 0x76636774L
+
+
+
+/**
+ * The default filter priority for the program
+ */
+const int64_t default_priority = 0;
+
+/**
+ * The default class for the program
+ */
+char* const default_class = PKGNAME "::cg-icc::standard";
+
+
+
+/**
+ * Print usage information and exit
+ */
+void usage(void)
+{
+ fprintf(stderr,
+ "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] "
+ "(-x | [-p priority] [-d] [file])\n",
+ argv0);
+ exit(1);
+}
+
+
+/**
+ * Handle a command line option
+ *
+ * @param opt The option, it is a NUL-terminate two-character
+ * string starting with either '-' or '+', if the
+ * argument is not recognised, call `usage`. This
+ * string will not be "-M", "-S", "-c", "-p", or "-R".
+ * @param arg The argument associated with `opt`,
+ * `NULL` there is no next argument, if this
+ * parameter is `NULL` but needed, call `usage`
+ * @return 0 if `arg` was not used,
+ * 1 if `arg` was used,
+ * -1 on error
+ */
+int handle_opt(char* opt, char* arg)
+{
+ if (opt[0] == '-')
+ switch (opt[1])
+ {
+ case 'd':
+ if (dflag || xflag)
+ usage();
+ dflag = 1;
+ break;
+ case 'x':
+ if (xflag || dflag)
+ usage();
+ xflag = 1;
+ break;
+ default:
+ usage();
+ }
+ return 0;
+}
+
+
+/**
+ * This function is called after the last
+ * call to `handle_opt`
+ *
+ * @param argc The number of unparsed arguments
+ * @param argv `NULL` terminated list of unparsed arguments
+ * @param method The argument associated with the "-M" option
+ * @param site The argument associated with the "-S" option
+ * @param crtcs The arguments associated with the "-c" options, `NULL`-terminated
+ * @param prio The argument associated with the "-p" option
+ * @param rule The argument associated with the "-R" option
+ * @return Zero on success, -1 on error
+ */
+int handle_args(int argc, char* argv[], char* method, char* site,
+ char** crtcs, char* prio, char* rule)
+{
+ int free_fflag = 0, saved_errno;
+ int q = xflag + dflag;
+ q += (method != NULL) && !strcmp(method, "?");
+ q += (prio != NULL) && !strcmp(prio, "?");
+ q += (rule != NULL) && (!strcmp(rule, "?") || !strcmp(rule, "??"));
+ for (; *crtcs; crtcs++)
+ q += !strcmp(*crtcs, "?");
+ if ((q > 1) || (xflag && ((argc > 0) || (prio != NULL))))
+ usage();
+ /* TODO */
+ return 0;
+ fail:
+ saved_errno = errno;
+ if (free_fflag)
+ free(fflag), fflag = NULL;
+ errno = saved_errno;
+ return cleanup(-1);
+}
+
+
+uint32_t icc_uint32(const char* restrict content)
+{
+ uint32_t rc;
+ rc = (uint32_t)(unsigned char)(content[0]), rc <<= 8;
+ rc |= (uint32_t)(unsigned char)(content[1]), rc <<= 8;
+ rc |= (uint32_t)(unsigned char)(content[2]), rc <<= 8;
+ rc |= (uint32_t)(unsigned char)(content[3]);
+ return rc;
+}
+
+
+uint16_t icc_uint16(const char* restrict content)
+{
+ uint16_t rc;
+ rc = (uint16_t)(unsigned char)(content[0]), rc <<= 8;
+ rc |= (uint16_t)(unsigned char)(content[1]);
+ return rc;
+}
+
+
+int parse_icc(const char* restrict content, size_t n, libcoopgamma_ramps_t* restrict ramps,
+ signed* restrict depth)
+{
+ uint32_t i_tag, n_tags;
+ size_t i, ptr = 0, xptr;
+
+ /* Skip header */
+ if (n - ptr < 128)
+ return -2;
+ ptr += 128;
+
+ /* Get the number of tags */
+ if (n - ptr < 4)
+ return -2;
+ n_tags = icc_uint32(content + ptr), ptr += 4;
+
+ for (i_tag = 0, xptr = ptr; i_tag < n_tags; i_tag++, ptr = xptr)
+ {
+ uint32_t tag_name, tag_offset, tag_size, gamma_type;
+
+ /* Get profile encoding type, offset to the profile and the encoding size of its data */
+ if (n - ptr < 12)
+ return -2;
+ tag_name = icc_uint32(content + ptr), ptr += 4;
+ tag_offset = icc_uint32(content + ptr), ptr += 4;
+ tag_size = icc_uint32(content + ptr), ptr += 4;
+ xptr = ptr;
+
+ /* Jump to the profile data */
+ if (tag_offset > INT32_MAX - tag_size)
+ return -2;
+ if (tag_offset + tag_size > n)
+ return -2;
+ ptr = tag_offset;
+
+ if (tag_name == MLUT_TAG)
+ {
+ /* The profile is encododed as an dual-byte precision lookup table */
+
+ /* Initialise ramps */
+ *depth = LIBCOOPGAMMA_UINT16;
+ ramps.u16.red_size = 256;
+ ramps.u16.green_size = 256;
+ ramps.u16.blue_size = 256;
+ if (libcoopgamma_ramps_initialise(&(ramps.u16)) < 0)
+ return -1;
+
+ /* Get the lookup table */
+ if (n - ptr < 3 * 256 * 2)
+ continue;
+ for (i = 0; i < 256; i++)
+ ramps.u16.red[i] = icc_uint16(content + ptr), ptr += 2;
+ for (i = 0; i < 256; i++)
+ ramps.u16.green[i] = icc_uint16(content + ptr), ptr += 2;
+ for (i = 0; i < 256; i++)
+ ramps.u16.blue[i] = icc_uint16(content + ptr), ptr += 2;
+
+ return 0;
+ }
+ else if (tag_name == VCGT_TAG)
+ {
+ /* The profile is encoded as with gamma, brightness and contrast values
+ * or as a variable precision lookup table profile */
+
+ /* VCGT profiles starts where their magic number */
+ if (n - ptr < 4)
+ continue;
+ tag_name = icc_uint32(content + ptr), ptr += 4;
+ if (tag_name == VCGT_TAG)
+ continue;
+
+ /* Skip four bytes */
+ if (n - ptr < 4)
+ continue;
+ ptr += 4;
+
+ /* Get the actual encoding type */
+ if (n - ptr < 4)
+ continue;
+ gamma_type = icc_uint32(content + ptr), ptr += 4;
+
+ if (gamma_type == 0)
+ {
+ /* The profile is encoded as a variable precision lookup table */
+ uint16_t n_channels, n_entries, entry_size;
+
+ /* Get metadata */
+ n_channels = icc_uint32(content + ptr), ptr += 4;
+ n_entries = icc_uint32(content + ptr), ptr += 4;
+ entry_size = icc_uint32(content + ptr), ptr += 4;
+ if (tag_size == 1584:)
+ n_channels = 3, n_entries = 256, entry_size = 2;
+ if (n_channels != 3)
+ /* Assuming sRGB, can only be an correct assumption if there are exactly three channels */
+ continue;
+
+ /* TODO */
+ }
+ else if (gamma_type == 1)
+ {
+ /* The profile is encoded with gamma, brightness and contrast values */
+ double r_gamma, r_min, r_max, g_gamma, g_min, g_max, b_gamma, b_min, b_max;
+
+ /* Get the gamma, brightness and contrast */
+ r_gamma = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ r_min = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ r_max = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ g_gamma = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ g_min = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ g_max = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ b_gamma = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ b_min = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+ b_max = (double)icc_uint32(content + ptr) / 65536L, ptr += 4;
+
+ /* Initialise ramps */
+ *depth = LIBCOOPGAMMA_DOUBLE;
+ if (libcoopgamma_ramps_initialise(&(ramps.d)) < 0)
+ return -1;
+
+ /* Set ramps */
+ libclut_start_over(&(ramps.d), (double)1, double, 1, 1, 1);
+ libclut_gamma(&(ramps.d), (double)1, double, r_gamma, g_gamma, b_gamma);
+ libclut_rgb_limits(&(ramps.d), (double)1, double, r_min, r_max, g_min, g_max, b_min, b_max);
+
+ return 0;
+ }
+ }
+ }
+
+ return -2;
+}
+