File: h_color.c

package info (click to toggle)
plotutils 2.4.1-15
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 11,072 kB
  • ctags: 6,952
  • sloc: ansic: 76,305; cpp: 12,402; sh: 8,475; yacc: 2,604; makefile: 894; lex: 144
file content (833 lines) | stat: -rw-r--r-- 30,624 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
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
#include "sys-defines.h"
#include "extern.h"

#define ONEBYTE (0xff)

#define USE_PEN_ZERO (_plotter->hpgl_version == 2 && (_plotter->hpgl_use_opaque_mode || _plotter->hpgl_can_assign_colors))

/* _h_set_pen_color() sets the pen color used by the HP-GL[/2] device to
   match the pen color in our current drawing state.  It's invoked just
   before any drawing operation.

   If the device's palette contains a matching color, the corresponding pen
   is selected.  Otherwise, we do one of several things.

   1. If we can add colors to the palette, we add the specified color, and
   then select the corresponding pen.

   2. If we can't add colors to the palette, but we can specify a shading
   level, i.e., a desaturation level, we find the closest point in the RGB
   cube to the specified color that's a shaded version of one of the colors
   in the palette.  Then we select it, by both selecting the appropriate
   pen, and selecting a shading level.

   There are two subcases to case #2: either the drawing operation to
   follow this invocation of set_pen_color will draw a path, or it will
   draw a text string using a font native to the HP-GL interpreter.  In the
   former case, we use the `SV' (screened vector) instruction to set the
   shading level; in the latter, we use the `CF' (character fill)
   instruction to set the shading level.  The two sub-cases are
   distinguished by a hint that's passed to us, by being placed in the
   HP-GL-specific part of the drawing state before this function is called.

   3. If we can't do either (1) or (2), then we search the palette for the
   closest match, and the corresponding `quantized' pen is selected.  We
   adopt a convention: nonwhite pen colors are never quantized to white.

   Pen #0 is the canonical white pen.  But on pen plotters, drawing with
   pen #0 isn't meaningful.  So we won't actually use pen #0 to draw with,
   unless HPGL_VERSION==2 and HPGL_OPAQUE_MODE=yes (or
   HPGL_ASSIGN_COLORS=yes, which presumably means the output is directed to
   a DesignJet).  If the closest match in case #3 is pen #0 and we won't be
   using pen #0 to draw with, we set the advisory `hpgl_bad_pen' flag in
   the Plotter to `true'; otherwise we set it to `false'. */

void
#ifdef _HAVE_PROTOS
_h_set_pen_color(R___(Plotter *_plotter) int hpgl_object_type)
#else
_h_set_pen_color(R___(_plotter) hpgl_object_type)
     S___(Plotter *_plotter;)
     int hpgl_object_type;
