File: p_color.c

package info (click to toggle)
plotutils 2.4.1-11
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 11,676 kB
  • ctags: 6,967
  • sloc: ansic: 76,305; sh: 15,172; cpp: 12,403; yacc: 2,604; makefile: 888; lex: 144
file content (234 lines) | stat: -rw-r--r-- 8,151 bytes parent folder | download | duplicates (3)
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
/* This file contains device-specific color computation routines.  They are
   called by various PSPlotter methods, before drawing objects.  They set
   the appropriate PSPlotter-specific fields in the drawing state. */

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

/* forward references */
static int _idraw_pseudocolor ____P((int red, int green, int blue));

/* We call this routine to evaluate _plotter->drawstate->ps_fgcolor lazily,
   i.e. only when needed (just before an object is written to the output
   buffer).  It finds the best match from among idraw's "foreground
   colors", i.e., pen colors.  See p_color2.c for the list of colors. */

void
#ifdef _HAVE_PROTOS
_p_set_pen_color(S___(Plotter *_plotter))
#else
_p_set_pen_color(S___(_plotter))
     S___(Plotter *_plotter;)
#endif
{
  _plotter->drawstate->ps_fgcolor_red = 
    ((double)((_plotter->drawstate->fgcolor).red))/0xFFFF;
  _plotter->drawstate->ps_fgcolor_green = 
    ((double)((_plotter->drawstate->fgcolor).green))/0xFFFF;
  _plotter->drawstate->ps_fgcolor_blue = 
    ((double)((_plotter->drawstate->fgcolor).blue))/0xFFFF;

  /* quantize for idraw */
  _plotter->drawstate->ps_idraw_fgcolor = 
    _idraw_pseudocolor ((_plotter->drawstate->fgcolor).red,
			(_plotter->drawstate->fgcolor).green,
			(_plotter->drawstate->fgcolor).blue);

  return;
}

/* We call this routine to evaluate _plotter->drawstate->ps_fillcolor
   lazily, i.e. only when needed (just before an object is written to the
   output buffer).  It finds the best match from among the possible idraw
   colors.  In idraw, the fill color is always an interpolation between a
   "foreground color" and a "background color", both of which are selected
   from a fixed set.  See p_color2.c. */

void
#ifdef _HAVE_PROTOS
_p_set_fill_color(S___(Plotter *_plotter))
#else
_p_set_fill_color(S___(_plotter))
     S___(Plotter *_plotter;)
#endif
{
  double red, green, blue;

  if (_plotter->drawstate->fill_type == 0)
    /* don't do anything, fill color will be ignored when writing objects*/
    return;

  red = ((double)((_plotter->drawstate->fillcolor).red))/0xFFFF;
  green = ((double)((_plotter->drawstate->fillcolor).green))/0xFFFF;
  blue = ((double)((_plotter->drawstate->fillcolor).blue))/0xFFFF;

  _plotter->drawstate->ps_fillcolor_red = red;
  _plotter->drawstate->ps_fillcolor_green = green;
  _plotter->drawstate->ps_fillcolor_blue = blue;

  /* next subroutine needs fields that this will fill in... */
  _p_set_pen_color (S___(_plotter));

  /* Quantize for idraw, in a complicated way; we can choose from among a
     finite discrete set of values for ps_idraw_bgcolor and
     ps_idraw_shading, to approximate the fill color.  We also adjust
     ps_fillcolor_* because the PS interpreter will use the
     ps_idraw_shading variable to interpolate between fgcolor and bgcolor,
     i.e. fgcolor and fillcolor. */
  _p_compute_idraw_bgcolor (S___(_plotter));
  
  return;
}

/* Find, within the RGB color cube, the idraw pen color ("foreground
   color") that is closest to a specified color (our pen color).  Euclidean
   distance is our metric.  Our convention: no non-white color should be
   mapped to white. */
static int
#ifdef _HAVE_PROTOS
_idraw_pseudocolor (int red, int green, int blue)
#else
_idraw_pseudocolor (red, green, blue)
     int red, green, blue;
#endif
{
  double difference;
  int i;
  int best = 0;
  
  difference = DBL_MAX;
  for (i = 0; i < IDRAW_NUM_STD_COLORS; i++)
    {
      double newdifference;
      
      if (_idraw_stdcolors[i].red == 0xffff
	  && _idraw_stdcolors[i].green == 0xffff
	  && _idraw_stdcolors[i].blue == 0xffff)
	/* white is a possible quantization only for white itself (our
           convention) */
	{
	  if (red == 0xffff && green == 0xffff && blue == 0xffff)
	    {
	      difference = 0.0;
	      best = i;
	    }
	  continue;
	}

      newdifference = ((double)(_idraw_stdcolors[i].red - red)
		       * (double)(_idraw_stdcolors[i].red - red))
		    + ((double)(_idraw_stdcolors[i].green - green) 
		       * (double)(_idraw_stdcolors[i].green - green)) 
		    + ((double)(_idraw_stdcolors[i].blue - blue)
		       * (double)(_idraw_stdcolors[i].blue - blue));
      
      if (newdifference < difference)
	{
	  difference = newdifference;
	  best = i;
	}
    }

  return best;
}

