xref: /netbsd-src/external/gpl2/groff/dist/src/libs/libgroff/color.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
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