1// -*- C++ -*-
2
3/* <groff_src_dir>/src/libs/libgroff/color.cpp
4
5Last update: 26 May 2004
6
7Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
8    Written by Gaius Mulley <gaius@glam.ac.uk>
9
10This file is part of groff.
11
12groff is free software; you can redistribute it and/or modify it under
13the terms of the GNU General Public License as published by the Free
14Software Foundation; either version 2, or (at your option) any later
15version.
16
17groff is distributed in the hope that it will be useful, but WITHOUT ANY
18WARRANTY; without even the implied warranty of MERCHANTABILITY or
19FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20for more details.
21
22You should have received a copy of the GNU General Public License along
23with groff; see the file COPYING.  If not, write to the Free Software
24Foundation, 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
40static inline unsigned int
41min(const unsigned int a, const unsigned int b)
42{
43  if (a < b)
44    return a;
45  else
46    return b;
47}
48
49color *color::free_list = 0;
50
51void *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
67void color::operator delete(void *p)
68{
69  if (p) {
70    ((color *)p)->next = free_list;
71    free_list = (color *)p;
72  }
73}
74
75color::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
85color::~color()
86{
87}
88
89int 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
117int color::operator!=(const color & c) const
118{
119  return !(*this == c);
120}
121
122color_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
135void color::set_default()
136{
137  scheme = DEFAULT;
138}
139
140// (0, 0, 0) is black
141
142void 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
153void 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
164void 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
176void 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
187static 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
214int 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
235int color::read_rgb(const char * const s)
236{
237  return read_encoding(RGB, s, 3);
238}
239
240int color::read_cmy(const char * const s)
241{
242  return read_encoding(CMY, s, 3);
243}
244
245int color::read_cmyk(const char * const s)
246{
247  return read_encoding(CMYK, s, 4);
248}
249
250int color::read_gray(const char * const s)
251{
252  return read_encoding(GRAY, s, 1);
253}
254
255void
256color::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
289void
290color::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
320void 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
373void 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
395char *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
429color default_color;
430