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