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