#endif
{
  bool found;
  int longred, longgreen, longblue;
  int red, green, blue;
  int i;
  plColor color;
  
  color = _plotter->drawstate->fgcolor;
  longred = color.red;
  longgreen = color.green;
  longblue = color.blue;

  /* truncate to 24-bit color */
  red = (longred >> 8) & ONEBYTE;
  green = (longgreen >> 8) & ONEBYTE;
  blue = (longblue >> 8) & ONEBYTE;
  
  /* Check whether color is in the palette, in which case all we need to do
     is select it. */
  found = false;
  for (i = 0; i < HPGL2_MAX_NUM_PENS; i++)
    {
      if (_plotter->hpgl_pen_defined[i] != 0 /* i.e. defined (hard or soft) */
	  && _plotter->hpgl_pen_color[i].red == red
	  && _plotter->hpgl_pen_color[i].green == green
	  && _plotter->hpgl_pen_color[i].blue == blue)
	{
	  found = true;
	  break;
	}
    }
      
  if (found)
    /* Color is in palette: the simplest case.  Besides selecting the
       corresponding pen, must set pen type to solid, if there's support
       for altering the pen type via `screening of vectors'; since in that
       case the pen type could have been set to `shaded' previously.  If a
       label is to be drawn rather than a path, we must similarly update
       the character rendition type to `solid fill' rather than `shaded'.  */
    {
      if (i != 0 || (i == 0 && USE_PEN_ZERO))
	/* can be selected */
	{
	  /* select the pen */
	  _set_hpgl_pen (R___(_plotter) i);
	  
	  /* in HP-GL/2 case, be sure that `solid' vector screening or
             character filling is used (one or the other, depending on a
             hint as to which type of object is to be drawn) */
	  switch (hpgl_object_type)
	    {
	    case HPGL_OBJECT_PATH:
	      if (_plotter->hpgl_version == 2 
		  && _plotter->hpgl_have_screened_vectors == true)
		/* set pen type to solid */
		_set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SOLID, 
				    /* options ignored */
				    0.0, 0.0);
	      break;
	    case HPGL_OBJECT_LABEL:
	      if (_plotter->hpgl_version == 2 
		  && _plotter->hpgl_have_char_fill == true)
		/* if necessary, emit `CF' instruction: specify that
		   characters are to be rendered by being filled solid with
		   the current pen color without edging, which is the
		   default */
		if (_plotter->hpgl_char_rendering_type != 
		    HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE)
		  {
		    sprintf (_plotter->data->page->point, "CF;");
		    _update_buffer (_plotter->data->page);
		    _plotter->hpgl_char_rendering_type =
		      HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE;
		  }
	      break;
	    default:
	      break;
	    }
	  
	  _plotter->hpgl_bad_pen = false;
	}
      else
	/* won't use pen #0, so set advisory flag */
	_plotter->hpgl_bad_pen = true;
    }

  else
    /* color not in palette, must do something */
    if (_plotter->hpgl_version == 2 && _plotter->hpgl_can_assign_colors)
      /* CASE #1: can soft-define pen colors (HP-GL/2, presumably a
	 DesignJet) */
      {
	/* assign current `free pen' to be the new color */
	sprintf (_plotter->data->page->point, "PC%d,%d,%d,%d;", 
		 _plotter->hpgl_free_pen, red, green, blue);
	_update_buffer (_plotter->data->page);
	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].red = red;
	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].green = green;
	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].blue = blue;
	_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] = 1; /* soft-def */
	/* select pen */
	_set_hpgl_pen (R___(_plotter) _plotter->hpgl_free_pen);
	/* update free pen, i.e. choose next non-hard-defined pen */
	do
	  _plotter->hpgl_free_pen = (_plotter->hpgl_free_pen + 1) % HPGL2_MAX_NUM_PENS;
	while (_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] == 2);
	
	/* in HP-GL/2 case, be sure that `solid' vector screening or
	   character filling is used (one or the other, depending on a hint
	   as to which type of object is to be drawn) */
	switch (hpgl_object_type)
	  {
	  case HPGL_OBJECT_PATH:
	    if (_plotter->hpgl_version == 2 
		&& _plotter->hpgl_have_screened_vectors == true)
	      /* set pen type to solid */
	      _set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SOLID, 
				/* options ignored */
				  0.0, 0.0);
	    break;
	  case HPGL_OBJECT_LABEL:
	    if (_plotter->hpgl_version == 2 
		&& _plotter->hpgl_have_char_fill == true)
	      /* if necessary, emit `CF' instruction: specify that
		 characters are to be rendered by being filled solid with
		 the current pen color without edging, which is the default */
	      if (_plotter->hpgl_char_rendering_type != 
		  HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE)
		{
		  sprintf (_plotter->data->page->point, "CF;");
		  _update_buffer (_plotter->data->page);
		  _plotter->hpgl_char_rendering_type =
		    HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE;
		}
	    break;
	  default:
	    break;
	  }
	
	_plotter->hpgl_bad_pen = false;
      }
  
    else if (_plotter->hpgl_version == 2 
	     && _plotter->hpgl_have_screened_vectors == true
	     && hpgl_object_type == HPGL_OBJECT_PATH)
      /* CASE #2a: HP-GL/2, and we have a path to draw, according to the
	 passed hint; can't soft-define pen colors, but can set a pen
	 shading level via the `SV' instruction.  So locate closest point
	 in RGB cube that is a desaturated version of one of the defined
	 pen colors, and shade at the appropriate level.  */
      {
	double shading;
	
	_hpgl_shaded_pseudocolor (R___(_plotter) 
				  red, green, blue, &i, &shading);
	
	if (i != 0 || (i == 0 && USE_PEN_ZERO))
	  /* can be selected */
	  {
	    /* select the pen */
	    _set_hpgl_pen (R___(_plotter) i);
	    /* set shading level, as a percentage */
	    _set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SHADED, 
				/* 2nd option ignored for HPGL_PEN_SHADED */
				100.0 * shading, 0.0);
	    _plotter->hpgl_bad_pen = false;
	  }
	else
	  /* won't use pen #0, so set advisory flag */
	  _plotter->hpgl_bad_pen = true;
      }
  
    else if (_plotter->hpgl_version == 2 
	     && _plotter->hpgl_have_char_fill == true
	     && hpgl_object_type == HPGL_OBJECT_LABEL)
      /* CASE #2b: HP-GL/2, and we have a label to draw, according to the
	 passed hint; can't soft-define pen colors, but can set a character
	 shading level via the `CF' instruction.  So locate closest point
	 in RGB cube that is a desaturated version of one of the defined
	 pen colors, and shade at the appropriate level.  */
      {
	double shading;
	
	_hpgl_shaded_pseudocolor (R___(_plotter) 
				  red, green, blue, &i, &shading);
	
	if (i != 0 || (i == 0 && USE_PEN_ZERO))
	  /* can be selected */
	  {
	    /* select the pen */
	    _set_hpgl_pen (R___(_plotter) i);
	    /* if necessary, emit `CF' instruction: specify that characters
	       are to be rendered in a non-default way, by being filled
	       with the current fill type (without edging) */
	    if (_plotter->hpgl_char_rendering_type != HPGL_CHAR_FILL)
	      {
		sprintf (_plotter->data->page->point, "CF%d;", HPGL_CHAR_FILL);
		_update_buffer (_plotter->data->page);
		_plotter->hpgl_char_rendering_type = HPGL_CHAR_FILL;
	      }
	    /* set the fill type to be a shading level (expressed as a
	       percentage) */
	    _set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SHADED, 
				 100.0 * shading, 0.0); /* 2nd option ignord */
	    _plotter->hpgl_bad_pen = false;
	  }
	else
	  /* won't use pen #0, so set advisory flag */
	  _plotter->hpgl_bad_pen = true;
      }
  
    else
      /* CASE #3: we're stuck with a fixed set of pen colors, from which we
	 need to choose. [HPGL_VERSION may be "1" (i.e. generic HP-GL) or
	 "1.5" (i.e. HP7550A), or "2" (i.e. modern HP-GL/2, but without the
	 ability to define a palette).]  So select closest defined pen in
	 RGB cube, using Euclidean distance as metric.  Final arg here is
	 `true' on account of our convention that a non-white pen color
	 [unlike a fill color] is never quantized to white (i.e. to pen
	 #0). */
      {
	i = _hpgl_pseudocolor (R___(_plotter) red, green, blue, true);
	if (i != 0 || (i == 0 && USE_PEN_ZERO))
	  /* can be selected */
	  {
	    /* select the pen */
	    _set_hpgl_pen (R___(_plotter) i);
	    
	    /* do some updating, based on the type of object to be drawn */
	    switch (hpgl_object_type)
	      {
	      case HPGL_OBJECT_PATH:
		if (_plotter->hpgl_version == 2 
		    && _plotter->hpgl_have_screened_vectors == true)
		  /* set pen type to solid */
		  _set_hpgl_pen_type (R___(_plotter) HPGL_PEN_SOLID, 
				/* options ignored */
				      0.0, 0.0);
		break;
	      case HPGL_OBJECT_LABEL:
		if (_plotter->hpgl_version == 2 
		    && _plotter->hpgl_have_char_fill == true)
		  /* if necessary, emit `CF' instruction: specify that
		     characters are to be rendered by being filled solid with
		     the current pen color without edging, which is the
		     default */
		  if (_plotter->hpgl_char_rendering_type != 
		      HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE)
		    {
		      sprintf (_plotter->data->page->point, "CF;");
		      _update_buffer (_plotter->data->page);
		      _plotter->hpgl_char_rendering_type =
			HPGL_CHAR_FILL_SOLID_AND_MAYBE_EDGE;
		    }
		break;
	      default:
		break;
	      }
	    
	    _plotter->hpgl_bad_pen = false;
	  }
	else
	  /* won't use pen #0, so set advisory flag */
	  _plotter->hpgl_bad_pen = true;
      }
}

