1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 #include "sys-defines.h"
20 #include "extern.h"
21 
22 #define ONEBYTE (0xff)
23 
24 #define USE_PEN_ZERO (_plotter->hpgl_version == 2 && (_plotter->hpgl_use_opaque_mode || _plotter->hpgl_can_assign_colors))
25 
26 /* _h_set_pen_color() sets the pen color used by the HP-GL[/2] device to
27    match the pen color in our current drawing state.  It's invoked just
28    before any drawing operation.
29 
30    If the device's palette contains a matching color, the corresponding pen
31    is selected.  Otherwise, we do one of several things.
32 
33    1. If we can add colors to the palette, we add the specified color, and
34    then select the corresponding pen.
35 
36    2. If we can't add colors to the palette, but we can specify a shading
37    level, i.e., a desaturation level, we find the closest point in the RGB
38    cube to the specified color that's a shaded version of one of the colors
39    in the palette.  Then we select it, by both selecting the appropriate
40    pen, and selecting a shading level.
41 
42    There are two subcases to case #2: either the drawing operation to
43    follow this invocation of set_pen_color will draw a path, or it will
44    draw a text string using a font native to the HP-GL interpreter.  In the
45    former case, we use the `SV' (screened vector) instruction to set the
46    shading level; in the latter, we use the `CF' (character fill)
47    instruction to set the shading level.  The two sub-cases are
48    distinguished by a hint that's passed to us, by being placed in the
49    HP-GL-specific part of the drawing state before this function is called.
50 
51    3. If we can't do either (1) or (2), then we search the palette for the
52    closest match, and the corresponding `quantized' pen is selected.  We
53    adopt a convention: nonwhite pen colors are never quantized to white.
54 
55    Pen #0 is the canonical white pen.  But on pen plotters, drawing with
56    pen #0 isn't meaningful.  So we won't actually use pen #0 to draw with,
57    unless HPGL_VERSION==2 and HPGL_OPAQUE_MODE=yes (or
58    HPGL_ASSIGN_COLORS=yes, which presumably means the output is directed to
59    a DesignJet).  If the closest match in case #3 is pen #0 and we won't be
60    using pen #0 to draw with, we set the advisory `hpgl_bad_pen' flag in
61    the Plotter to `true'; otherwise we set it to `false'. */
62 
63 void
_pl_h_set_pen_color(R___ (Plotter * _plotter)int hpgl_object_type)64 _pl_h_set_pen_color(R___(Plotter *_plotter) int hpgl_object_type)
65 {
66   bool found;
67   int longred, longgreen, longblue;
68   int red, green, blue;
69   int i;
70   plColor color;
71 
72   color = _plotter->drawstate->fgcolor;
73   longred = color.red;
74   longgreen = color.green;
75   longblue = color.blue;
76 
77   /* truncate to 24-bit color */
78   red = (longred >> 8) & ONEBYTE;
79   green = (longgreen >> 8) & ONEBYTE;
80   blue = (longblue >> 8) & ONEBYTE;
81 
82   /* Check whether color is in the palette, in which case all we need to do
83      is select it. */
84   found = false;
85   for (i = 0; i < HPGL2_MAX_NUM_PENS; i++)
86     {
87       if (_plotter->hpgl_pen_defined[i] != 0 /* i.e. defined (hard or soft) */
88 	  && _plotter->hpgl_pen_color[i].red == red
89 	  && _plotter->hpgl_pen_color[i].green == green
90 	  && _plotter->hpgl_pen_color[i].blue == blue)
91 	{
92 	  found = true;
93 	  break;
94 	}
95     }
96 
97   if (found)
98     /* Color is in palette: the simplest case.  Besides selecting the
99        corresponding pen, must set pen type to solid, if there's support
100        for altering the pen type via `screening of vectors'; since in that
101        case the pen type could have been set to `shaded' previously.  If a
102        label is to be drawn rather than a path, we must similarly update
103        the character rendition type to `solid fill' rather than `shaded'.  */
104     {
105       if (i != 0 || (i == 0 && USE_PEN_ZERO))
106 	/* can be selected */
107 	{
108 	  /* select the pen */
109 	  _pl_h_set_hpgl_pen (R___(_plotter) i);
110 
111 	  /* in HP-GL/2 case, be sure that `solid' vector screening or
112              character filling is used (one or the other, depending on a
113              hint as to which type of object is to be drawn) */
114 	  switch (hpgl_object_type)
115 	    {
116 	    case HPGL_OBJECT_PATH:
117 	      if (_plotter->hpgl_version == 2
118 		  && _plotter->hpgl_have_screened_vectors == true)
119 		/* set pen type to solid */
120 		_pl_h_set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SOLID,
121 					 /* options ignored */
122 					 0.0, 0.0);
123 	      break;
124 	    case HPGL_OBJECT_LABEL:
125 	      if (_plotter->hpgl_version == 2
126 		  && _plotter->hpgl_have_char_fill == true)
127 		/* if necessary, emit `CF' instruction: specify that
128 		   characters are to be rendered by being filled solid with
129 		   the current pen color without edging, which is the
130 		   default */
131 		if (_plotter->hpgl_char_rendering_type !=
132 		    HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE)
133 		  {
134 		    sprintf (_plotter->data->page->point, "CF;");
135 		    _update_buffer (_plotter->data->page);
136 		    _plotter->hpgl_char_rendering_type =
137 		      HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE;
138 		  }
139 	      break;
140 	    default:
141 	      break;
142 	    }
143 
144 	  _plotter->hpgl_bad_pen = false;
145 	}
146       else
147 	/* won't use pen #0, so set advisory flag */
148 	_plotter->hpgl_bad_pen = true;
149     }
150 
151   else
152     /* color not in palette, must do something */
153     if (_plotter->hpgl_version == 2 && _plotter->hpgl_can_assign_colors)
154       /* CASE #1: can soft-define pen colors (HP-GL/2, presumably a
155 	 DesignJet) */
156       {
157 	/* assign current `free pen' to be the new color */
158 	sprintf (_plotter->data->page->point, "PC%d,%d,%d,%d;",
159 		 _plotter->hpgl_free_pen, red, green, blue);
160 	_update_buffer (_plotter->data->page);
161 	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].red = red;
162 	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].green = green;
163 	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].blue = blue;
164 	_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] = 1; /* soft-def */
165 	/* select pen */
166 	_pl_h_set_hpgl_pen (R___(_plotter) _plotter->hpgl_free_pen);
167 	/* update free pen, i.e. choose next non-hard-defined pen */
168 	do
169 	  _plotter->hpgl_free_pen = (_plotter->hpgl_free_pen + 1) % HPGL2_MAX_NUM_PENS;
170 	while (_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] == 2);
171 
172 	/* in HP-GL/2 case, be sure that `solid' vector screening or
173 	   character filling is used (one or the other, depending on a hint
174 	   as to which type of object is to be drawn) */
175 	switch (hpgl_object_type)
176 	  {
177 	  case HPGL_OBJECT_PATH:
178 	    if (_plotter->hpgl_version == 2
179 		&& _plotter->hpgl_have_screened_vectors == true)
180 	      /* set pen type to solid */
181 	      _pl_h_set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SOLID,
182 				       /* options ignored */
183 				       0.0, 0.0);
184 	    break;
185 	  case HPGL_OBJECT_LABEL:
186 	    if (_plotter->hpgl_version == 2
187 		&& _plotter->hpgl_have_char_fill == true)
188 	      /* if necessary, emit `CF' instruction: specify that
189 		 characters are to be rendered by being filled solid with
190 		 the current pen color without edging, which is the default */
191 	      if (_plotter->hpgl_char_rendering_type !=
192 		  HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE)
193 		{
194 		  sprintf (_plotter->data->page->point, "CF;");
195 		  _update_buffer (_plotter->data->page);
196 		  _plotter->hpgl_char_rendering_type =
197 		    HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE;
198 		}
199 	    break;
200 	  default:
201 	    break;
202 	  }
203 
204 	_plotter->hpgl_bad_pen = false;
205       }
206 
207     else if (_plotter->hpgl_version == 2
208 	     && _plotter->hpgl_have_screened_vectors == true
209 	     && hpgl_object_type == HPGL_OBJECT_PATH)
210       /* CASE #2a: HP-GL/2, and we have a path to draw, according to the
211 	 passed hint; can't soft-define pen colors, but can set a pen
212 	 shading level via the `SV' instruction.  So locate closest point
213 	 in RGB cube that is a desaturated version of one of the defined
214 	 pen colors, and shade at the appropriate level.  */
215       {
216 	double shading;
217 
218 	_pl_h_hpgl_shaded_pseudocolor (R___(_plotter)
219 				       red, green, blue, &i, &shading);
220 
221 	if (i != 0 || (i == 0 && USE_PEN_ZERO))
222 	  /* can be selected */
223 	  {
224 	    /* select the pen */
225 	    _pl_h_set_hpgl_pen (R___(_plotter) i);
226 	    /* set shading level, as a percentage */
227 	    _pl_h_set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SHADED,
228 				     /* 2nd option ignored for HPGL_PEN_SHADED */
229 				     100.0 * shading, 0.0);
230 	    _plotter->hpgl_bad_pen = false;
231 	  }
232 	else
233 	  /* won't use pen #0, so set advisory flag */
234 	  _plotter->hpgl_bad_pen = true;
235       }
236 
237     else if (_plotter->hpgl_version == 2
238 	     && _plotter->hpgl_have_char_fill == true
239 	     && hpgl_object_type == HPGL_OBJECT_LABEL)
240       /* CASE #2b: HP-GL/2, and we have a label to draw, according to the
241 	 passed hint; can't soft-define pen colors, but can set a character
242 	 shading level via the `CF' instruction.  So locate closest point
243 	 in RGB cube that is a desaturated version of one of the defined
244 	 pen colors, and shade at the appropriate level.  */
245       {
246 	double shading;
247 
248 	_pl_h_hpgl_shaded_pseudocolor (R___(_plotter)
249 				       red, green, blue, &i, &shading);
250 
251 	if (i != 0 || (i == 0 && USE_PEN_ZERO))
252 	  /* can be selected */
253 	  {
254 	    /* select the pen */
255 	    _pl_h_set_hpgl_pen (R___(_plotter) i);
256 	    /* if necessary, emit `CF' instruction: specify that characters
257 	       are to be rendered in a non-default way, by being filled
258 	       with the current fill type (without edging) */
259 	    if (_plotter->hpgl_char_rendering_type != HPGL_CHAR_FILL)
260 	      {
261 		sprintf (_plotter->data->page->point, "CF%d;", HPGL_CHAR_FILL);
262 		_update_buffer (_plotter->data->page);
263 		_plotter->hpgl_char_rendering_type = HPGL_CHAR_FILL;
264 	      }
265 	    /* set the fill type to be a shading level (expressed as a
266 	       percentage) */
267 	    _pl_h_set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SHADED,
268 				      100.0 * shading, 0.0); /* 2nd option ignord */
269 	    _plotter->hpgl_bad_pen = false;
270 	  }
271 	else
272 	  /* won't use pen #0, so set advisory flag */
273 	  _plotter->hpgl_bad_pen = true;
274       }
275 
276     else
277       /* CASE #3: we're stuck with a fixed set of pen colors, from which we
278 	 need to choose. [HPGL_VERSION may be "1" (i.e. generic HP-GL) or
279 	 "1.5" (i.e. HP7550A), or "2" (i.e. modern HP-GL/2, but without the
280 	 ability to define a palette).]  So select closest defined pen in
281 	 RGB cube, using Euclidean distance as metric.  Final arg here is
282 	 `true' on account of our convention that a non-white pen color
283 	 [unlike a fill color] is never quantized to white (i.e. to pen
284 	 #0). */
285       {
286 	i = _pl_h_hpgl_pseudocolor (R___(_plotter) red, green, blue, true);
287 	if (i != 0 || (i == 0 && USE_PEN_ZERO))
288 	  /* can be selected */
289 	  {
290 	    /* select the pen */
291 	    _pl_h_set_hpgl_pen (R___(_plotter) i);
292 
293 	    /* do some updating, based on the type of object to be drawn */
294 	    switch (hpgl_object_type)
295 	      {
296 	      case HPGL_OBJECT_PATH:
297 		if (_plotter->hpgl_version == 2
298 		    && _plotter->hpgl_have_screened_vectors == true)
299 		  /* set pen type to solid */
300 		  _pl_h_set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SOLID,
301 					   /* options ignored */
302 					   0.0, 0.0);
303 		break;
304 	      case HPGL_OBJECT_LABEL:
305 		if (_plotter->hpgl_version == 2
306 		    && _plotter->hpgl_have_char_fill == true)
307 		  /* if necessary, emit `CF' instruction: specify that
308 		     characters are to be rendered by being filled solid with
309 		     the current pen color without edging, which is the
310 		     default */
311 		  if (_plotter->hpgl_char_rendering_type !=
312 		      HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE)
313 		    {
314 		      sprintf (_plotter->data->page->point, "CF;");
315 		      _update_buffer (_plotter->data->page);
316 		      _plotter->hpgl_char_rendering_type =
317 			HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE;
318 		    }
319 		break;
320 	      default:
321 		break;
322 	      }
323 
324 	    _plotter->hpgl_bad_pen = false;
325 	  }
326 	else
327 	  /* won't use pen #0, so set advisory flag */
328 	  _plotter->hpgl_bad_pen = true;
329       }
330 }
331 
332 /* _pl_h_set_fill_color() is similar to _pl_h_set_pen_color: it sets the
333    HP-GL pen color (and fill type, if appropriate) to match the fill color
334    in our current drawing state.  It's invoked before any filling
335    operation.
336 
337    (Note that all filling operations will use the polygon buffer, except
338    when we're emitting generic HP-GL [i.e., HPGL_VERSION="1"], which has no
339    polygon buffer and no support for general filling operations.  In that
340    case the only filling operations we perform are the filling of circles
341    and rectangles aligned with the coordinate axes.)
342 
343    There are three cases.
344 
345    (1) An HP-GL/2 device supporting modification of the palette,
346    i.e. `soft-definition' of pen colors. I.e., HPGL_VERSION="2" and
347    HPGL_ASSIGN_COLORS="yes".  We use solid filling, after defining the fill
348    color as a new pen color if necessary.
349 
350    (2) An HP-GL/2 device not supporting modification of the palette, but
351    which do support shading at any specified intensity.  I.e.,
352    HPGL_VERSION="2" and HPGL_ASSIGN_COLORS="no".  We determine which shade
353    of which defined pen is closest to the fill color in the sense of
354    Euclidean distance within the RGB cube.  `Shades' are really
355    desaturations (interpolations between a pen color, and white).
356 
357    (3) An HP7550A-like device or generic HP-GL device, neither of which has
358    firmware support for shading.  I.e., HPGL_VERSION="1.5" or "1".  Such
359    devices do support cross-hatching, though.  So we (a) determine which
360    shade of which defined pen is closest to the fill color in the sense of
361    Euclidean distance within the RGB cube, and (b) select a cross-hatch
362    distance that will emulate this shade.  For this, we use the algorithm
363    that the HP-GL/2 counterpart of the HP7550A, the HP7550B, uses.
364 
365    (WARNING: our selection of cross-hatching includes the setting of the
366    line type to `solid'.  As a consequence, if HPGL_VERSION="1.5" or "1",
367    then `_pl_h_set_fill_color' does not commute with
368    `_pl_h_set_attributes'.  This is taken into account in several places in
369    h_path.c; grep for KLUDGE.)
370 
371    Pen #0 is the canonical white pen.  But on pen plotters, filling with
372    pen #0 isn't meaningful.  So we won't actually use pen #0 to fill with
373    unless HPGL_VERSION==2 and HPGL_OPAQUE_MODE=yes (or
374    HPGL_ASSIGN_COLORS=yes, which presumably means the output is directed to
375    a DesignJet).  Accordingly if the closest match here is pen #0, we set
376    the advisory `hpgl_bad_pen' flag in the Plotter to `true'; otherwise we
377    set it to `false'.  This is just as in set_pen_color() above. */
378 
379 void
_pl_h_set_fill_color(R___ (Plotter * _plotter)bool force_pen_color)380 _pl_h_set_fill_color(R___(Plotter *_plotter) bool force_pen_color)
381 {
382   bool found;
383   int longred, longgreen, longblue;
384   int red, green, blue;
385   int i;
386 
387   if (force_pen_color == false && _plotter->drawstate->fill_type == 0)
388     /* won't be doing filling, so punt */
389     return;
390 
391   /* get 48-bit color; if force_pen_color is set, use pen color
392      instead of fill color */
393   if (force_pen_color)
394     {
395       longred = _plotter->drawstate->fgcolor.red;
396       longgreen = _plotter->drawstate->fgcolor.green;
397       longblue = _plotter->drawstate->fgcolor.blue;
398     }
399   else
400     {
401       longred = _plotter->drawstate->fillcolor.red;
402       longgreen = _plotter->drawstate->fillcolor.green;
403       longblue = _plotter->drawstate->fillcolor.blue;
404     }
405 
406   /* truncate to 24-bit color */
407   red = (longred >> 8) & ONEBYTE;
408   green = (longgreen >> 8) & ONEBYTE;
409   blue = (longblue >> 8) & ONEBYTE;
410 
411   /* check whether color is already in palette, in which case all we need
412      to do is select it (and set fill type to solid) */
413   found = false;
414   for (i = 0; i < HPGL2_MAX_NUM_PENS; i++)
415     {
416       if (_plotter->hpgl_pen_defined[i] != 0 /* i.e. defined (hard or soft) */
417 	  && _plotter->hpgl_pen_color[i].red == red
418 	  && _plotter->hpgl_pen_color[i].green == green
419 	  && _plotter->hpgl_pen_color[i].blue == blue)
420 	{
421 	  found = true;
422 	  break;
423 	}
424     }
425 
426   if (found)
427     /* color is in palette */
428     {
429       if (i != 0 || (i == 0 && USE_PEN_ZERO))
430 	/* can be selected */
431 	{
432 	  /* select it */
433 	  _pl_h_set_hpgl_pen (R___(_plotter) i);
434 	  /* set fill type to solid, unidirectional */
435 	  _pl_h_set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SOLID_UNI,
436 				    0.0, 0.0); /* options ignored */
437 	  _plotter->hpgl_bad_pen = false;
438 	}
439       else
440 	/* aren't using pen #0, so set advisory flag */
441 	_plotter->hpgl_bad_pen = true;
442     }
443 
444   else
445   /* color not in palette, must do something */
446     if (_plotter->hpgl_version == 2 && _plotter->hpgl_can_assign_colors)
447       /* CASE #1: HP-GL/2 and can soft-define pen colors */
448       {
449 	/* assign current `free pen' to be the new color */
450 	sprintf (_plotter->data->page->point, "PC%d,%d,%d,%d;",
451 		 _plotter->hpgl_free_pen, red, green, blue);
452 	_update_buffer (_plotter->data->page);
453 	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].red = red;
454 	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].green = green;
455 	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].blue = blue;
456 	_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] = 1; /* soft-def */
457 	/* select pen */
458 	_pl_h_set_hpgl_pen (R___(_plotter) _plotter->hpgl_free_pen);
459 	/* update free pen, i.e. choose next non-hard-defined pen */
460 	do
461 	  _plotter->hpgl_free_pen = (_plotter->hpgl_free_pen + 1) % HPGL2_MAX_NUM_PENS;
462 	while (_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] == 2);
463 	/* set fill type to solid, unidirectional */
464 	_pl_h_set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SOLID_UNI,
465 				  0.0, 0.0); /* options ignored */
466 
467 	_plotter->hpgl_bad_pen = false;
468       }
469 
470     else if (_plotter->hpgl_version == 2
471 	     && _plotter->hpgl_can_assign_colors == false)
472       /* CASE #2: HP-GL/2, but can't soft-define pen colors; locate closest
473 	 point in RGB cube that is a desaturated version of one of the
474 	 defined pen colors, and fill by shading at the appropriate level */
475       {
476 	double shading;
477 
478 	_pl_h_hpgl_shaded_pseudocolor (R___(_plotter)
479 				       red, green, blue, &i, &shading);
480 
481 	if (i != 0 || (i == 0 && USE_PEN_ZERO))
482 	  /* can be selected */
483 	  {
484 	    _pl_h_set_hpgl_pen (R___(_plotter) i);
485 	    /* shading level in HP-GL/2 is expressed as a percentage */
486 	    _pl_h_set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SHADED,
487 				      100.0 * shading, 0.0); /* 2nd option ignord */
488 	    _plotter->hpgl_bad_pen = false;
489 	  }
490 	else
491 	  /* aren't using pen #0, so set advisory flag */
492 	  _plotter->hpgl_bad_pen = true;
493       }
494 
495     else
496       /* CASE #3: HPGL_VERSION must be "1" (i.e. generic HP-GL) or "1.5"
497 	 (i.e. HP7550A), so (a) determine which shade of which defined pen
498 	 is closest to the fill color in the sense of Euclidean distance
499 	 within the RGB cube, and (b) select a cross-hatch distance that
500 	 will emulate this shade.  For this, we use the algorithm that the
501 	 HP-GL/2 counterpart of the HP7550A, the HP7550B, uses.  As with
502 	 the HP7550B, we use a cross-hatch angle of 45 degrees.  */
503       {
504 	double shading;
505 
506 	_pl_h_hpgl_shaded_pseudocolor (R___(_plotter)
507 				       red, green, blue, &i, &shading);
508 
509 	if (i != 0 && shading > 0.01)
510 	  /* pen can be selected; note that we insist that shading level be
511 	     at least 1%, to avoid silly huge inter-line spacings, and also
512 	     division by zero */
513 	  {
514 	    double interline_distance;
515 
516 	    _pl_h_set_hpgl_pen (R___(_plotter) i);
517 
518 	    /* convert shading fraction to cross-hatch distance */
519 
520 	    /* If w=width of pen, d=distance between lines, and f=fraction,
521 	       then f = (2wd - w^2)/(d^2).  I.e., fd^2 - 2wd +w^2 = 0.
522 	       Relevant solution is d = (w/f) [1 + sqrt(1-f)].
523 
524 	       HP7550B algorithm assume that w = 0.3mm = 12 plotter units,
525 	       which is a standard width for plotter pens.  So that's what
526 	       we use for w also; we call it HPGL_NOMINAL_PEN_WIDTH.
527 
528 	       We specify spacing in native plotter units because that's
529 	       what the HP7550B does.  Its interpretation of shading level
530 	       as crosshatching is entirely independent of the definition
531 	       of user units, the locations of the scaling points, etc. */
532 
533 	    interline_distance
534 	      = HPGL_NOMINAL_PEN_WIDTH * (1.0 + sqrt (1.0 - shading)) /shading;
535 
536 	    _pl_h_set_hpgl_fill_type (R___(_plotter) HPGL_FILL_CROSSHATCHED_LINES,
537 				      interline_distance, 45.0); /* 45 degrees */
538 	    _plotter->hpgl_bad_pen = false;
539 	  }
540 	else
541 	  /* aren't doing any filling (which would be white or near-white),
542 	     so set advisory flag */
543 	  _plotter->hpgl_bad_pen = true;
544       }
545 }
546 
547 /* Low-level routine that emits the HP-GL `SP' instruction to set the pen
548    color by selecting a pen in the palette, by number. */
549 
550 void
_pl_h_set_hpgl_pen(R___ (Plotter * _plotter)int new_pen)551 _pl_h_set_hpgl_pen (R___(Plotter *_plotter) int new_pen)
552 {
553   if (new_pen != _plotter->hpgl_pen) /* need to select new pen */
554     {
555       if (_plotter->hpgl_pendown)
556 	{
557 	  sprintf (_plotter->data->page->point, "PU;");
558 	  _update_buffer (_plotter->data->page);
559 	  _plotter->hpgl_pendown = false;
560 	}
561       sprintf (_plotter->data->page->point, "SP%d;", new_pen);
562       _update_buffer (_plotter->data->page);
563       _plotter->hpgl_pen = new_pen;
564     }
565 }
566 
567 /* Low-level routine for HP-GL/2 only, which emits an `SV' instruction to
568    select not a pen, but rather a `screening type', i.e. an area fill type
569    such as a shading, that will be applied to all pen strokes.  (Nearly all
570    HP-GL/2 devices that aren't pen plotters support `screened vectors'.)
571    This permits accurate matching of user-specified pen colors; see
572    above. */
573 
574 void
_pl_h_set_hpgl_pen_type(R___ (Plotter * _plotter)int new_hpgl_pen_type,double option1,double option2)575 _pl_h_set_hpgl_pen_type (R___(Plotter *_plotter) int new_hpgl_pen_type, double option1, double option2)
576 {
577   if (new_hpgl_pen_type != _plotter->hpgl_pen_type
578       /* in shading case, we store the current shading level in the option1
579 	 field */
580       || (new_hpgl_pen_type == HPGL_PEN_SHADED
581 	  && _plotter->hpgl_pen_option1 != option1)
582       /* in predefined pattern case (there are six cross-hatch patterns
583 	 that are imported from PCL or RTL, each of which has line width 4
584 	 dots and cell size 32x32 dots on a 600dpi printer), we store the
585 	 current pattern type in the option1 field */
586       || (new_hpgl_pen_type == HPGL_PEN_PREDEFINED_CROSSHATCH
587 	  && _plotter->hpgl_pen_option1 != option1))
588     /* need to emit `SV' instruction to change vector screening */
589     {
590       switch (new_hpgl_pen_type)
591 	{
592 	case HPGL_PEN_SOLID:
593 	default:
594 	  /* options ignored */
595 	  sprintf (_plotter->data->page->point, "SV;");
596 	  break;
597 	case HPGL_PEN_SHADED:
598 	  /* option1 is shading level in percent */
599 	  sprintf (_plotter->data->page->point, "SV%d,%.1f;",
600 		   new_hpgl_pen_type, option1);
601 	  /* stash shading level */
602 	  _plotter->hpgl_pen_option1 = option1;
603 	  break;
604 	case HPGL_PEN_PREDEFINED_CROSSHATCH: /* imported from PCL or RTL */
605 	  /* option1 is pattern type, in range 1..6 */
606 	  sprintf (_plotter->data->page->point, "SV%d,%d;",
607 		   new_hpgl_pen_type, IROUND(option1));
608 	  /* stash pattern type */
609 	  _plotter->hpgl_pen_option1 = option1;
610 	  break;
611 	}
612       _update_buffer (_plotter->data->page);
613       _plotter->hpgl_pen_type = new_hpgl_pen_type;
614     }
615 }
616 
617 /* Low-level routine, which emits the HP-GL `FT' instruction to set a `fill
618    type', e.g., a shading or a cross-hatching, that will be applied when
619    doing filling operations.  WARNING: in the case of filling with
620    cross-hatched or parallel lines, this monkeys with the line type (it
621    sets it to `solid'). */
622 
623 void
_pl_h_set_hpgl_fill_type(R___ (Plotter * _plotter)int new_hpgl_fill_type,double option1,double option2)624 _pl_h_set_hpgl_fill_type (R___(Plotter *_plotter) int new_hpgl_fill_type, double option1, double option2)
625 {
626   if (new_hpgl_fill_type != _plotter->hpgl_fill_type
627       /* in shading case, we store the current shading level in the option1
628 	 field */
629       || (new_hpgl_fill_type == HPGL_FILL_SHADED
630 	  && _plotter->hpgl_fill_option1 != option1)
631       /* in cross-hatched or parallel line case, we store the current
632 	 inter-line distance (in plotter units) in the option1 field, and
633 	 and the line angle in the option2 field */
634       || ((new_hpgl_fill_type == HPGL_FILL_CROSSHATCHED_LINES
635 	   || new_hpgl_fill_type == HPGL_FILL_PARALLEL_LINES)
636 	  && (_plotter->hpgl_fill_option1 != option1
637 	      || _plotter->hpgl_fill_option2 != option2))
638       /* in predefined fill pattern case (there are six cross-hatch
639 	 patterns that are imported from PCL or RTL, each of which has line
640 	 width 4 dots and cell size 32x32 dots on a 600dpi printer), we
641 	 store the current pattern type in the option1 field */
642       || (new_hpgl_fill_type == HPGL_FILL_PREDEFINED_CROSSHATCH
643 	  && _plotter->hpgl_fill_option1 != option1))
644     /* need to emit `FT' instruction to change fill type */
645     {
646       switch (new_hpgl_fill_type)
647 	{
648 	case HPGL_FILL_SOLID_BI: /* bidirectional solid fill */
649 	case HPGL_FILL_SOLID_UNI: /* unidirectional solid fill */
650 	default:
651 	  /* options ignored */
652 	  sprintf (_plotter->data->page->point, "FT%d;", new_hpgl_fill_type);
653 	  break;
654 	case HPGL_FILL_SHADED:
655 	  /* option1 is shading level in percent */
656 	  sprintf (_plotter->data->page->point, "FT%d,%.1f;",
657 		   new_hpgl_fill_type, option1);
658 	  /* stash shading level */
659 	  _plotter->hpgl_fill_option1 = option1;
660 	  break;
661 	case HPGL_FILL_CROSSHATCHED_LINES:
662 	case HPGL_FILL_PARALLEL_LINES:
663 	  /* Our convention: option1 is inter-line distance in plotter
664 	     units (option2 is angle of lines).  By emitting `SC' commands,
665 	     we switch from using user units to plotter units, and back
666 	     (for the latter, cf. setup commands in h_openpl.c).  Also, we
667 	     always switch to the solid line type for drawing the lines
668 	     (see warning above). */
669 	  sprintf (_plotter->data->page->point,
670 		   "LT;SC;FT%d,%d,%d;SC%d,%d,%d,%d;",
671 		   new_hpgl_fill_type, IROUND(option1), IROUND(option2),
672 		   IROUND (_plotter->data->xmin), IROUND (_plotter->data->xmax),
673 		   IROUND (_plotter->data->ymin), IROUND (_plotter->data->ymax));
674 	  _plotter->hpgl_line_type = HPGL_L_SOLID;
675 	  /* stash inter-line distance and angle of lines */
676 	  _plotter->hpgl_fill_option1 = option1;
677 	  _plotter->hpgl_fill_option2 = option2;
678 	  break;
679 	case HPGL_FILL_PREDEFINED_CROSSHATCH: /* imported from PCL or RTL */
680 	  /* option1 is pattern type, in range 1..6 */
681 	  sprintf (_plotter->data->page->point, "FT%d,%d;",
682 		   new_hpgl_fill_type, IROUND(option1));
683 	  /* stash pattern type */
684 	  _plotter->hpgl_fill_option1 = option1;
685 	  break;
686 	}
687       _update_buffer (_plotter->data->page);
688       _plotter->hpgl_fill_type = new_hpgl_fill_type;
689     }
690 }
691 
692 /* Find closest point within the RGB color cube that is a defined pen
693    color, using Euclidean distance as our metric.  Final arg, if set,
694    specifies that nonwhite colors should never be quantized to white. */
695 int
_pl_h_hpgl_pseudocolor(R___ (Plotter * _plotter)int red,int green,int blue,bool restrict_white)696 _pl_h_hpgl_pseudocolor (R___(Plotter *_plotter) int red, int green, int blue, bool restrict_white)
697 {
698   unsigned long int difference = INT_MAX;
699   int i;
700   int best = 0;
701 
702   if (red == 0xff && green == 0xff && blue == 0xff)
703     /* white pen */
704     return 0;
705 
706   for (i = (restrict_white ? 1 : 0); i < HPGL2_MAX_NUM_PENS; i++)
707     {
708       if (_plotter->hpgl_pen_defined[i] != 0)
709 	{
710 	  unsigned long int newdifference;
711 	  int ored, ogreen, oblue;
712 
713 	  ored = _plotter->hpgl_pen_color[i].red;
714 	  ogreen = _plotter->hpgl_pen_color[i].green;
715 	  oblue = _plotter->hpgl_pen_color[i].blue;
716 	  newdifference = ((red - ored) * (red - ored)
717 			   + (green - ogreen) * (green - ogreen)
718 			   + (blue - oblue) * (blue - oblue));
719 
720 	  if (newdifference < difference)
721 	    {
722 	      difference = newdifference;
723 	      best = i;
724 	    }
725 	}
726     }
727   return best;
728 }
729 
730 /* Locate closest point in RGB cube that is a desaturated ("shaded")
731    version of one of the defined pen colors, using Euclidean distance as
732    our metric. */
733 void
_pl_h_hpgl_shaded_pseudocolor(R___ (Plotter * _plotter)int red,int green,int blue,int * pen_ptr,double * shading_ptr)734 _pl_h_hpgl_shaded_pseudocolor (R___(Plotter *_plotter) int red, int green, int blue, int *pen_ptr, double *shading_ptr)
735 {
736   int best = 0;
737   int i;
738   double best_shading = 0.0;
739   double difference = INT_MAX;
740   double red_shifted, green_shifted, blue_shifted;
741 
742   /* shift color vector so that it emanates from `white' */
743   red_shifted = (double)(red - 0xff);
744   green_shifted = (double)(green - 0xff);
745   blue_shifted = (double)(blue - 0xff);
746 
747   /* begin with pen #1 */
748   for (i = 1; i < HPGL2_MAX_NUM_PENS; i++)
749     {
750       int ored, ogreen, oblue;
751       double ored_shifted, ogreen_shifted, oblue_shifted;
752       double red_proj_shifted, green_proj_shifted, blue_proj_shifted;
753       double reciprocal_normsquared, dotproduct;
754       double newdifference, shading;
755 
756       /* skip undefined pens */
757       if (_plotter->hpgl_pen_defined[i] == 0)
758 	continue;
759 
760       /* shift each pen color vector so that it emanates from `white' */
761       ored = _plotter->hpgl_pen_color[i].red;
762       ogreen = _plotter->hpgl_pen_color[i].green;
763       oblue = _plotter->hpgl_pen_color[i].blue;
764       /* if luser specified a white pen, skip it to avoid division by 0 */
765       if (ored == 0xff && ogreen == 0xff && oblue == 0xff)
766 	continue;
767       ored_shifted = (double)(ored - 0xff);
768       ogreen_shifted = (double)(ogreen - 0xff);
769       oblue_shifted = (double)(oblue - 0xff);
770 
771       /* project shifted color vector onto shifted pen color vector */
772       reciprocal_normsquared = 1.0 / (ored_shifted * ored_shifted
773 				      + ogreen_shifted * ogreen_shifted
774 				      + oblue_shifted * oblue_shifted);
775       dotproduct = (red_shifted * ored_shifted
776 		    + green_shifted * ogreen_shifted
777 		    + blue_shifted * oblue_shifted);
778       shading = reciprocal_normsquared * dotproduct;
779 
780       red_proj_shifted = shading * ored_shifted;
781       green_proj_shifted = shading * ogreen_shifted;
782       blue_proj_shifted = shading * oblue_shifted;
783 
784       newdifference = (((red_proj_shifted - red_shifted)
785 			* (red_proj_shifted - red_shifted))
786 		       + ((green_proj_shifted - green_shifted)
787 			  * (green_proj_shifted - green_shifted))
788 		       + ((blue_proj_shifted - blue_shifted)
789 			  * (blue_proj_shifted - blue_shifted)));
790 
791       if (newdifference < difference)
792 	{
793 	  difference = newdifference;
794 	  best = i;
795 	  best_shading = shading;
796 	}
797     }
798 
799   /* compensate for roundoff error */
800   if (best_shading <= 0.0)
801     best_shading = 0.0;
802 
803   *pen_ptr = best;
804   *shading_ptr = best_shading;
805 }
806