| /* convert.c |
| * |
| * COPYRIGHT: Written by John Cunningham Bowler, 2013. |
| * To the extent possible under law, the author has waived all copyright and |
| * related or neighboring rights to this work. This work is published from: |
| * United States. |
| * |
| * Convert 8-bit sRGB or 16-bit linear values to another format. |
| */ |
| |
| #define _ISOC99_SOURCE 1 |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> |
| #include <stdio.h> |
| |
| #include <fenv.h> |
| |
| #include "sRGB.h" |
| |
| static void |
| usage(const char *prog) |
| { |
| fprintf(stderr, |
| "%s: usage: %s [-linear|-sRGB] [-gray|-color] component{1,4}\n", |
| prog, prog); |
| exit(1); |
| } |
| |
| unsigned long |
| component(const char *prog, const char *arg, int issRGB) |
| { |
| char *ep; |
| unsigned long c = strtoul(arg, &ep, 0); |
| |
| if (ep <= arg || *ep || c > 65535 || (issRGB && c > 255)) |
| { |
| fprintf(stderr, "%s: %s: invalid component value (%lu)\n", prog, arg, c); |
| usage(prog); |
| } |
| |
| return c; |
| } |
| |
| int |
| main(int argc, const char **argv) |
| { |
| const char *prog = *argv++; |
| int to_linear = 0, to_gray = 0, to_color = 0; |
| int channels = 0; |
| double c[4]; |
| |
| /* FE_TONEAREST is the IEEE754 round to nearest, preferring even, mode; i.e. |
| * everything rounds to the nearest value except that '.5' rounds to the |
| * nearest even value. |
| */ |
| fesetround(FE_TONEAREST); |
| |
| c[3] = c[2] = c[1] = c[0] = 0; |
| |
| while (--argc > 0 && **argv == '-') |
| { |
| const char *arg = 1+*argv++; |
| |
| if (strcmp(arg, "sRGB") == 0) |
| to_linear = 0; |
| |
| else if (strcmp(arg, "linear") == 0) |
| to_linear = 1; |
| |
| else if (strcmp(arg, "gray") == 0) |
| to_gray = 1, to_color = 0; |
| |
| else if (strcmp(arg, "color") == 0) |
| to_gray = 0, to_color = 1; |
| |
| else |
| usage(prog); |
| } |
| |
| switch (argc) |
| { |
| default: |
| usage(prog); |
| break; |
| |
| case 4: |
| c[3] = component(prog, argv[3], to_linear); |
| ++channels; |
| case 3: |
| c[2] = component(prog, argv[2], to_linear); |
| ++channels; |
| case 2: |
| c[1] = component(prog, argv[1], to_linear); |
| ++channels; |
| case 1: |
| c[0] = component(prog, argv[0], to_linear); |
| ++channels; |
| break; |
| } |
| |
| if (to_linear) |
| { |
| int i; |
| int components = channels; |
| |
| if ((components & 1) == 0) |
| --components; |
| |
| for (i=0; i<components; ++i) c[i] = linear_from_sRGB(c[i] / 255); |
| if (components < channels) |
| c[components] = c[components] / 255; |
| } |
| |
| else |
| { |
| int i; |
| for (i=0; i<4; ++i) c[i] /= 65535; |
| |
| if ((channels & 1) == 0) |
| { |
| double alpha = c[channels-1]; |
| |
| if (alpha > 0) |
| for (i=0; i<channels-1; ++i) c[i] /= alpha; |
| else |
| for (i=0; i<channels-1; ++i) c[i] = 1; |
| } |
| } |
| |
| if (to_gray) |
| { |
| if (channels < 3) |
| { |
| fprintf(stderr, "%s: too few channels (%d) for -gray\n", |
| prog, channels); |
| usage(prog); |
| } |
| |
| c[0] = YfromRGB(c[0], c[1], c[2]); |
| channels -= 2; |
| } |
| |
| if (to_color) |
| { |
| if (channels > 2) |
| { |
| fprintf(stderr, "%s: too many channels (%d) for -color\n", |
| prog, channels); |
| usage(prog); |
| } |
| |
| c[3] = c[1]; /* alpha, if present */ |
| c[2] = c[1] = c[0]; |
| } |
| |
| if (to_linear) |
| { |
| int i; |
| if ((channels & 1) == 0) |
| { |
| double alpha = c[channels-1]; |
| for (i=0; i<channels-1; ++i) c[i] *= alpha; |
| } |
| |
| for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 65535); |
| } |
| |
| else /* to sRGB */ |
| { |
| int i = (channels+1)&~1; |
| while (--i >= 0) |
| c[i] = sRGB_from_linear(c[i]); |
| |
| for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 255); |
| } |
| |
| { |
| int i; |
| for (i=0; i<channels; ++i) printf(" %g", c[i]); |
| } |
| printf("\n"); |
| |
| return 0; |
| } |