/* _h_set_fill_color() is similar to _h_set_pen_color: it sets the HP-GL
   pen color (and fill type, if appropriate) to match the fill color in our
   current drawing state.  It's invoked before any filling operation.

   (Note that all filling operations will use the polygon buffer, except
   when we're emitting generic HP-GL [i.e., HPGL_VERSION="1"], which has no
   polygon buffer and no support for general filling operations.  In that
   case the only filling operations we perform are the filling of circles
   and rectangles aligned with the coordinate axes.)

   There are three cases.

   (1) An HP-GL/2 device supporting modification of the palette,
   i.e. `soft-definition' of pen colors. I.e., HPGL_VERSION="2" and
   HPGL_ASSIGN_COLORS="yes".  We use solid filling, after defining the fill
   color as a new pen color if necessary.

   (2) An HP-GL/2 device not supporting modification of the palette, but
   which do support shading at any specified intensity.  I.e.,
   HPGL_VERSION="2" and HPGL_ASSIGN_COLORS="no".  We determine which shade
   of which defined pen is closest to the fill color in the sense of
   Euclidean distance within the RGB cube.  `Shades' are really
   desaturations (interpolations between a pen color, and white).

   (3) An HP7550A-like device or generic HP-GL device, neither of which has
   firmware support for shading.  I.e., HPGL_VERSION="1.5" or "1".  Such
   devices do support cross-hatching, though.  So we (a) determine which
   shade of which defined pen is closest to the fill color in the sense of
   Euclidean distance within the RGB cube, and (b) select a cross-hatch
   distance that will emulate this shade.  For this, we use the algorithm
   that the HP-GL/2 counterpart of the HP7550A, the HP7550B, uses.

   (WARNING: our selection of cross-hatching includes the setting of the
   line type to `solid'.  As a consequence, if HPGL_VERSION="1.5" or "1",
   then `_h_set_fill_color' does not commute with `_h_set_attributes'.
   This is taken into account in several places in h_path.c; grep for
   KLUDGE.)

   Pen #0 is the canonical white pen.  But on pen plotters, filling with
   pen #0 isn't meaningful.  So we won't actually use pen #0 to fill with
   unless HPGL_VERSION==2 and HPGL_OPAQUE_MODE=yes (or
   HPGL_ASSIGN_COLORS=yes, which presumably means the output is directed to
   a DesignJet).  Accordingly if the closest match here is pen #0, we set
   the advisory `hpgl_bad_pen' flag in the Plotter to `true'; otherwise we
   set it to `false'.  This is just as in set_pen_color() above. */

