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
24static void
25usage(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
33unsigned long
34component(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
48int
49main(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