LCOV - code coverage report
Current view: top level - libs/libgroff - color.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 80 251 31.9 %
Date: 2026-01-16 17:51:41 Functions: 12 22 54.5 %
Legend: Lines: hit not hit

          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:

Generated by: LCOV version 1.14