void
#ifdef _HAVE_PROTOS
_h_set_fill_color(R___(Plotter *_plotter) bool force_pen_color)
#else
_h_set_fill_color(R___(_plotter) force_pen_color)
     S___(Plotter *_plotter;)
     bool force_pen_color;
#endif
{
  bool found;
  int longred, longgreen, longblue;
  int red, green, blue;
  int i;
  
  if (force_pen_color == false && _plotter->drawstate->fill_type == 0) 
    /* won't be doing filling, so punt */
    return;

  /* get 48-bit color; if force_pen_color is set, use pen color
     instead of fill color */
  if (force_pen_color)
    {
      longred = _plotter->drawstate->fgcolor.red;
      longgreen = _plotter->drawstate->fgcolor.green;
      longblue = _plotter->drawstate->fgcolor.blue;
    }
  else
    {
      longred = _plotter->drawstate->fillcolor.red;
      longgreen = _plotter->drawstate->fillcolor.green;
      longblue = _plotter->drawstate->fillcolor.blue;
    }

  /* truncate to 24-bit color */
  red = (longred >> 8) & ONEBYTE;
  green = (longgreen >> 8) & ONEBYTE;
  blue = (longblue >> 8) & ONEBYTE;
  
  /* check whether color is already in palette, in which case all we need
     to do is select it (and set fill type to solid) */
  found = false;
  for (i = 0; i < HPGL2_MAX_NUM_PENS; i++)
    {
      if (_plotter->hpgl_pen_defined[i] != 0 /* i.e. defined (hard or soft) */
	  && _plotter->hpgl_pen_color[i].red == red
	  && _plotter->hpgl_pen_color[i].green == green
	  && _plotter->hpgl_pen_color[i].blue == blue)
	{
	  found = true;
	  break;
	}
    }
      
  if (found)
    /* color is in palette */
    {
      if (i != 0 || (i == 0 && USE_PEN_ZERO))
	/* can be selected */
	{
	  /* select it */
	  _set_hpgl_pen (R___(_plotter) i);
	  /* set fill type to solid, unidirectional */
	  _set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SOLID_UNI, 
			       0.0, 0.0); /* options ignored */
	  _plotter->hpgl_bad_pen = false;
	}
      else
	/* aren't using pen #0, so set advisory flag */
	_plotter->hpgl_bad_pen = true;
    }

  else
  /* color not in palette, must do something */
    if (_plotter->hpgl_version == 2 && _plotter->hpgl_can_assign_colors)
      /* CASE #1: HP-GL/2 and can soft-define pen colors */
      {
	/* assign current `free pen' to be the new color */
	sprintf (_plotter->data->page->point, "PC%d,%d,%d,%d;", 
		 _plotter->hpgl_free_pen, red, green, blue);
	_update_buffer (_plotter->data->page);
	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].red = red;
	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].green = green;
	_plotter->hpgl_pen_color[_plotter->hpgl_free_pen].blue = blue;
	_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] = 1; /* soft-def */
	/* select pen */
	_set_hpgl_pen (R___(_plotter) _plotter->hpgl_free_pen);
	/* update free pen, i.e. choose next non-hard-defined pen */
	do
	  _plotter->hpgl_free_pen = (_plotter->hpgl_free_pen + 1) % HPGL2_MAX_NUM_PENS;
	while (_plotter->hpgl_pen_defined[_plotter->hpgl_free_pen] == 2);
	/* set fill type to solid, unidirectional */
	_set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SOLID_UNI, 
			     0.0, 0.0); /* options ignored */
	
	_plotter->hpgl_bad_pen = false;
      }

    else if (_plotter->hpgl_version == 2 
	     && _plotter->hpgl_can_assign_colors == false)
      /* CASE #2: HP-GL/2, but can't soft-define pen colors; locate closest
	 point in RGB cube that is a desaturated version of one of the
	 defined pen colors, and fill by shading at the appropriate level */
      {
	double shading;
	
	_hpgl_shaded_pseudocolor (R___(_plotter) 
				  red, green, blue, &i, &shading);
	
	if (i != 0 || (i == 0 && USE_PEN_ZERO))
	  /* can be selected */
	  {
	    _set_hpgl_pen (R___(_plotter) i);
	    /* shading level in HP-GL/2 is expressed as a percentage */
	    _set_hpgl_fill_type (R___(_plotter) HPGL_FILL_SHADED, 
				 100.0 * shading, 0.0); /* 2nd option ignord */
	    _plotter->hpgl_bad_pen = false;
	  }
	else
	  /* aren't using pen #0, so set advisory flag */
	  _plotter->hpgl_bad_pen = true;
      }

    else
      /* CASE #3: HPGL_VERSION must be "1" (i.e. generic HP-GL) or "1.5"
	 (i.e. HP7550A), so (a) determine which shade of which defined pen
	 is closest to the fill color in the sense of Euclidean distance
	 within the RGB cube, and (b) select a cross-hatch distance that
	 will emulate this shade.  For this, we use the algorithm that the
	 HP-GL/2 counterpart of the HP7550A, the HP7550B, uses.  As with
	 the HP7550B, we use a cross-hatch angle of 45 degrees.  */
      {
	double shading;
	
	_hpgl_shaded_pseudocolor (R___(_plotter) 
				  red, green, blue, &i, &shading);
	
	if (i != 0 && shading > 0.01)
	  /* pen can be selected; note that we insist that shading level be
	     at least 1%, to avoid silly huge inter-line spacings, and also
	     division by zero */
	  {
	    double interline_distance;
	    
	    _set_hpgl_pen (R___(_plotter) i);
	    
	    /* convert shading fraction to cross-hatch distance */
	    
	    /* If w=width of pen, d=distance between lines, and f=fraction,
	       then f = (2wd - w^2)/(d^2).  I.e., fd^2 - 2wd +w^2 = 0.
	       Relevant solution is d = (w/f) [1 + sqrt(1-f)].
	       
	       HP7550B algorithm assume that w = 0.3mm = 12 plotter units,
	       which is a standard width for plotter pens.  So that's what
	       we use for w also; we call it HPGL_NOMINAL_PEN_WIDTH.
	       
	       We specify spacing in native plotter units because that's
	       what the HP7550B does.  Its interpretation of shading level
	       as crosshatching is entirely independent of the definition
	       of user units, the locations of the scaling points, etc. */

	    interline_distance 
	      = HPGL_NOMINAL_PEN_WIDTH * (1.0 + sqrt (1.0 - shading)) /shading;
	    
	    _set_hpgl_fill_type (R___(_plotter) HPGL_FILL_CROSSHATCHED_LINES, 
				 interline_distance, 45.0); /* 45 degrees */
	    _plotter->hpgl_bad_pen = false;
	  }
	else
	  /* aren't doing any filling (which would be white or near-white),
	     so set advisory flag */
	  _plotter->hpgl_bad_pen = true;
      }
}

