color.cpp revision 151497
1280304Sjkim// -*- C++ -*- 2280304Sjkim 3280304Sjkim/* <groff_src_dir>/src/libs/libgroff/color.cpp 4238384Sjkim 5238384SjkimLast update: 26 May 2004 6238384Sjkim 7238384SjkimCopyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. 8238384Sjkim Written by Gaius Mulley <gaius@glam.ac.uk> 9238384Sjkim 10238384SjkimThis file is part of groff. 11238384Sjkim 12238384Sjkimgroff is free software; you can redistribute it and/or modify it under 13280304Sjkimthe terms of the GNU General Public License as published by the Free 14238384SjkimSoftware Foundation; either version 2, or (at your option) any later 15238384Sjkimversion. 16238384Sjkim 17238384Sjkimgroff is distributed in the hope that it will be useful, but WITHOUT ANY 18238384SjkimWARRANTY; without even the implied warranty of MERCHANTABILITY or 19238384SjkimFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 20238384Sjkimfor more details. 21238384Sjkim 22238384SjkimYou should have received a copy of the GNU General Public License along 23238384Sjkimwith groff; see the file COPYING. If not, write to the Free Software 24238384SjkimFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 25238384Sjkim 26238384Sjkim#include "lib.h" 27238384Sjkim#include "color.h" 28238384Sjkim#include "cset.h" 29238384Sjkim#ifdef HAVE_UNISTD_H 30238384Sjkim#include <unistd.h> 31238384Sjkim#endif 32238384Sjkim 33238384Sjkim#include <assert.h> 34238384Sjkim#include <stdio.h> 35238384Sjkim#include <fcntl.h> 36238384Sjkim#include <stdlib.h> 37238384Sjkim#include "errarg.h" 38238384Sjkim#include "error.h" 39238384Sjkim 40238384Sjkimstatic inline unsigned int 41238384Sjkimmin(const unsigned int a, const unsigned int b) 42238384Sjkim{ 43238384Sjkim if (a < b) 44238384Sjkim return a; 45238384Sjkim else 46238384Sjkim return b; 47238384Sjkim} 48238384Sjkim 49238384Sjkimcolor *color::free_list = 0; 50238384Sjkim 51238384Sjkimvoid *color::operator new(size_t n) 52238384Sjkim{ 53238384Sjkim assert(n == sizeof(color)); 54238384Sjkim if (!free_list) { 55238384Sjkim const int BLOCK = 128; 56238384Sjkim free_list = (color *)new char[sizeof(color)*BLOCK]; 57238384Sjkim for (int i = 0; i < BLOCK - 1; i++) 58238384Sjkim free_list[i].next = free_list + i + 1; 59238384Sjkim free_list[BLOCK-1].next = 0; 60238384Sjkim } 61238384Sjkim color *p = free_list; 62238384Sjkim free_list = (color *)(free_list->next); 63238384Sjkim p->next = 0; 64238384Sjkim return p; 65238384Sjkim} 66280304Sjkim 67280304Sjkimvoid color::operator delete(void *p) 68280304Sjkim{ 69238384Sjkim if (p) { 70238384Sjkim ((color *)p)->next = free_list; 71238384Sjkim free_list = (color *)p; 72280304Sjkim } 73280304Sjkim} 74280304Sjkim 75238384Sjkimcolor::color(const color * const c) 76238384Sjkim{ 77280304Sjkim nm = c->nm; 78280304Sjkim scheme = c->scheme; 79280304Sjkim components[0] = c->components[0]; 80280304Sjkim components[1] = c->components[1]; 81280304Sjkim components[2] = c->components[2]; 82280304Sjkim components[3] = c->components[3]; 83280304Sjkim} 84280304Sjkim 85238384Sjkimcolor::~color() 86238384Sjkim{ 87280304Sjkim} 88280304Sjkim 89280304Sjkimint color::operator==(const color & c) const 90280304Sjkim{ 91280304Sjkim if (scheme != c.scheme) 92238384Sjkim return 0; 93280304Sjkim switch (scheme) { 94280304Sjkim case DEFAULT: 95280304Sjkim break; 96280304Sjkim case RGB: 97238384Sjkim if (Red != c.Red || Green != c.Green || Blue != c.Blue) 98238384Sjkim return 0; 99280304Sjkim break; 100280304Sjkim case CMYK: 101280304Sjkim if (Cyan != c.Cyan || Magenta != c.Magenta 102280304Sjkim || Yellow != c.Yellow || Black != c.Black) 103238384Sjkim return 0; 104238384Sjkim break; 105238384Sjkim case GRAY: 106280304Sjkim if (Gray != c.Gray) 107280304Sjkim return 0; 108280304Sjkim break; 109280304Sjkim case CMY: 110280304Sjkim if (Cyan != c.Cyan || Magenta != c.Magenta || Yellow != c.Yellow) 111291721Sjkim return 0; 112291721Sjkim break; 113291721Sjkim } 114280304Sjkim return 1; 115291721Sjkim} 116291721Sjkim 117291721Sjkimint color::operator!=(const color & c) const 118291721Sjkim{ 119280304Sjkim return !(*this == c); 120238384Sjkim} 121238384Sjkim 122280304Sjkimcolor_scheme color::get_components(unsigned int *c) const 123280304Sjkim{ 124280304Sjkim#if 0 125280304Sjkim if (sizeof (c) < sizeof (unsigned int) * 4) 126280304Sjkim fatal("argument is not big enough to store 4 color components"); 127280304Sjkim#endif 128280304Sjkim c[0] = components[0]; 129280304Sjkim c[1] = components[1]; 130280304Sjkim c[2] = components[2]; 131238384Sjkim c[3] = components[3]; 132280304Sjkim return scheme; 133238384Sjkim} 134280304Sjkim 135280304Sjkimvoid color::set_default() 136280304Sjkim{ 137238384Sjkim scheme = DEFAULT; 138280304Sjkim} 139280304Sjkim 140280304Sjkim// (0, 0, 0) is black 141238384Sjkim 142238384Sjkimvoid color::set_rgb(const unsigned int r, const unsigned int g, 143280304Sjkim const unsigned int b) 144280304Sjkim{ 145280304Sjkim scheme = RGB; 146280304Sjkim Red = min(MAX_COLOR_VAL, r); 147238384Sjkim Green = min(MAX_COLOR_VAL, g); 148280304Sjkim Blue = min(MAX_COLOR_VAL, b); 149280304Sjkim} 150238384Sjkim 151280304Sjkim// (0, 0, 0) is white 152238384Sjkim 153280304Sjkimvoid color::set_cmy(const unsigned int c, const unsigned int m, 154238384Sjkim const unsigned int y) 155280304Sjkim{ 156280304Sjkim scheme = CMY; 157280304Sjkim Cyan = min(MAX_COLOR_VAL, c); 158238384Sjkim Magenta = min(MAX_COLOR_VAL, m); 159280304Sjkim Yellow = min(MAX_COLOR_VAL, y); 160280304Sjkim} 161238384Sjkim 162280304Sjkim// (0, 0, 0, 0) is white 163280304Sjkim 164238384Sjkimvoid color::set_cmyk(const unsigned int c, const unsigned int m, 165280304Sjkim const unsigned int y, const unsigned int k) 166238384Sjkim{ 167280304Sjkim 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