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