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