File: f_color.c

package info (click to toggle)
plotutils 2.6-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, wheezy
  • size: 13,156 kB
  • ctags: 7,141
  • sloc: ansic: 68,670; sh: 20,082; cpp: 12,382; yacc: 2,588; makefile: 889; lex: 137
file content (298 lines) | stat: -rw-r--r-- 11,065 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/* This file is part of the GNU plotutils package.  Copyright (C) 1995,
   1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.

   The GNU plotutils package is free software.  You may redistribute it
   and/or modify it under the terms of the GNU General Public License as
   published by the Free Software foundation; either version 2, or (at your
   option) any later version.

   The GNU plotutils package is distributed in the hope that it will be
   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with the GNU plotutils package; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
   Boston, MA 02110-1301, USA. */

/* This file contains device-specific color database access routines.  They
   are called by various FigPlotter methods, before drawing objects.  They
   set the appropriate FigPlotter-specific fields in the drawing state. */

#include "sys-defines.h"
#include "extern.h"

#define ONEBYTE 0xff

/* by setting this undocumented variable, user may request quantization of
   colors (no user-defined colors, only native xfig ones). */
#ifdef SUPPORT_FIG_COLOR_QUANTIZATION
int _libplotfig_use_pseudocolor = 0;
#endif

/* FIG_COLOR returns the index of the Fig color corresponding to specified
   a 48-bit RGB value.  This has (or may have) a side effect.  If the Fig
   color is not standard, it will be added to the database of user-defined
   colors, and a Fig `color pseudo-object' will be output later.

   Note: according to Fig documentation, xfig should support as many as
   FIG_NUM_USER_COLORS (i.e., 512) user-defined colors.  However, I have
   observed that on at least one platform, it pops up a warning message
   unless the number is 511 or less.  That's why FIG_NUM_USER_COLORS - 1
   appears in the code below.

   We do not call this function whenever the user calls pencolor() or
   fillcolor(), since we don't want to fill up the database with colors
   that the user may not actually use.  Instead, we call it just before we
   write a colored object to the output buffer (lazy evaluation), by
   evaluating the f_set_pen_color() and f_set_fill_color() functions below.

   If the external variable _libplotfig_use_pseudocolor is nonzero, we
   don't actually maintain a database of user-defined colors.  Instead we
   just quantize to one of xfig's native 32 colors.  (They provide a
   [rather strange] partition of the color cube; see f_color2.c.) */

/* forward references */
static int _fig_pseudocolor (int red, int green, int blue, const long int *fig_usercolors, int fig_num_usercolors);

