Home | History | Annotate | Line # | Download | only in libgroff
      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 (at) 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
     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 
     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 
     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 
     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 
     87 color::~color()
     88 {
     89 }
     90 
     91 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 
    119 int color::operator!=(const color & c) const
    120 {
    121   return !(*this == c);
    122 }
    123 
    124 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 
    137 void color::set_default()
    138 {
    139   scheme = DEFAULT;
    140 }
    141 
    142 // (0, 0, 0) is black
    143 
    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 
    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 
    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 
    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 
    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 
    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 
    237 int color::read_rgb(const char * const s)
    238 {
    239   return read_encoding(RGB, s, 3);
    240 }
    241 
    242 int color::read_cmy(const char * const s)
    243 {
    244   return read_encoding(CMY, s, 3);
    245 }
    246 
    247 int color::read_cmyk(const char * const s)
    248 {
    249   return read_encoding(CMYK, s, 4);
    250 }
    251 
    252 int color::read_gray(const char * const s)
    253 {
    254   return read_encoding(GRAY, s, 1);
    255 }
    256 
    257 void
    258 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
    292 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 
    322 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 
    375 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 
    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