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