int
_pl_f_fig_color(R___(Plotter *_plotter) int red, int green, int blue)
{
  int fig_fgcolor_red, fig_fgcolor_green, fig_fgcolor_blue;
  long int fig_fgcolor_rgb;
  int i;

  /* xfig supports only 24-bit color, so extract 8 bits for each of R,G,B */
  fig_fgcolor_red = (red >> 8) & ONEBYTE;
  fig_fgcolor_green = (green >> 8) & ONEBYTE;
  fig_fgcolor_blue = (blue >> 8) & ONEBYTE;

#ifdef SUPPORT_FIG_COLOR_QUANTIZATION
  if (_libplotfig_use_pseudocolor)
    /* always quantize: approximate by closest standard color, and don't
       create user-defined colors at all */
    return _fig_pseudocolor (fig_fgcolor_red, fig_fgcolor_green,
			     fig_fgcolor_blue, 
			     (const long int *)NULL, 0);
#endif

  /* search list of standard colors */
  for (i = 0; i < FIG_NUM_STD_COLORS; i++)
    {
      if ((_pl_f_fig_stdcolors[i].red == fig_fgcolor_red)
	  && (_pl_f_fig_stdcolors[i].green == fig_fgcolor_green)
	  && (_pl_f_fig_stdcolors[i].blue == fig_fgcolor_blue))
	/* perfect match, return it */
	return i;
    }

  /* This is the 24-bit (i.e. 3-byte) integer used internally by xfig, and
     also by us when we stored user-defined colors.  We assume long ints
     are wide enough to handle 3 bytes. */
  fig_fgcolor_rgb = (fig_fgcolor_red << 16) + (fig_fgcolor_green << 8)
		    + (fig_fgcolor_blue);
    
  /* search list of user-defined colors */
  for (i = 0; i < _plotter->fig_num_usercolors; i++) 
    {
      if (_plotter->fig_usercolors[i] == fig_fgcolor_rgb)
	/* perfect match, return it */
	return FIG_USER_COLOR_MIN + i;
    }

  /* color wasn't found in either list */

  if (_plotter->fig_num_usercolors == FIG_MAX_NUM_USER_COLORS - 1)
    /* can't add new color to user-defined list, must approximate */
    {
      if (_plotter->fig_colormap_warning_issued == false)
	{
	  _plotter->warning (R___(_plotter) 
			     "supply of user-defined colors is exhausted");
	  _plotter->fig_colormap_warning_issued = true;
	}
      return _fig_pseudocolor (fig_fgcolor_red, fig_fgcolor_green,
			       fig_fgcolor_blue, 
			       _plotter->fig_usercolors,
			       FIG_MAX_NUM_USER_COLORS - 1);
    }
  else
    /* create new user-defined color, will emit it to the .fig file */
    {
      _plotter->fig_usercolors[_plotter->fig_num_usercolors] = fig_fgcolor_rgb;
      _plotter->fig_num_usercolors++;
      return FIG_USER_COLOR_MIN + _plotter->fig_num_usercolors - 1;
    }
}

/* Find closest known point to a specified 24-bit color within the RGB
   color cube, using Euclidean distance as our metric.  We search both
   Fig's standard colors and a specified number of user-defined colors,
   which are stored in an array, a pointer to which is passed.  Return
   value is Fig color index.  Standard Fig colors are located in
   0..FIG_NUM_STD_COLORS-1, and user-defined colors beginning at
   FIG_USER_COLOR_MIN, which is equal to FIG_NUM_STD_COLORS. */

static int
_fig_pseudocolor (int red, int green, int blue, const long int *fig_usercolors, int fig_num_usercolors)
{
  unsigned long int difference = INT_MAX;
  int i;
  int best = 0;
  
  for (i = 0; i < FIG_NUM_STD_COLORS; i++)
    {
      unsigned long int newdifference;
      
      if (_pl_f_fig_stdcolors[i].red == 0xff
	  && _pl_f_fig_stdcolors[i].green == 0xff
	  && _pl_f_fig_stdcolors[i].blue == 0xff)
	/* white is a possible quantization only for white itself (our
           convention) */
	{
	  if (red == 0xff && green == 0xff && blue == 0xff)
	    {
	      difference = 0;
	      best = i;
	    }
	  continue;
	}

      newdifference = (((_pl_f_fig_stdcolors[i].red - red) 
			* (_pl_f_fig_stdcolors[i].red - red))
		       + ((_pl_f_fig_stdcolors[i].green - green) 
			  * (_pl_f_fig_stdcolors[i].green - green))
		       + ((_pl_f_fig_stdcolors[i].blue - blue) 
			  * (_pl_f_fig_stdcolors[i].blue - blue)));
      if (newdifference < difference)
	{
	  difference = newdifference;
	  best = i;		/* save Fig color index */
	}
    }

  /* search through passed array of user-defined colors too */
  for (i = 0; i < fig_num_usercolors; i++)
    {
      unsigned long int newdifference;
      plColor usercolor;
      
      /* extract 3 RGB octets from 24-byte Fig-style color */
      usercolor.red = (fig_usercolors[i] >> 16) & ONEBYTE;
      usercolor.green = (fig_usercolors[i] >> 8) & ONEBYTE;
      usercolor.blue = (fig_usercolors[i] >> 0) & ONEBYTE;

      newdifference = ((usercolor.red - red) * (usercolor.red - red)
		       + (usercolor.green - green) * (usercolor.green - green)
		       + (usercolor.blue - blue) * (usercolor.blue - blue));
      if (newdifference < difference)
	{
	  difference = newdifference;
	  best = i + FIG_USER_COLOR_MIN; /* save Fig color index */
	}
    }

  return best;
}