/* Low-level routine that emits the HP-GL `SP' instruction to set the pen
   color by selecting a pen in the palette, by number. */

void 
#ifdef _HAVE_PROTOS
_set_hpgl_pen (R___(Plotter *_plotter) int new_pen)
#else
_set_hpgl_pen (R___(_plotter) new_pen)
     S___(Plotter *_plotter;)
     int new_pen;
#endif
{
  if (new_pen != _plotter->hpgl_pen) /* need to select new pen */
    {
      if (_plotter->hpgl_pendown)
	{
	  sprintf (_plotter->data->page->point, "PU;");
	  _update_buffer (_plotter->data->page);
	  _plotter->hpgl_pendown = false;
	}
      sprintf (_plotter->data->page->point, "SP%d;", new_pen);
      _update_buffer (_plotter->data->page);
      _plotter->hpgl_pen = new_pen;
    }
}

/* Low-level routine for HP-GL/2 only, which emits an `SV' instruction to
   select not a pen, but rather a `screening type', i.e. an area fill type
   such as a shading, that will be applied to all pen strokes.  (Nearly all
   HP-GL/2 devices that aren't pen plotters support `screened vectors'.)
   This permits accurate matching of user-specified pen colors; see
   above. */

void
#ifdef _HAVE_PROTOS
_set_hpgl_pen_type (R___(Plotter *_plotter) int new_hpgl_pen_type, double option1, double option2)
#else
_set_hpgl_pen_type (R___(_plotter) new_hpgl_pen_type, option1, option2)
     S___(Plotter *_plotter;)
     int new_hpgl_pen_type;
     double option1, option2;
