Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / groff / src / libs / libgroff / color.cc
1 // -*- C++ -*-
2
3 /* <groff_src_dir>/src/libs/libgroff/color.cc
4
5 Last update: 10 Apr 2002
6
7 Copyright (C) 2001, 2002 Free Software Foundation, Inc.
8     Written by Gaius Mulley <gaius@glam.ac.uk>
9
10 This file is part of groff.
11
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
16
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 for more details.
21
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING.  If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25
26 #include "color.h"
27 #include "cset.h"
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <stdlib.h>
36 #include "errarg.h"
37 #include "error.h"
38
39 static inline unsigned int
40 min(const unsigned int a, const unsigned int b)
41 {
42   if (a < b)
43     return a;
44   else
45     return b;
46 }
47
48 color::color(const color * const c)
49 {
50   scheme = c->scheme;
51   components[0] = c->components[0];
52   components[1] = c->components[1];
53   components[2] = c->components[2];
54   components[3] = c->components[3];
55 }
56
57 int color::operator==(const color & c) const
58 {
59   if (scheme != c.scheme)
60     return 0;
61   switch (scheme) {
62   case DEFAULT:
63     break;
64   case RGB:
65     if (Red != c.Red || Green != c.Green || Blue != c.Blue)
66       return 0;
67     break;
68   case CMYK:
69     if (Cyan != c.Cyan || Magenta != c.Magenta
70         || Yellow != c.Yellow || Black != c.Black)
71       return 0;
72     break;
73   case GRAY:
74     if (Gray != c.Gray)
75       return 0;
76     break;
77   case CMY:
78     if (Cyan != c.Cyan || Magenta != c.Magenta || Yellow != c.Yellow)
79       return 0;
80     break;
81   }
82   return 1;
83 }
84
85 int color::operator!=(const color & c) const
86 {
87   return !(*this == c);
88 }
89
90 color_scheme color::get_components(unsigned int *c) const
91 {
92 #if 0
93   if (sizeof (c) < sizeof (unsigned int) * 4)
94     fatal("argument is not big enough to store 4 color components");
95 #endif
96   c[0] = components[0];
97   c[1] = components[1];
98   c[2] = components[2];
99   c[3] = components[3];
100   return scheme;
101 }
102
103 void color::set_default()
104 {
105   scheme = DEFAULT;
106 }
107
108 // (0, 0, 0) is black
109
110 void color::set_rgb(const unsigned int r, const unsigned int g,
111                     const unsigned int b)
112 {
113   scheme = RGB;
114   Red = min(MAX_COLOR_VAL, r);
115   Green = min(MAX_COLOR_VAL, g);
116   Blue = min(MAX_COLOR_VAL, b);
117 }
118
119 // (0, 0, 0) is white
120
121 void color::set_cmy(const unsigned int c, const unsigned int m,
122                     const unsigned int y)
123 {
124   scheme = CMY;
125   Cyan = min(MAX_COLOR_VAL, c);
126   Magenta = min(MAX_COLOR_VAL, m);
127   Yellow = min(MAX_COLOR_VAL, y);
128 }
129
130 // (0, 0, 0, 0) is white
131
132 void color::set_cmyk(const unsigned int c, const unsigned int m,
133                      const unsigned int y, const unsigned int k)
134 {
135   scheme = CMYK;
136   Cyan = min(MAX_COLOR_VAL, c);
137   Magenta = min(MAX_COLOR_VAL, m);
138   Yellow = min(MAX_COLOR_VAL, y);
139   Black = min(MAX_COLOR_VAL, k);
140 }
141
142 // (0) is black
143
144 void color::set_gray(const unsigned int g)
145 {
146   scheme = GRAY;
147   Gray = min(MAX_COLOR_VAL, g);
148 }
149
150 /*
151  *  atoh - computes the decimal value of a hexadecimal number string.
152  *         `length' characters of `s' are read.  Returns 1 if successful.
153  */
154
155 static int atoh(unsigned int *result,
156                 const char * const s, const size_t length)
157 {
158   size_t i = 0;
159   unsigned int val = 0;
160   while ((i < length) && csxdigit(s[i])) {
161     if (csdigit(s[i]))
162       val = val*0x10 + (s[i]-'0');
163     else if (csupper(s[i]))
164       val = val*0x10 + (s[i]-'A') + 10;
165     else
166       val = val*0x10 + (s[i]-'a') + 10;
167     i++;
168   }
169   if (i != length)
170     return 0;
171   *result = val;
172   return 1;
173 }
174
175 /*
176  *  read_encoding - set color from a hexadecimal color string.
177  *
178  *  Use color scheme `cs' to parse `n' color components from string `s'.
179  *  Returns 1 if successful.
180  */
181
182 int color::read_encoding(const color_scheme cs, const char * const s,
183                          const size_t n)
184 {
185   size_t hex_length = 2;
186   scheme = cs;
187   char *p = (char *) s;
188   p++;
189   if (*p == '#') {
190     hex_length = 4;
191     p++;
192   }
193   for (size_t i = 0; i < n; i++) {
194     if (!atoh(&(components[i]), p, hex_length))
195       return 0;
196     if (hex_length == 2)
197       components[i] *= 0x101;   // scale up -- 0xff should become 0xffff
198     p += hex_length;
199   }
200   return 1;
201 }
202
203 int color::read_rgb(const char * const s)
204 {
205   return read_encoding(RGB, s, 3);
206 }
207
208 int color::read_cmy(const char * const s)
209 {
210   return read_encoding(CMY, s, 3);
211 }
212
213 int color::read_cmyk(const char * const s)
214 {
215   return read_encoding(CMYK, s, 4);
216 }
217
218 int color::read_gray(const char * const s)
219 {
220   return read_encoding(GRAY, s, 1);
221 }
222
223 void
224 color::get_rgb(unsigned int *r, unsigned int *g, unsigned int *b) const
225 {
226   switch (scheme) {
227   case RGB:
228     *r = Red;
229     *g = Green;
230     *b = Blue;
231     break;
232   case CMY:
233     *r = MAX_COLOR_VAL - Cyan;
234     *g = MAX_COLOR_VAL - Magenta;
235     *b = MAX_COLOR_VAL - Yellow;
236     break;
237   case CMYK:
238     *r = MAX_COLOR_VAL
239          - min(MAX_COLOR_VAL,
240                Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
241     *g = MAX_COLOR_VAL
242          - min(MAX_COLOR_VAL,
243                Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
244     *b = MAX_COLOR_VAL
245          - min(MAX_COLOR_VAL,
246                Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
247     break;
248   case GRAY:
249     *r = *g = *b = Gray;
250     break;
251   default:
252     assert(0);
253     break;
254   }
255 }
256
257 void
258 color::get_cmy(unsigned int *c, unsigned int *m, unsigned int *y) const
259 {
260   switch (scheme) {
261   case RGB:
262     *c = MAX_COLOR_VAL - Red;
263     *m = MAX_COLOR_VAL - Green;
264     *y = MAX_COLOR_VAL - Blue;
265     break;
266   case CMY:
267     *c = Cyan;
268     *m = Magenta;
269     *y = Yellow;
270     break;
271   case CMYK:
272     *c = min(MAX_COLOR_VAL,
273              Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
274     *m = min(MAX_COLOR_VAL,
275              Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
276     *y = min(MAX_COLOR_VAL,
277              Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
278     break;
279   case GRAY:
280     *c = *m = *y = MAX_COLOR_VAL - Gray;
281     break;
282   default:
283     assert(0);
284     break;
285   }
286 }
287
288 void color::get_cmyk(unsigned int *c, unsigned int *m,
289                      unsigned int *y, unsigned int *k) const
290 {
291   switch (scheme) {
292   case RGB:
293     *k = min(MAX_COLOR_VAL - Red,
294              min(MAX_COLOR_VAL - Green, MAX_COLOR_VAL - Blue));
295     if (MAX_COLOR_VAL == *k) {
296       *c = MAX_COLOR_VAL;
297       *m = MAX_COLOR_VAL;
298       *y = MAX_COLOR_VAL;
299     }
300     else {
301       *c = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Red - *k))
302            / (MAX_COLOR_VAL - *k);
303       *m = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Green - *k))
304            / (MAX_COLOR_VAL - *k);
305       *y = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Blue - *k))
306            / (MAX_COLOR_VAL - *k);
307     }
308     break;
309   case CMY:
310     *k = min(Cyan, min(Magenta, Yellow));
311     if (MAX_COLOR_VAL == *k) {
312       *c = MAX_COLOR_VAL;
313       *m = MAX_COLOR_VAL;
314       *y = MAX_COLOR_VAL;
315     }
316     else {
317       *c = (MAX_COLOR_VAL * (Cyan - *k)) / (MAX_COLOR_VAL - *k);
318       *m = (MAX_COLOR_VAL * (Magenta - *k)) / (MAX_COLOR_VAL - *k);
319       *y = (MAX_COLOR_VAL * (Yellow - *k)) / (MAX_COLOR_VAL - *k);
320     }
321     break;
322   case CMYK:
323     *c = Cyan;
324     *m = Magenta;
325     *y = Yellow;
326     *k = Black;
327     break;
328   case GRAY:
329     *c = *m = *y = 0;
330     *k = MAX_COLOR_VAL - Gray;
331     break;
332   default:
333     assert(0);
334     break;
335   }
336 }
337
338 // we use `0.222r + 0.707g + 0.071b' (this is the ITU standard)
339 // as an approximation for gray
340
341 void color::get_gray(unsigned int *g) const
342 {
343   switch (scheme) {
344   case RGB:
345     *g = (222*Red + 707*Green + 71*Blue) / 1000;
346     break;
347   case CMY:
348     *g = MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000;
349     break;
350   case CMYK:
351     *g = (MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000)
352          * (MAX_COLOR_VAL - Black);
353     break;
354   case GRAY:
355     *g = Gray;
356     break;
357   default:
358     assert(0);
359     break;
360   }
361 }
362
363 color default_color;