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