#endif
{
  if (new_hpgl_pen_type != _plotter->hpgl_pen_type
      /* in shading case, we store the current shading level in the option1
	 field */
      || (new_hpgl_pen_type == HPGL_PEN_SHADED 
	  && _plotter->hpgl_pen_option1 != option1)
      /* in predefined pattern case (there are six cross-hatch patterns
	 that are imported from PCL or RTL, each of which has line width 4
	 dots and cell size 32x32 dots on a 600dpi printer), we store the
	 current pattern type in the option1 field */
      || (new_hpgl_pen_type == HPGL_PEN_PREDEFINED_CROSSHATCH
	  && _plotter->hpgl_pen_option1 != option1))
    /* need to emit `SV' instruction to change vector screening */
    {
      switch (new_hpgl_pen_type)
	{
	case HPGL_PEN_SOLID:
	default:
	  /* options ignored */
	  sprintf (_plotter->data->page->point, "SV;");
	  break;
	case HPGL_PEN_SHADED:
	  /* option1 is shading level in percent */
	  sprintf (_plotter->data->page->point, "SV%d,%.1f;", 
		   new_hpgl_pen_type, option1);
	  /* stash shading level */
	  _plotter->hpgl_pen_option1 = option1;
	  break;
	case HPGL_PEN_PREDEFINED_CROSSHATCH: /* imported from PCL or RTL */
	  /* option1 is pattern type, in range 1..6 */
	  sprintf (_plotter->data->page->point, "SV%d,%d;",
		   new_hpgl_pen_type, IROUND(option1));
	  /* stash pattern type */
	  _plotter->hpgl_pen_option1 = option1;
	  break;
	}
      _update_buffer (_plotter->data->page);
      _plotter->hpgl_pen_type = new_hpgl_pen_type;
    }
}

/* Low-level routine, which emits the HP-GL `FT' instruction to set a `fill
   type', e.g., a shading or a cross-hatching, that will be applied when
   doing filling operations.  WARNING: in the case of filling with
   cross-hatched or parallel lines, this monkeys with the line type (it
   sets it to `solid'). */

