1114402Sru// -*- C++ -*-
2114402Sru
3114402Sru/* <groff_src_dir>/src/libs/libgroff/color.cpp
4114402Sru
5151497SruLast update: 26 May 2004
6114402Sru
7151497SruCopyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
8114402Sru    Written by Gaius Mulley <gaius@glam.ac.uk>
9114402Sru
10114402SruThis file is part of groff.
11114402Sru
12114402Srugroff is free software; you can redistribute it and/or modify it under
13114402Sruthe terms of the GNU General Public License as published by the Free
14114402SruSoftware Foundation; either version 2, or (at your option) any later
15114402Sruversion.
16114402Sru
17114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
18114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
19114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20114402Srufor more details.
21114402Sru
22114402SruYou should have received a copy of the GNU General Public License along
23114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
24151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
25114402Sru
26151497Sru#include "lib.h"
27114402Sru#include "color.h"
28114402Sru#include "cset.h"
29114402Sru#ifdef HAVE_UNISTD_H
30114402Sru#include <unistd.h>
31114402Sru#endif
32114402Sru
33114402Sru#include <assert.h>
34114402Sru#include <stdio.h>
35114402Sru#include <fcntl.h>
36114402Sru#include <stdlib.h>
37114402Sru#include "errarg.h"
38114402Sru#include "error.h"
39114402Sru
40114402Srustatic inline unsigned int
41114402Srumin(const unsigned int a, const unsigned int b)
42114402Sru{
43114402Sru  if (a < b)
44114402Sru    return a;
45114402Sru  else
46114402Sru    return b;
47114402Sru}
48114402Sru
49114402Srucolor *color::free_list = 0;
50114402Sru
51114402Sruvoid *color::operator new(size_t n)
52114402Sru{
53114402Sru  assert(n == sizeof(color));
54114402Sru  if (!free_list) {
55114402Sru    const int BLOCK = 128;
56114402Sru    free_list = (color *)new char[sizeof(color)*BLOCK];
57114402Sru    for (int i = 0; i < BLOCK - 1; i++)
58114402Sru      free_list[i].next = free_list + i + 1;
59114402Sru    free_list[BLOCK-1].next = 0;
60114402Sru  }
61114402Sru  color *p = free_list;
62114402Sru  free_list = (color *)(free_list->next);
63114402Sru  p->next = 0;
64114402Sru  return p;
65114402Sru}
66114402Sru
67114402Sruvoid color::operator delete(void *p)
68114402Sru{
69114402Sru  if (p) {
70114402Sru    ((color *)p)->next = free_list;
71114402Sru    free_list = (color *)p;
72114402Sru  }
73114402Sru}
74114402Sru
75114402Srucolor::color(const color * const c)
76114402Sru{
77151497Sru  nm = c->nm;
78114402Sru  scheme = c->scheme;
79114402Sru  components[0] = c->components[0];
80114402Sru  components[1] = c->components[1];
81114402Sru  components[2] = c->components[2];
82114402Sru  components[3] = c->components[3];
83114402Sru}
84114402Sru
85114402Srucolor::~color()
86114402Sru{
87114402Sru}
88114402Sru
89114402Sruint color::operator==(const color & c) const
90114402Sru{
91114402Sru  if (scheme != c.scheme)
92114402Sru    return 0;
93114402Sru  switch (scheme) {
94114402Sru  case DEFAULT:
95114402Sru    break;
96114402Sru  case RGB:
97114402Sru    if (Red != c.Red || Green != c.Green || Blue != c.Blue)
98114402Sru      return 0;
99114402Sru    break;
100114402Sru  case CMYK:
101114402Sru    if (Cyan != c.Cyan || Magenta != c.Magenta
102114402Sru	|| Yellow != c.Yellow || Black != c.Black)
103114402Sru      return 0;
104114402Sru    break;
105114402Sru  case GRAY:
106114402Sru    if (Gray != c.Gray)
107114402Sru      return 0;
108114402Sru    break;
109114402Sru  case CMY:
110114402Sru    if (Cyan != c.Cyan || Magenta != c.Magenta || Yellow != c.Yellow)
111114402Sru      return 0;
112114402Sru    break;
113114402Sru  }
114114402Sru  return 1;
115114402Sru}
116114402Sru
117114402Sruint color::operator!=(const color & c) const
118114402Sru{
119114402Sru  return !(*this == c);
120114402Sru}
121114402Sru
122114402Srucolor_scheme color::get_components(unsigned int *c) const
123114402Sru{
124114402Sru#if 0
125114402Sru  if (sizeof (c) < sizeof (unsigned int) * 4)
126114402Sru    fatal("argument is not big enough to store 4 color components");
127114402Sru#endif
128114402Sru  c[0] = components[0];
129114402Sru  c[1] = components[1];
130114402Sru  c[2] = components[2];
131114402Sru  c[3] = components[3];
132114402Sru  return scheme;
133114402Sru}
134114402Sru
135114402Sruvoid color::set_default()
136114402Sru{
137114402Sru  scheme = DEFAULT;
138114402Sru}
139114402Sru
140114402Sru// (0, 0, 0) is black
141114402Sru
142114402Sruvoid color::set_rgb(const unsigned int r, const unsigned int g,
143114402Sru		    const unsigned int b)
144114402Sru{
145114402Sru  scheme = RGB;
146114402Sru  Red = min(MAX_COLOR_VAL, r);
147114402Sru  Green = min(MAX_COLOR_VAL, g);
148114402Sru  Blue = min(MAX_COLOR_VAL, b);
149114402Sru}
150114402Sru
151114402Sru// (0, 0, 0) is white
152114402Sru
153114402Sruvoid color::set_cmy(const unsigned int c, const unsigned int m,
154114402Sru		    const unsigned int y)
155114402Sru{
156114402Sru  scheme = CMY;
157114402Sru  Cyan = min(MAX_COLOR_VAL, c);
158114402Sru  Magenta = min(MAX_COLOR_VAL, m);
159114402Sru  Yellow = min(MAX_COLOR_VAL, y);
160114402Sru}
161114402Sru
162114402Sru// (0, 0, 0, 0) is white
163114402Sru
164114402Sruvoid color::set_cmyk(const unsigned int c, const unsigned int m,
165114402Sru		     const unsigned int y, const unsigned int k)
166114402Sru{
167114402Sru  scheme = CMYK;
168114402Sru  Cyan = min(MAX_COLOR_VAL, c);
169114402Sru  Magenta = min(MAX_COLOR_VAL, m);
170114402Sru  Yellow = min(MAX_COLOR_VAL, y);
171114402Sru  Black = min(MAX_COLOR_VAL, k);
172114402Sru}
173114402Sru
174114402Sru// (0) is black
175114402Sru
176114402Sruvoid color::set_gray(const unsigned int g)
177114402Sru{
178114402Sru  scheme = GRAY;
179114402Sru  Gray = min(MAX_COLOR_VAL, g);
180114402Sru}
181114402Sru
182114402Sru/*
183114402Sru *  atoh - computes the decimal value of a hexadecimal number string.
184114402Sru *         `length' characters of `s' are read.  Returns 1 if successful.
185114402Sru */
186114402Sru
187114402Srustatic int atoh(unsigned int *result,
188114402Sru		const char * const s, const size_t length)
189114402Sru{
190114402Sru  size_t i = 0;
191114402Sru  unsigned int val = 0;
192114402Sru  while ((i < length) && csxdigit(s[i])) {
193114402Sru    if (csdigit(s[i]))
194114402Sru      val = val*0x10 + (s[i]-'0');
195114402Sru    else if (csupper(s[i]))
196114402Sru      val = val*0x10 + (s[i]-'A') + 10;
197114402Sru    else
198114402Sru      val = val*0x10 + (s[i]-'a') + 10;
199114402Sru    i++;
200114402Sru  }
201114402Sru  if (i != length)
202114402Sru    return 0;
203114402Sru  *result = val;
204114402Sru  return 1;
205114402Sru}
206114402Sru
207114402Sru/*
208114402Sru *  read_encoding - set color from a hexadecimal color string.
209114402Sru *
210114402Sru *  Use color scheme `cs' to parse `n' color components from string `s'.
211114402Sru *  Returns 1 if successful.
212114402Sru */
213114402Sru
214114402Sruint color::read_encoding(const color_scheme cs, const char * const s,
215114402Sru			 const size_t n)
216114402Sru{
217114402Sru  size_t hex_length = 2;
218114402Sru  scheme = cs;
219114402Sru  char *p = (char *) s;
220114402Sru  p++;
221114402Sru  if (*p == '#') {
222114402Sru    hex_length = 4;
223114402Sru    p++;
224114402Sru  }
225114402Sru  for (size_t i = 0; i < n; i++) {
226114402Sru    if (!atoh(&(components[i]), p, hex_length))
227114402Sru      return 0;
228114402Sru    if (hex_length == 2)
229114402Sru      components[i] *= 0x101;	// scale up -- 0xff should become 0xffff
230114402Sru    p += hex_length;
231114402Sru  }
232114402Sru  return 1;
233114402Sru}
234114402Sru
235114402Sruint color::read_rgb(const char * const s)
236114402Sru{
237114402Sru  return read_encoding(RGB, s, 3);
238114402Sru}
239114402Sru
240114402Sruint color::read_cmy(const char * const s)
241114402Sru{
242114402Sru  return read_encoding(CMY, s, 3);
243114402Sru}
244114402Sru
245114402Sruint color::read_cmyk(const char * const s)
246114402Sru{
247114402Sru  return read_encoding(CMYK, s, 4);
248114402Sru}
249114402Sru
250114402Sruint color::read_gray(const char * const s)
251114402Sru{
252114402Sru  return read_encoding(GRAY, s, 1);
253114402Sru}
254114402Sru
255114402Sruvoid
256114402Srucolor::get_rgb(unsigned int *r, unsigned int *g, unsigned int *b) const
257114402Sru{
258114402Sru  switch (scheme) {
259114402Sru  case RGB:
260114402Sru    *r = Red;
261114402Sru    *g = Green;
262114402Sru    *b = Blue;
263114402Sru    break;
264114402Sru  case CMY:
265114402Sru    *r = MAX_COLOR_VAL - Cyan;
266114402Sru    *g = MAX_COLOR_VAL - Magenta;
267114402Sru    *b = MAX_COLOR_VAL - Yellow;
268114402Sru    break;
269114402Sru  case CMYK:
270114402Sru    *r = MAX_COLOR_VAL
271114402Sru	 - min(MAX_COLOR_VAL,
272114402Sru	       Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
273114402Sru    *g = MAX_COLOR_VAL
274114402Sru	 - min(MAX_COLOR_VAL,
275114402Sru	       Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
276114402Sru    *b = MAX_COLOR_VAL
277114402Sru	 - min(MAX_COLOR_VAL,
278114402Sru	       Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
279114402Sru    break;
280114402Sru  case GRAY:
281114402Sru    *r = *g = *b = Gray;
282114402Sru    break;
283114402Sru  default:
284114402Sru    assert(0);
285114402Sru    break;
286114402Sru  }
287114402Sru}
288114402Sru
289114402Sruvoid
290114402Srucolor::get_cmy(unsigned int *c, unsigned int *m, unsigned int *y) const
291114402Sru{
292114402Sru  switch (scheme) {
293114402Sru  case RGB:
294114402Sru    *c = MAX_COLOR_VAL - Red;
295114402Sru    *m = MAX_COLOR_VAL - Green;
296114402Sru    *y = MAX_COLOR_VAL - Blue;
297114402Sru    break;
298114402Sru  case CMY:
299114402Sru    *c = Cyan;
300114402Sru    *m = Magenta;
301114402Sru    *y = Yellow;
302114402Sru    break;
303114402Sru  case CMYK:
304114402Sru    *c = min(MAX_COLOR_VAL,
305114402Sru	     Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
306114402Sru    *m = min(MAX_COLOR_VAL,
307114402Sru	     Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
308114402Sru    *y = min(MAX_COLOR_VAL,
309114402Sru	     Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
310114402Sru    break;
311114402Sru  case GRAY:
312114402Sru    *c = *m = *y = MAX_COLOR_VAL - Gray;
313114402Sru    break;
314114402Sru  default:
315114402Sru    assert(0);
316114402Sru    break;
317114402Sru  }
318114402Sru}
319114402Sru
320114402Sruvoid color::get_cmyk(unsigned int *c, unsigned int *m,
321114402Sru		     unsigned int *y, unsigned int *k) const
322114402Sru{
323114402Sru  switch (scheme) {
324114402Sru  case RGB:
325114402Sru    *k = min(MAX_COLOR_VAL - Red,
326114402Sru	     min(MAX_COLOR_VAL - Green, MAX_COLOR_VAL - Blue));
327114402Sru    if (MAX_COLOR_VAL == *k) {
328114402Sru      *c = MAX_COLOR_VAL;
329114402Sru      *m = MAX_COLOR_VAL;
330114402Sru      *y = MAX_COLOR_VAL;
331114402Sru    }
332114402Sru    else {
333114402Sru      *c = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Red - *k))
334114402Sru	   / (MAX_COLOR_VAL - *k);
335114402Sru      *m = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Green - *k))
336114402Sru	   / (MAX_COLOR_VAL - *k);
337114402Sru      *y = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Blue - *k))
338114402Sru	   / (MAX_COLOR_VAL - *k);
339114402Sru    }
340114402Sru    break;
341114402Sru  case CMY:
342114402Sru    *k = min(Cyan, min(Magenta, Yellow));
343114402Sru    if (MAX_COLOR_VAL == *k) {
344114402Sru      *c = MAX_COLOR_VAL;
345114402Sru      *m = MAX_COLOR_VAL;
346114402Sru      *y = MAX_COLOR_VAL;
347114402Sru    }
348114402Sru    else {
349114402Sru      *c = (MAX_COLOR_VAL * (Cyan - *k)) / (MAX_COLOR_VAL - *k);
350114402Sru      *m = (MAX_COLOR_VAL * (Magenta - *k)) / (MAX_COLOR_VAL - *k);
351114402Sru      *y = (MAX_COLOR_VAL * (Yellow - *k)) / (MAX_COLOR_VAL - *k);
352114402Sru    }
353114402Sru    break;
354114402Sru  case CMYK:
355114402Sru    *c = Cyan;
356114402Sru    *m = Magenta;
357114402Sru    *y = Yellow;
358114402Sru    *k = Black;
359114402Sru    break;
360114402Sru  case GRAY:
361114402Sru    *c = *m = *y = 0;
362114402Sru    *k = MAX_COLOR_VAL - Gray;
363114402Sru    break;
364114402Sru  default:
365114402Sru    assert(0);
366114402Sru    break;
367114402Sru  }
368114402Sru}
369114402Sru
370114402Sru// we use `0.222r + 0.707g + 0.071b' (this is the ITU standard)
371114402Sru// as an approximation for gray
372114402Sru
373114402Sruvoid color::get_gray(unsigned int *g) const
374114402Sru{
375114402Sru  switch (scheme) {
376114402Sru  case RGB:
377114402Sru    *g = (222*Red + 707*Green + 71*Blue) / 1000;
378114402Sru    break;
379114402Sru  case CMY:
380114402Sru    *g = MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000;
381114402Sru    break;
382114402Sru  case CMYK:
383114402Sru    *g = (MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000)
384114402Sru	 * (MAX_COLOR_VAL - Black);
385114402Sru    break;
386114402Sru  case GRAY:
387114402Sru    *g = Gray;
388114402Sru    break;
389114402Sru  default:
390114402Sru    assert(0);
391114402Sru    break;
392114402Sru  }
393114402Sru}
394114402Sru
395114402Sruchar *color::print_color()
396114402Sru{
397114402Sru  char *s = new char[30];
398114402Sru  switch (scheme) {
399114402Sru  case DEFAULT:
400114402Sru    sprintf(s, "default");
401114402Sru    break;
402114402Sru  case RGB:
403114402Sru    sprintf(s, "rgb %.2ff %.2ff %.2ff",
404114402Sru	    double(Red) / MAX_COLOR_VAL,
405114402Sru	    double(Green) / MAX_COLOR_VAL,
406114402Sru	    double(Blue) / MAX_COLOR_VAL);
407114402Sru    break;
408114402Sru  case CMY:
409114402Sru    sprintf(s, "cmy %.2ff %.2ff %.2ff",
410114402Sru	    double(Cyan) / MAX_COLOR_VAL,
411114402Sru	    double(Magenta) / MAX_COLOR_VAL,
412114402Sru	    double(Yellow) / MAX_COLOR_VAL);
413114402Sru    break;
414114402Sru  case CMYK:
415114402Sru    sprintf(s, "cmyk %.2ff %.2ff %.2ff %.2ff",
416114402Sru	    double(Cyan) / MAX_COLOR_VAL,
417114402Sru	    double(Magenta) / MAX_COLOR_VAL,
418114402Sru	    double(Yellow) / MAX_COLOR_VAL,
419114402Sru	    double(Black) / MAX_COLOR_VAL);
420114402Sru    break;
421114402Sru  case GRAY:
422114402Sru    sprintf(s, "gray %.2ff",
423114402Sru	    double(Gray) / MAX_COLOR_VAL);
424114402Sru    break;
425114402Sru  }
426114402Sru  return s;
427114402Sru}
428114402Sru
429114402Srucolor default_color;
430