/* we call this routine to evaluate _plotter->drawstate->fig_fgcolor
   lazily, i.e. only when needed (just before an object is written to the
   output buffer) */
void
_pl_f_set_pen_color(S___(Plotter *_plotter))
{
  /* OOB switches to default color */
  if (((_plotter->drawstate->fgcolor).red > 0xffff) 
      || ((_plotter->drawstate->fgcolor).green > 0xffff) 
      || ((_plotter->drawstate->fgcolor).blue > 0xffff))
    _plotter->drawstate->fig_fgcolor = _default_drawstate.fig_fgcolor;
  else
    _plotter->drawstate->fig_fgcolor = 
      _pl_f_fig_color (R___(_plotter) 
		       (_plotter->drawstate->fgcolor).red,
		       (_plotter->drawstate->fgcolor).green, 
		       (_plotter->drawstate->fgcolor).blue);
  return;
}

/* we call this routine to evaluate _plotter->drawstate->fig_fillcolor and
   _plotter->drawstate->fig_fill_level lazily, i.e. only when needed (just
   before an object is written to the output buffer) */

void
_pl_f_set_fill_color(S___(Plotter *_plotter))
{
  double fill_level;

  /* OOB switches to default color */
  if (_plotter->drawstate->fillcolor_base.red > 0xffff
      || _plotter->drawstate->fillcolor_base.green > 0xffff
      || _plotter->drawstate->fillcolor_base.blue > 0xffff)
    _plotter->drawstate->fig_fillcolor = _default_drawstate.fig_fillcolor;

  else
    _plotter->drawstate->fig_fillcolor = 
      _pl_f_fig_color (R___(_plotter)
		       _plotter->drawstate->fillcolor_base.red,
		       _plotter->drawstate->fillcolor_base.green, 
		       _plotter->drawstate->fillcolor_base.blue);
  
  /* Now that we know drawstate->fig_fillcolor, we can compute the fig fill
     level that will match the user's requested fill level.  Fig fill level
     is interpreted in a color dependent way, as follows.  The value -1 is
     special; means no fill at all (objects will be transparent).  For
     other values, this is the interpretation:
     
     Color = black or default:
     		fill = 0  -> white
		fill = 1  -> very light grey
		     .
		     .
	        fill = 19 -> very dark grey
     		fill = 20 -> black

     Color = all colors other than black or default, including white
   		   fill = 0  -> black
   		   fill = 1  -> color, very faint intensity
			.
			.
		   fill = 19 -> color, very bright intensity
		   fill = 20 -> color, full intensity

     So 1->20 give increasingly intense "shades" of the color, with 20
     giving the color itself.  Values 20->40 are increasingly desaturated
     "tints" of the color, ranging from the color itself (20) to white
     (40).  A tint is defined as the color mixed with white.  (Values
     21->40 are not used when the color is black or default, or white
     itself.) */

  fill_level = ((double)_plotter->drawstate->fill_type - 1.)/0xFFFE;

  /* OOB sets fill level to a non-OOB default value */
  if (fill_level > 1.)
    fill_level = ((double)_default_drawstate.fill_type - 1.)/0xFFFE;

  /* level = 0 turns off filling (objects will be transparent) */
  else if (fill_level < 0.)
    fill_level = -1.0;

  if (fill_level == -1.0)
    _plotter->drawstate->fig_fill_level = -1;
  else
    {
      switch (_plotter->drawstate->fig_fillcolor)
	{
	case FIG_C_WHITE:	/* can't desaturate white */
	  _plotter->drawstate->fig_fill_level = 20;
	  break;
	case FIG_C_BLACK:
	  _plotter->drawstate->fig_fill_level = IROUND(20.0 - 20.0 * fill_level);
	  break;
	default:		/* interpret fill level as a saturation */
	  _plotter->drawstate->fig_fill_level = IROUND(20.0 + 20.0 * fill_level);
	  break;
	}
    }
  return;
}