void
#ifdef _HAVE_PROTOS
_set_hpgl_fill_type (R___(Plotter *_plotter) int new_hpgl_fill_type, double option1, double option2)
#else
_set_hpgl_fill_type (R___(_plotter) new_hpgl_fill_type, option1, option2)
     S___(Plotter *_plotter;)
     int new_hpgl_fill_type;
     double option1, option2;
#endif
{
  if (new_hpgl_fill_type != _plotter->hpgl_fill_type
      /* in shading case, we store the current shading level in the option1
	 field */
      || (new_hpgl_fill_type == HPGL_FILL_SHADED 
	  && _plotter->hpgl_fill_option1 != option1)
      /* in cross-hatched or parallel line case, we store the current
	 inter-line distance (in plotter units) in the option1 field, and
	 and the line angle in the option2 field */
      || ((new_hpgl_fill_type == HPGL_FILL_CROSSHATCHED_LINES
	   || new_hpgl_fill_type == HPGL_FILL_PARALLEL_LINES)
	  && (_plotter->hpgl_fill_option1 != option1
	      || _plotter->hpgl_fill_option2 != option2))
      /* in predefined fill pattern case (there are six cross-hatch
	 patterns that are imported from PCL or RTL, each of which has line
	 width 4 dots and cell size 32x32 dots on a 600dpi printer), we
	 store the current pattern type in the option1 field */
      || (new_hpgl_fill_type == HPGL_FILL_PREDEFINED_CROSSHATCH
	  && _plotter->hpgl_fill_option1 != option1))
    /* need to emit `FT' instruction to change fill type */
    {
      switch (new_hpgl_fill_type)
	{
	case HPGL_FILL_SOLID_BI: /* bidirectional solid fill */
	case HPGL_FILL_SOLID_UNI: /* unidirectional solid fill */
	default:
	  /* options ignored */
	  sprintf (_plotter->data->page->point, "FT%d;", new_hpgl_fill_type);
	  break;
	case HPGL_FILL_SHADED:
	  /* option1 is shading level in percent */
	  sprintf (_plotter->data->page->point, "FT%d,%.1f;", 
		   new_hpgl_fill_type, option1);
	  /* stash shading level */
	  _plotter->hpgl_fill_option1 = option1;
	  break;
	case HPGL_FILL_CROSSHATCHED_LINES:
	case HPGL_FILL_PARALLEL_LINES:
	  /* Our convention: option1 is inter-line distance in plotter
	     units (option2 is angle of lines).  By emitting `SC' commands,
	     we switch from using user units to plotter units, and back
	     (for the latter, cf. setup commands in h_openpl.c).  Also, we
	     always switch to the solid line type for drawing the lines
	     (see warning above). */
	  sprintf (_plotter->data->page->point, 
		   "LT;SC;FT%d,%d,%d;SC%d,%d,%d,%d;",
		   new_hpgl_fill_type, IROUND(option1), IROUND(option2),
		   IROUND (_plotter->data->xmin), IROUND (_plotter->data->xmax), 
		   IROUND (_plotter->data->ymin), IROUND (_plotter->data->ymax));
	  _plotter->hpgl_line_type = HPGL_L_SOLID;
	  /* stash inter-line distance and angle of lines */
	  _plotter->hpgl_fill_option1 = option1;
	  _plotter->hpgl_fill_option2 = option2;
	  break;
	case HPGL_FILL_PREDEFINED_CROSSHATCH: /* imported from PCL or RTL */
	  /* option1 is pattern type, in range 1..6 */
	  sprintf (_plotter->data->page->point, "FT%d,%d;",
		   new_hpgl_fill_type, IROUND(option1));
	  /* stash pattern type */
	  _plotter->hpgl_fill_option1 = option1;
	  break;
	}
      _update_buffer (_plotter->data->page);
      _plotter->hpgl_fill_type = new_hpgl_fill_type;
    }
}

/* Find closest point within the RGB color cube that is a defined pen
   color, using Euclidean distance as our metric.  Final arg, if set,
   specifies that nonwhite colors should never be quantized to white. */
int
#ifdef _HAVE_PROTOS
_hpgl_pseudocolor (R___(Plotter *_plotter) int red, int green, int blue, bool restrict_white)
#else
_hpgl_pseudocolor (R___(_plotter) red, green, blue, restrict_white)
     S___(Plotter *_plotter;)
     int red, green, blue;
     bool restrict_white;
