1 // -*- C++ -*-
2
3 /* <groff_src_dir>/src/libs/libgroff/color.cpp
4
5 Last update: 26 May 2004
6
7 Copyright (C) 2001, 2002, 2003, 2004 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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
25
26 #include "lib.h"
27 #include "color.h"
28 #include "cset.h"
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <stdlib.h>
37 #include "errarg.h"
38 #include "error.h"
39
40 static inline unsigned int
min(const unsigned int a,const unsigned int b)41 min(const unsigned int a, const unsigned int b)
42 {
43 if (a < b)
44 return a;
45 else
46 return b;
47 }
48
49 color *color::free_list = 0;
50
operator new(size_t n)51 void *color::operator new(size_t n)
52 {
53 assert(n == sizeof(color));
54 if (!free_list) {
55 const int BLOCK = 128;
56 free_list = (color *)new char[sizeof(color)*BLOCK];
57 for (int i = 0; i < BLOCK - 1; i++)
58 free_list[i].next = free_list + i + 1;
59 free_list[BLOCK-1].next = 0;
60 }
61 color *p = free_list;
62 free_list = (color *)(free_list->next);
63 p->next = 0;
64 return p;
65 }
66
operator delete(void * p)67 void color::operator delete(void *p)
68 {
69 if (p) {
70 ((color *)p)->next = free_list;
71 free_list = (color *)p;
72 }
73 }
74
color(const color * const c)75 color::color(const color * const c)
76 {
77 nm = c->nm;
78 scheme = c->scheme;
79 components[0] = c->components[0];
80 components[1] = c->components[1];
81 components[2] = c->components[2];
82 components[3] = c->components[3];
83 }
84
~color()85 color::~color()
86 {
87 }
88
operator ==(const color & c) const89 int color::operator==(const color & c) const
90 {
91 if (scheme != c.scheme)
92 return 0;
93 switch (scheme) {
94 case DEFAULT:
95 break;
96 case RGB:
97 if (Red != c.Red || Green != c.Green || Blue != c.Blue)
98 return 0;
99 break;
100 case CMYK:
101 if (Cyan != c.Cyan || Magenta != c.Magenta
102 || Yellow != c.Yellow || Black != c.Black)
103 return 0;
104 break;
105 case GRAY:
106 if (Gray != c.Gray)
107 return 0;
108 break;
109 case CMY:
110 if (Cyan != c.Cyan || Magenta != c.Magenta || Yellow != c.Yellow)
111 return 0;
112 break;
113 }
114 return 1;
115 }
116
operator !=(const color & c) const117 int color::operator!=(const color & c) const
118 {
119 return !(*this == c);
120 }
121
get_components(unsigned int * c) const122 color_scheme color::get_components(unsigned int *c) const
123 {
124 #if 0
125 if (sizeof (c) < sizeof (unsigned int) * 4)
126 fatal("argument is not big enough to store 4 color components");
127 #endif
128 c[0] = components[0];
129 c[1] = components[1];
130 c[2] = components[2];
131 c[3] = components[3];
132 return scheme;
133 }
134
set_default()135 void color::set_default()
136 {
137 scheme = DEFAULT;
138 }
139
140 // (0, 0, 0) is black
141
set_rgb(const unsigned int r,const unsigned int g,const unsigned int b)142 void color::set_rgb(const unsigned int r, const unsigned int g,
143 const unsigned int b)
144 {
145 scheme = RGB;
146 Red = min(MAX_COLOR_VAL, r);
147 Green = min(MAX_COLOR_VAL, g);
148 Blue = min(MAX_COLOR_VAL, b);
149 }
150
151 // (0, 0, 0) is white
152
set_cmy(const unsigned int c,const unsigned int m,const unsigned int y)153 void color::set_cmy(const unsigned int c, const unsigned int m,
154 const unsigned int y)
155 {
156 scheme = CMY;
157 Cyan = min(MAX_COLOR_VAL, c);
158 Magenta = min(MAX_COLOR_VAL, m);
159 Yellow = min(MAX_COLOR_VAL, y);
160 }
161
162 // (0, 0, 0, 0) is white
163
set_cmyk(const unsigned int c,const unsigned int m,const unsigned int y,const unsigned int k)164 void color::set_cmyk(const unsigned int c, const unsigned int m,
165 const unsigned int y, const unsigned int k)
166 {
167 scheme = CMYK;
168 Cyan = min(MAX_COLOR_VAL, c);
169 Magenta = min(MAX_COLOR_VAL, m);
170 Yellow = min(MAX_COLOR_VAL, y);
171 Black = min(MAX_COLOR_VAL, k);
172 }
173
174 // (0) is black
175
set_gray(const unsigned int g)176 void color::set_gray(const unsigned int g)
177 {
178 scheme = GRAY;
179 Gray = min(MAX_COLOR_VAL, g);
180 }
181
182 /*
183 * atoh - computes the decimal value of a hexadecimal number string.
184 * `length' characters of `s' are read. Returns 1 if successful.
185 */
186
atoh(unsigned int * result,const char * const s,const size_t length)187 static int atoh(unsigned int *result,
188 const char * const s, const size_t length)
189 {
190 size_t i = 0;
191 unsigned int val = 0;
192 while ((i < length) && csxdigit(s[i])) {
193 if (csdigit(s[i]))
194 val = val*0x10 + (s[i]-'0');
195 else if (csupper(s[i]))
196 val = val*0x10 + (s[i]-'A') + 10;
197 else
198 val = val*0x10 + (s[i]-'a') + 10;
199 i++;
200 }
201 if (i != length)
202 return 0;
203 *result = val;
204 return 1;
205 }
206
207 /*
208 * read_encoding - set color from a hexadecimal color string.
209 *
210 * Use color scheme `cs' to parse `n' color components from string `s'.
211 * Returns 1 if successful.
212 */
213
read_encoding(const color_scheme cs,const char * const s,const size_t n)214 int color::read_encoding(const color_scheme cs, const char * const s,
215 const size_t n)
216 {
217 size_t hex_length = 2;
218 scheme = cs;
219 char *p = (char *) s;
220 p++;
221 if (*p == '#') {
222 hex_length = 4;
223 p++;
224 }
225 for (size_t i = 0; i < n; i++) {
226 if (!atoh(&(components[i]), p, hex_length))
227 return 0;
228 if (hex_length == 2)
229 components[i] *= 0x101; // scale up -- 0xff should become 0xffff
230 p += hex_length;
231 }
232 return 1;
233 }
234
read_rgb(const char * const s)235 int color::read_rgb(const char * const s)
236 {
237 return read_encoding(RGB, s, 3);
238 }
239
read_cmy(const char * const s)240 int color::read_cmy(const char * const s)
241 {
242 return read_encoding(CMY, s, 3);
243 }
244
read_cmyk(const char * const s)245 int color::read_cmyk(const char * const s)
246 {
247 return read_encoding(CMYK, s, 4);
248 }
249
read_gray(const char * const s)250 int color::read_gray(const char * const s)
251 {
252 return read_encoding(GRAY, s, 1);
253 }
254
255 void
get_rgb(unsigned int * r,unsigned int * g,unsigned int * b) const256 color::get_rgb(unsigned int *r, unsigned int *g, unsigned int *b) const
257 {
258 switch (scheme) {
259 case RGB:
260 *r = Red;
261 *g = Green;
262 *b = Blue;
263 break;
264 case CMY:
265 *r = MAX_COLOR_VAL - Cyan;
266 *g = MAX_COLOR_VAL - Magenta;
267 *b = MAX_COLOR_VAL - Yellow;
268 break;
269 case CMYK:
270 *r = MAX_COLOR_VAL
271 - min(MAX_COLOR_VAL,
272 Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
273 *g = MAX_COLOR_VAL
274 - min(MAX_COLOR_VAL,
275 Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
276 *b = MAX_COLOR_VAL
277 - min(MAX_COLOR_VAL,
278 Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
279 break;
280 case GRAY:
281 *r = *g = *b = Gray;
282 break;
283 default:
284 assert(0);
285 break;
286 }
287 }
288
289 void
get_cmy(unsigned int * c,unsigned int * m,unsigned int * y) const290 color::get_cmy(unsigned int *c, unsigned int *m, unsigned int *y) const
291 {
292 switch (scheme) {
293 case RGB:
294 *c = MAX_COLOR_VAL - Red;
295 *m = MAX_COLOR_VAL - Green;
296 *y = MAX_COLOR_VAL - Blue;
297 break;
298 case CMY:
299 *c = Cyan;
300 *m = Magenta;
301 *y = Yellow;
302 break;
303 case CMYK:
304 *c = min(MAX_COLOR_VAL,
305 Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
306 *m = min(MAX_COLOR_VAL,
307 Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
308 *y = min(MAX_COLOR_VAL,
309 Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
310 break;
311 case GRAY:
312 *c = *m = *y = MAX_COLOR_VAL - Gray;
313 break;
314 default:
315 assert(0);
316 break;
317 }
318 }
319
get_cmyk(unsigned int * c,unsigned int * m,unsigned int * y,unsigned int * k) const320 void color::get_cmyk(unsigned int *c, unsigned int *m,
321 unsigned int *y, unsigned int *k) const
322 {
323 switch (scheme) {
324 case RGB:
325 *k = min(MAX_COLOR_VAL - Red,
326 min(MAX_COLOR_VAL - Green, MAX_COLOR_VAL - Blue));
327 if (MAX_COLOR_VAL == *k) {
328 *c = MAX_COLOR_VAL;
329 *m = MAX_COLOR_VAL;
330 *y = MAX_COLOR_VAL;
331 }
332 else {
333 *c = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Red - *k))
334 / (MAX_COLOR_VAL - *k);
335 *m = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Green - *k))
336 / (MAX_COLOR_VAL - *k);
337 *y = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Blue - *k))
338 / (MAX_COLOR_VAL - *k);
339 }
340 break;
341 case CMY:
342 *k = min(Cyan, min(Magenta, Yellow));
343 if (MAX_COLOR_VAL == *k) {
344 *c = MAX_COLOR_VAL;
345 *m = MAX_COLOR_VAL;
346 *y = MAX_COLOR_VAL;
347 }
348 else {
349 *c = (MAX_COLOR_VAL * (Cyan - *k)) / (MAX_COLOR_VAL - *k);
350 *m = (MAX_COLOR_VAL * (Magenta - *k)) / (MAX_COLOR_VAL - *k);
351 *y = (MAX_COLOR_VAL * (Yellow - *k)) / (MAX_COLOR_VAL - *k);
352 }
353 break;
354 case CMYK:
355 *c = Cyan;
356 *m = Magenta;
357 *y = Yellow;
358 *k = Black;
359 break;
360 case GRAY:
361 *c = *m = *y = 0;
362 *k = MAX_COLOR_VAL - Gray;
363 break;
364 default:
365 assert(0);
366 break;
367 }
368 }
369
370 // we use `0.222r + 0.707g + 0.071b' (this is the ITU standard)
371 // as an approximation for gray
372
get_gray(unsigned int * g) const373 void color::get_gray(unsigned int *g) const
374 {
375 switch (scheme) {
376 case RGB:
377 *g = (222*Red + 707*Green + 71*Blue) / 1000;
378 break;
379 case CMY:
380 *g = MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000;
381 break;
382 case CMYK:
383 *g = (MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000)
384 * (MAX_COLOR_VAL - Black);
385 break;
386 case GRAY:
387 *g = Gray;
388 break;
389 default:
390 assert(0);
391 break;
392 }
393 }
394
print_color()395 char *color::print_color()
396 {
397 char *s = new char[30];
398 switch (scheme) {
399 case DEFAULT:
400 sprintf(s, "default");
401 break;
402 case RGB:
403 sprintf(s, "rgb %.2ff %.2ff %.2ff",
404 double(Red) / MAX_COLOR_VAL,
405 double(Green) / MAX_COLOR_VAL,
406 double(Blue) / MAX_COLOR_VAL);
407 break;
408 case CMY:
409 sprintf(s, "cmy %.2ff %.2ff %.2ff",
410 double(Cyan) / MAX_COLOR_VAL,
411 double(Magenta) / MAX_COLOR_VAL,
412 double(Yellow) / MAX_COLOR_VAL);
413 break;
414 case CMYK:
415 sprintf(s, "cmyk %.2ff %.2ff %.2ff %.2ff",
416 double(Cyan) / MAX_COLOR_VAL,
417 double(Magenta) / MAX_COLOR_VAL,
418 double(Yellow) / MAX_COLOR_VAL,
419 double(Black) / MAX_COLOR_VAL);
420 break;
421 case GRAY:
422 sprintf(s, "gray %.2ff",
423 double(Gray) / MAX_COLOR_VAL);
424 break;
425 }
426 return s;
427 }
428
429 color default_color;
430