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