#endif
{
  unsigned long int difference = INT_MAX;
  int i;
  int best = 0;

  if (red == 0xff && green == 0xff && blue == 0xff)
    /* white pen */
    return 0;

  for (i = (restrict_white ? 1 : 0); i < HPGL2_MAX_NUM_PENS; i++)
    {
      if (_plotter->hpgl_pen_defined[i] != 0)
	{
	  unsigned long int newdifference;
	  int ored, ogreen, oblue;
	  
	  ored = _plotter->hpgl_pen_color[i].red;
	  ogreen = _plotter->hpgl_pen_color[i].green;
	  oblue = _plotter->hpgl_pen_color[i].blue;
	  newdifference = ((red - ored) * (red - ored)
			   + (green - ogreen) * (green - ogreen)
			   + (blue - oblue) * (blue - oblue));
	  
	  if (newdifference < difference)
	    {
	      difference = newdifference;
	      best = i;
	    }
	}
    }
  return best;
}

/* Locate closest point in RGB cube that is a desaturated ("shaded")
   version of one of the defined pen colors, using Euclidean distance as
   our metric. */
void
#ifdef _HAVE_PROTOS
_hpgl_shaded_pseudocolor (R___(Plotter *_plotter) int red, int green, int blue, int *pen_ptr, double *shading_ptr)
#else
_hpgl_shaded_pseudocolor (R___(_plotter) red, green, blue, pen_ptr, shading_ptr)
     S___(Plotter *_plotter;)
     int red, green, blue;
     int *pen_ptr;
     double *shading_ptr;
#endif
{
  int best = 0;
  int i;
  double best_shading = 0.0;
  double difference = INT_MAX;
  double red_shifted, green_shifted, blue_shifted;
  
  /* shift color vector so that it emanates from `white' */
  red_shifted = (double)(red - 0xff);
  green_shifted = (double)(green - 0xff);
  blue_shifted = (double)(blue - 0xff);

  /* begin with pen #1 */
  for (i = 1; i < HPGL2_MAX_NUM_PENS; i++)
    {
      int ored, ogreen, oblue;
      double ored_shifted, ogreen_shifted, oblue_shifted;
      double red_proj_shifted, green_proj_shifted, blue_proj_shifted;
      double reciprocal_normsquared, dotproduct;
      double newdifference, shading;
      
      /* skip undefined pens */
      if (_plotter->hpgl_pen_defined[i] == 0)
	continue;
      
      /* shift each pen color vector so that it emanates from `white' */
      ored = _plotter->hpgl_pen_color[i].red;
      ogreen = _plotter->hpgl_pen_color[i].green;
      oblue = _plotter->hpgl_pen_color[i].blue;
      /* if luser specified a white pen, skip it to avoid division by 0 */
      if (ored == 0xff && ogreen == 0xff && oblue == 0xff)
	continue;
      ored_shifted = (double)(ored - 0xff);
      ogreen_shifted = (double)(ogreen - 0xff);
      oblue_shifted = (double)(oblue - 0xff);

      /* project shifted color vector onto shifted pen color vector */
      reciprocal_normsquared = 1.0 / (ored_shifted * ored_shifted
				      + ogreen_shifted * ogreen_shifted
				      + oblue_shifted * oblue_shifted);
      dotproduct = (red_shifted * ored_shifted
		    + green_shifted * ogreen_shifted
		    + blue_shifted * oblue_shifted);
      shading = reciprocal_normsquared * dotproduct;
      
      red_proj_shifted = shading * ored_shifted;
      green_proj_shifted = shading * ogreen_shifted;
      blue_proj_shifted = shading * oblue_shifted;
      
      newdifference = (((red_proj_shifted - red_shifted) 
			* (red_proj_shifted - red_shifted))
		       + ((green_proj_shifted - green_shifted) 
			  * (green_proj_shifted - green_shifted))
		       + ((blue_proj_shifted - blue_shifted) 
			  * (blue_proj_shifted - blue_shifted)));
      
      if (newdifference < difference)
	{
	  difference = newdifference;
	  best = i;
	  best_shading = shading;
	}
    }

  /* compensate for roundoff error */
  if (best_shading <= 0.0)
    best_shading = 0.0;

  *pen_ptr = best;
  *shading_ptr = best_shading;
}