1 /* convert.c
2 *
3 * COPYRIGHT: Written by John Cunningham Bowler, 2013.
4 * To the extent possible under law, the author has waived all copyright and
5 * related or neighboring rights to this work. This work is published from:
6 * United States.
7 *
8 * Convert 8-bit sRGB or 16-bit linear values to another format.
9 */
10
11 #define _ISOC99_SOURCE 1
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <math.h>
16 #include <stdio.h>
17
18 #include <fenv.h>
19
20 #include "sRGB.h"
21
22 static void
usage(const char * prog)23 usage(const char *prog)
24 {
25 fprintf(stderr,
26 "%s: usage: %s [-linear|-sRGB] [-gray|-color] component{1,4}\n",
27 prog, prog);
28 exit(1);
29 }
30
31 unsigned long
component(const char * prog,const char * arg,int issRGB)32 component(const char *prog, const char *arg, int issRGB)
33 {
34 char *ep;
35 unsigned long c = strtoul(arg, &ep, 0);
36
37 if (ep <= arg || *ep || c > 65535 || (issRGB && c > 255))
38 {
39 fprintf(stderr, "%s: %s: invalid component value (%lu)\n", prog, arg, c);
40 usage(prog);
41 }
42
43 return c;
44 }
45
46 int
main(int argc,const char ** argv)47 main(int argc, const char **argv)
48 {
49 const char *prog = *argv++;
50 int to_linear = 0, to_gray = 0, to_color = 0;
51 int channels = 0;
52 double c[4];
53
54 /* FE_TONEAREST is the IEEE754 round to nearest, preferring even, mode; i.e.
55 * everything rounds to the nearest value except that '.5' rounds to the
56 * nearest even value.
57 */
58 fesetround(FE_TONEAREST);
59
60 c[3] = c[2] = c[1] = c[0] = 0;
61
62 while (--argc > 0 && **argv == '-')
63 {
64 const char *arg = 1+*argv++;
65
66 if (strcmp(arg, "sRGB") == 0)
67 to_linear = 0;
68
69 else if (strcmp(arg, "linear") == 0)
70 to_linear = 1;
71
72 else if (strcmp(arg, "gray") == 0)
73 to_gray = 1, to_color = 0;
74
75 else if (strcmp(arg, "color") == 0)
76 to_gray = 0, to_color = 1;
77
78 else
79 usage(prog);
80 }
81
82 switch (argc)
83 {
84 default:
85 usage(prog);
86 break;
87
88 case 4:
89 c[3] = component(prog, argv[3], to_linear);
90 ++channels;
91 case 3:
92 c[2] = component(prog, argv[2], to_linear);
93 ++channels;
94 case 2:
95 c[1] = component(prog, argv[1], to_linear);
96 ++channels;
97 case 1:
98 c[0] = component(prog, argv[0], to_linear);
99 ++channels;
100 break;
101 }
102
103 if (to_linear)
104 {
105 int i;
106 int components = channels;
107
108 if ((components & 1) == 0)
109 --components;
110
111 for (i=0; i<components; ++i) c[i] = linear_from_sRGB(c[i] / 255);
112 if (components < channels)
113 c[components] = c[components] / 255;
114 }
115
116 else
117 {
118 int i;
119 for (i=0; i<4; ++i) c[i] /= 65535;
120
121 if ((channels & 1) == 0)
122 {
123 double alpha = c[channels-1];
124
125 if (alpha > 0)
126 for (i=0; i<channels-1; ++i) c[i] /= alpha;
127 else
128 for (i=0; i<channels-1; ++i) c[i] = 1;
129 }
130 }
131
132 if (to_gray)
133 {
134 if (channels < 3)
135 {
136 fprintf(stderr, "%s: too few channels (%d) for -gray\n",
137 prog, channels);
138 usage(prog);
139 }
140
141 c[0] = YfromRGB(c[0], c[1], c[2]);
142 channels -= 2;
143 }
144
145 if (to_color)
146 {
147 if (channels > 2)
148 {
149 fprintf(stderr, "%s: too many channels (%d) for -color\n",
150 prog, channels);
151 usage(prog);
152 }
153
154 c[3] = c[1]; /* alpha, if present */
155 c[2] = c[1] = c[0];
156 }
157
158 if (to_linear)
159 {
160 int i;
161 if ((channels & 1) == 0)
162 {
163 double alpha = c[channels-1];
164 for (i=0; i<channels-1; ++i) c[i] *= alpha;
165 }
166
167 for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 65535);
168 }
169
170 else /* to sRGB */
171 {
172 int i = (channels+1)&~1;
173 while (--i >= 0)
174 c[i] = sRGB_from_linear(c[i]);
175
176 for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 255);
177 }
178
179 {
180 int i;
181 for (i=0; i<channels; ++i) printf(" %g", c[i]);
182 }
183 printf("\n");
184
185 return 0;
186 }
187