/* Once the idraw foreground color (i.e. quantized pen color) has been
   determined, this routine computes the idraw background color and idraw
   shading (0.0, 0.25, 0.5, 0.75, or 1.0) that will most closely match the
   user-specified fill color.  It is called only when the elements
   ps_fillcolor_*, ps_idraw_fgcolor_* of the drawing state have been filled in.

   At the end of this function we adjust ps_fillcolor_* so that the output
   file will produce similar colors when parsed both by idraw and the PS
   interpreter.  In fact we can persuade the PS interpreter to produce
   exactly the fill color specified by the user, except when the idraw
   shading is 0.0.  In that case the fill color must be the same as the pen
   color.  That situation will occur only if the user-specified fill color
   is very close to the user-specified pen color. */

void
#ifdef _HAVE_PROTOS
_p_compute_idraw_bgcolor(S___(Plotter *_plotter))
#else
_p_compute_idraw_bgcolor(S___(_plotter))
     S___(Plotter *_plotter;)
#endif
{
  double truered, truegreen, trueblue;
  double fgred, fggreen, fgblue;
  double difference = DBL_MAX;
  int i, j;
  int best_bgcolor = 0, best_shading = 0;
  double best_shade = 0.0;

  truered = 0xFFFF * _plotter->drawstate->ps_fillcolor_red;
  truegreen = 0xFFFF * _plotter->drawstate->ps_fillcolor_green;
  trueblue = 0xFFFF * _plotter->drawstate->ps_fillcolor_blue;

  fgred = (double)(_idraw_stdcolors[_plotter->drawstate->ps_idraw_fgcolor].red);
  fggreen = (double)(_idraw_stdcolors[_plotter->drawstate->ps_idraw_fgcolor].green);
  fgblue = (double)(_idraw_stdcolors[_plotter->drawstate->ps_idraw_fgcolor].blue);

  for (i = 0; i < IDRAW_NUM_STD_COLORS; i++)
    {
      double bgred, bggreen, bgblue;

      bgred = (double)(_idraw_stdcolors[i].red);
      bggreen = (double)(_idraw_stdcolors[i].green);
      bgblue = (double)(_idraw_stdcolors[i].blue);

      for (j = 0; j < IDRAW_NUM_STD_SHADINGS; j++)
	{
	  double approxred, approxgreen, approxblue;
	  double shade, newdifference;
	  
	  shade = _idraw_stdshadings[j];
	  
	  approxred = shade * bgred + (1.0 - shade) * fgred;
	  approxgreen = shade * bggreen + (1.0 - shade) * fggreen;
	  approxblue = shade * bgblue + (1.0 - shade) * fgblue;	  

	  newdifference = (truered - approxred) * (truered - approxred)
	    + (truegreen - approxgreen) * (truegreen - approxgreen)
	      + (trueblue - approxblue) * (trueblue - approxblue);
	  
	  if (newdifference < difference)
	    {
	      difference = newdifference;
	      best_bgcolor = i;
	      best_shading = j;
	      best_shade = shade;
	    }
	}
    }

  _plotter->drawstate->ps_idraw_bgcolor = best_bgcolor;
  _plotter->drawstate->ps_idraw_shading = best_shading;

  /* now adjust ps_fillcolor_* fields so that interpolation between
     ps_fgcolor_* and ps_fillcolor_*, as specified by the shade, will yield
     the user-specified fill color.  According to the PS prologue, the PS
     interpreter will compute a fill color thus:

     true_FILLCOLOR = shade * PS_FILLCOLOR + (1-shade) * PS_FGCOLOR

     we can compute an adjusted fillcolor thus:

     PS_FILLCOLOR = (true_FILLCOLOR - (1-shade) * PS_FGCOLOR) / shade.

     This is possible unless shade=0.0, in which case both idraw and the PS
     interpreter will use the pen color as the fill color. */

  if (best_shade != 0.0)
    {
      _plotter->drawstate->ps_fillcolor_red 
	= (_plotter->drawstate->ps_fillcolor_red 
	   - (1.0 - best_shade) * _plotter->drawstate->ps_fgcolor_red) / best_shade;
      _plotter->drawstate->ps_fillcolor_green
	= (_plotter->drawstate->ps_fillcolor_green
	   - (1.0 - best_shade) * _plotter->drawstate->ps_fgcolor_green) / best_shade;
      _plotter->drawstate->ps_fillcolor_blue
	= (_plotter->drawstate->ps_fillcolor_blue
	   - (1.0 - best_shade) * _plotter->drawstate->ps_fgcolor_blue) / best_shade;
    }
}