]> git.ipfire.org Git - thirdparty/cups.git/blob - pstoraster/gxclpath.c
Import cups.org releases
[thirdparty/cups.git] / pstoraster / gxclpath.c
1 /* Copyright (C) 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
2
3 This file is part of GNU Ghostscript.
4
5 GNU Ghostscript is distributed in the hope that it will be useful, but
6 WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
7 to anyone for the consequences of using it or for whether it serves any
8 particular purpose or works at all, unless he says so in writing. Refer
9 to the GNU General Public License for full details.
10
11 Everyone is granted permission to copy, modify and redistribute GNU
12 Ghostscript, but only under the conditions described in the GNU General
13 Public License. A copy of this license is supposed to have been given
14 to you along with GNU Ghostscript so you can know your rights and
15 responsibilities. It should be in a file named COPYING. Among other
16 things, the copyright notice and this notice must be preserved on all
17 copies.
18
19 Aladdin Enterprises supports the work of the GNU Project, but is not
20 affiliated with the Free Software Foundation or the GNU Project. GNU
21 Ghostscript, as distributed by Aladdin Enterprises, does not require any
22 GNU software to build or run it.
23 */
24
25 /*$Id$ */
26 /* Higher-level path operations for band lists */
27 #include "math_.h"
28 #include "memory_.h"
29 #include "gx.h"
30 #include "gpcheck.h"
31 #include "gserrors.h"
32 #include "gxdevice.h"
33 #include "gxdevmem.h" /* must precede gxcldev.h */
34 #include "gxcldev.h"
35 #include "gxclpath.h"
36 #include "gxcolor2.h"
37 #include "gxpaint.h" /* for gx_fill/stroke_params */
38 #include "gzpath.h"
39 #include "gzcpath.h"
40
41 /* Statistics */
42 #ifdef DEBUG
43 ulong stats_cmd_diffs[5];
44 #endif
45
46 /* Forward declarations */
47 private int cmd_put_path(P8(gx_device_clist_writer * cldev,
48 gx_clist_state * pcls, const gx_path * ppath, fixed ymin, fixed ymax, byte op,
49 bool implicit_close, segment_notes keep_notes));
50
51 /* ------ Utilities ------ */
52
53 /* Write out the color for filling, stroking, or masking. */
54 /* We should be able to share this with clist_tile_rectangle, */
55 /* but I don't see how to do it without adding a level of procedure. */
56 int
57 cmd_put_drawing_color(gx_device_clist_writer * cldev, gx_clist_state * pcls,
58 const gx_drawing_color * pdcolor)
59 {
60 const gx_strip_bitmap *tile;
61 gx_color_index color0, color1;
62 ulong offset_temp;
63
64 if (gx_dc_is_pure(pdcolor)) {
65 gx_color_index color1 = gx_dc_pure_color(pdcolor);
66
67 if (color1 != pcls->colors[1]) {
68 int code = cmd_set_color1(cldev, pcls, color1);
69
70 if (code < 0)
71 return code;
72 }
73 return cmd_dc_type_pure;
74 }
75 /* Any non-pure color will require the phase. */
76 {
77 int px = pdcolor->phase.x, py = pdcolor->phase.y;
78
79 if (px != pcls->tile_phase.x || py != pcls->tile_phase.y) {
80 int code = cmd_set_tile_phase(cldev, pcls, px, py);
81
82 if (code < 0)
83 return code;
84 }
85 }
86 if (gx_dc_is_binary_halftone(pdcolor)) {
87 tile = gx_dc_binary_tile(pdcolor);
88 color0 = gx_dc_binary_color0(pdcolor);
89 color1 = gx_dc_binary_color1(pdcolor);
90 /* Set up tile and colors as for clist_tile_rectangle. */
91 if (!cls_has_tile_id(cldev, pcls, tile->id, offset_temp)) {
92 int depth =
93 (color1 == gx_no_color_index &&
94 color0 == gx_no_color_index ?
95 cldev->color_info.depth : 1);
96
97 if (tile->id == gx_no_bitmap_id ||
98 clist_change_tile(cldev, pcls, tile, depth) < 0
99 )
100 return_error(-1); /* can't cache tile */
101 }
102 if (color1 != pcls->tile_colors[1] ||
103 color0 != pcls->tile_colors[0]
104 ) {
105 int code = cmd_set_tile_colors(cldev, pcls, color0, color1);
106
107 if (code < 0)
108 return code;
109 }
110 return cmd_dc_type_ht;
111 } else if (gx_dc_is_colored_halftone(pdcolor)) {
112 const gx_device_halftone *pdht = pdcolor->colors.colored.c_ht;
113 int num_comp = pdht->num_comp;
114 byte buf[4 + 4 * cmd_max_intsize(sizeof(pdcolor->colors.colored.c_level[0]))];
115 byte *bp = buf;
116 int i;
117 uint short_bases = 0;
118 ulong bases = 0;
119 byte *dp;
120 int code;
121
122 /****** HOW TO TELL IF COLOR IS ALREADY SET? ******/
123 if (pdht->id != cldev->device_halftone_id) {
124 int code = cmd_put_halftone(cldev, pdht, pdht->type);
125
126 if (code < 0)
127 return code;
128 cldev->device_halftone_id = pdht->id;
129 }
130 for (i = 0; i < num_comp; ++i) {
131 uint base = pdcolor->colors.colored.c_base[i];
132
133 if (base > 31)
134 return_error(gs_error_rangecheck);
135 bases |= base << ((3 - i) * 5);
136 short_bases |= base << (3 - i);
137 }
138 if (bases & 0xf7bde) { /* Some base value requires more than 1 bit. */
139 *bp++ = 0x10 + (byte) (bases >> 16);
140 *bp++ = (byte) (bases >> 8);
141 *bp++ = (byte) bases;
142 } else { /* The bases all fit in 1 bit each. */
143 *bp++ = 0x00 + (byte) short_bases;
144 }
145 for (i = 0; i < num_comp; ++i)
146 bp = cmd_put_w((uint) pdcolor->colors.colored.c_level[i], bp);
147 /****** IGNORE alpha ******/
148 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color, bp - buf + 1);
149 if (code < 0)
150 return code;
151 memcpy(dp + 1, buf, bp - buf);
152 return cmd_dc_type_color;
153 } else
154 return_error(-1);
155 }
156
157 /* Clear (a) specific 'known' flag(s) for all bands. */
158 /* We must do this whenever the value of a 'known' parameter changes. */
159 void
160 cmd_clear_known(gx_device_clist_writer * cldev, uint known)
161 {
162 ushort unknown = ~known;
163 gx_clist_state *pcls = cldev->states;
164 int i;
165
166 for (i = cldev->nbands - 1; i >= 0; i --, pcls ++)
167 pcls->known &= unknown;
168 }
169
170 /* Check whether we need to change the clipping path in the device. */
171 bool
172 cmd_check_clip_path(gx_device_clist_writer * cldev, const gx_clip_path * pcpath)
173 {
174 if (pcpath == NULL)
175 return false;
176 /* The clip path might have moved in memory, so even if the */
177 /* ids match, update the pointer. */
178 cldev->clip_path = pcpath;
179 if (pcpath->id == cldev->clip_path_id)
180 return false;
181 cldev->clip_path_id = pcpath->id;
182 return true;
183 }
184
185 /* Construct the parameters for writing out a matrix. */
186 /* We need a buffer of at least 1 + 6 * sizeof(float) bytes. */
187 byte *
188 cmd_for_matrix(byte * cbuf, const gs_matrix * pmat)
189 {
190 byte *cp = cbuf + 1;
191 byte b = 0;
192 float coeffs[6];
193 int i;
194
195 coeffs[0] = pmat->xx;
196 coeffs[1] = pmat->xy;
197 coeffs[2] = pmat->yx;
198 coeffs[3] = pmat->yy;
199 coeffs[4] = pmat->tx;
200 coeffs[5] = pmat->ty;
201 for (i = 0; i < 4; i += 2) {
202 float u = coeffs[i], v = coeffs[i ^ 3];
203
204 b <<= 2;
205 if (u != 0 || v != 0) {
206 memcpy(cp, &u, sizeof(float));
207 cp += sizeof(float);
208
209 if (v == u)
210 b += 1;
211 else if (v == -u)
212 b += 2;
213 else {
214 b += 3;
215 memcpy(cp, &v, sizeof(float));
216 cp += sizeof(float);
217 }
218 }
219 }
220 for (; i < 6; ++i) {
221 float v = coeffs[i];
222
223 b <<= 1;
224 if (v != 0) {
225 ++b;
226 memcpy(cp, &v, sizeof(float));
227 cp += sizeof(float);
228 }
229 }
230 cbuf[0] = b << 2;
231 return cp;
232 }
233
234 /* Write out values of any unknown parameters. */
235 int
236 cmd_write_unknown(gx_device_clist_writer * cldev, gx_clist_state * pcls,
237 uint must_know)
238 {
239 ushort unknown = ~pcls->known & must_know;
240 byte *dp;
241 int code;
242
243 if (unknown & flatness_known) {
244 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_flatness,
245 1 + sizeof(float));
246 if (code < 0)
247 return code;
248 memcpy(dp + 1, &cldev->imager_state.flatness, sizeof(float));
249 pcls->known |= flatness_known;
250 }
251 if (unknown & fill_adjust_known) {
252 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_fill_adjust,
253 1 + sizeof(fixed) * 2);
254 if (code < 0)
255 return code;
256 memcpy(dp + 1, &cldev->imager_state.fill_adjust.x, sizeof(fixed));
257 memcpy(dp + 1 + sizeof(fixed), &cldev->imager_state.fill_adjust.y, sizeof(fixed));
258 pcls->known |= fill_adjust_known;
259 }
260 if (unknown & ctm_known) {
261 byte cbuf[1 + 6 * sizeof(float)];
262 uint len =
263 cmd_for_matrix(cbuf,
264 (const gs_matrix *)&cldev->imager_state.ctm) - cbuf;
265
266 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_ctm, len + 1);
267 if (code < 0)
268 return code;
269 memcpy(dp + 1, cbuf, len);
270 pcls->known |= ctm_known;
271 }
272 if (unknown & line_width_known) {
273 float width =
274 gx_current_line_width(&cldev->imager_state.line_params);
275
276 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_line_width,
277 1 + sizeof(width));
278 if (code < 0)
279 return code;
280 memcpy(dp + 1, &width, sizeof(width));
281 pcls->known |= line_width_known;
282 }
283 if (unknown & miter_limit_known) {
284 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_miter_limit,
285 1 + sizeof(float));
286 if (code < 0)
287 return code;
288 memcpy(dp + 1, &cldev->imager_state.line_params.miter_limit,
289 sizeof(float));
290 pcls->known |= miter_limit_known;
291 }
292 if (unknown & misc0_known) {
293 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2, 2);
294 if (code < 0)
295 return code;
296 dp[1] = cmd_set_misc2_cap_join +
297 (cldev->imager_state.line_params.cap << 3) +
298 cldev->imager_state.line_params.join;
299 pcls->known |= misc0_known;
300 }
301 if (unknown & misc1_known) {
302 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2, 2);
303 if (code < 0)
304 return code;
305 dp[1] = cmd_set_misc2_ac_op_sa +
306 (cldev->imager_state.accurate_curves ? 4 : 0) +
307 (cldev->imager_state.overprint ? 2 : 0) +
308 (cldev->imager_state.stroke_adjust ? 1 : 0);
309 pcls->known |= misc1_known;
310 }
311 if (unknown & dash_known) {
312 int n = cldev->imager_state.line_params.dash.pattern_size;
313
314 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_dash,
315 2 + (n + 2) * sizeof(float));
316 if (code < 0)
317 return code;
318 dp[1] = n + (cldev->imager_state.line_params.dash.adapt ? 0x80 : 0) +
319 (cldev->imager_state.line_params.dot_length_absolute ? 0x40 : 0);
320 memcpy(dp + 2, &cldev->imager_state.line_params.dot_length,
321 sizeof(float));
322 memcpy(dp + 2 + sizeof(float),
323 &cldev->imager_state.line_params.dash.offset,
324 sizeof(float));
325 if (n != 0)
326 memcpy(dp + 2 + sizeof(float) * 2,
327 cldev->imager_state.line_params.dash.pattern,
328 n * sizeof(float));
329 pcls->known |= dash_known;
330 }
331 if (unknown & alpha_known) {
332 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_misc2,
333 2 + sizeof(cldev->imager_state.alpha));
334 if (code < 0)
335 return code;
336 dp[1] = cmd_set_misc2_alpha;
337 memcpy(dp + 2, &cldev->imager_state.alpha,
338 sizeof(cldev->imager_state.alpha));
339 pcls->known |= alpha_known;
340 }
341 if (unknown & clip_path_known) {
342 /*
343 * We can write out the clipping path either as rectangles
344 * or as a real (filled) path.
345 */
346 const gx_clip_path *pcpath = cldev->clip_path;
347 int band_height = cldev->page_band_height;
348 int ymin = (pcls - cldev->states) * band_height;
349 int ymax = min(ymin + band_height, cldev->height);
350 gs_fixed_rect box;
351 bool punt_to_outer_box = false;
352 int code;
353
354 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_begin_clip, 1);
355 if (code < 0)
356 return code;
357 if (pcpath->path_valid) {
358 if (gx_path_is_rectangle(&pcpath->path, &box) &&
359 fixed_is_int(box.p.x | box.p.y | box.q.x | box.q.y)
360 ) {
361 /* Write the path as a rectangle. */
362 code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
363 fixed2int_var(box.p.x),
364 fixed2int_var(box.p.y),
365 fixed2int(box.q.x - box.p.x),
366 fixed2int(box.q.y - box.p.y));
367 } else if ( !(cldev->disable_mask & clist_disable_complex_clip) ) {
368 /* Write the path. */
369 code = cmd_put_path(cldev, pcls, &pcpath->path,
370 int2fixed(ymin - 1),
371 int2fixed(ymax + 1),
372 (pcpath->rule == gx_rule_even_odd ?
373 cmd_opv_eofill : cmd_opv_fill),
374 true, sn_not_first);
375 } else {
376 /* Complex paths disabled: write outer box as clip */
377 punt_to_outer_box = true;
378 }
379 } else { /* Write out the rectangles. */
380 const gx_clip_list *list = gx_cpath_list(pcpath);
381 const gx_clip_rect *prect = list->head;
382
383 if (prect == 0)
384 prect = &list->single;
385 else if (cldev->disable_mask & clist_disable_complex_clip)
386 punt_to_outer_box = true;
387 if (!punt_to_outer_box) {
388 for (; prect != 0 && code >= 0; prect = prect->next)
389 if (prect->xmax > prect->xmin &&
390 prect->ymin < ymax && prect->ymax > ymin
391 ) {
392 code =
393 cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
394 prect->xmin, prect->ymin,
395 prect->xmax - prect->xmin,
396 prect->ymax - prect->ymin);
397 }
398 }
399 }
400 if (punt_to_outer_box) {
401 /* Clip is complex, but disabled. Write out the outer box */
402 gs_fixed_rect box;
403
404 gx_cpath_outer_box(pcpath, &box);
405 box.p.x = fixed_floor(box.p.x);
406 box.p.y = fixed_floor(box.p.y);
407 code = cmd_write_rect_cmd(cldev, pcls, cmd_op_fill_rect,
408 fixed2int_var(box.p.x),
409 fixed2int_var(box.p.y),
410 fixed2int_ceiling(box.q.x - box.p.x),
411 fixed2int_ceiling(box.q.y - box.p.y));
412 }
413 {
414 int end_code =
415 set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2);
416
417 if (code >= 0)
418 code = end_code; /* take the first failure seen */
419 if (end_code < 0 && cldev->error_is_retryable) {
420 /*
421 * end_clip has to work despite lo-mem to maintain consistency.
422 * This isn't error recovery, but just to prevent dangling
423 * cmd_opv_begin_clip's.
424 */
425 ++cldev->ignore_lo_mem_warnings;
426 end_code =
427 set_cmd_put_op(dp, cldev, pcls, cmd_opv_end_clip, 2);
428 --cldev->ignore_lo_mem_warnings;
429 }
430 if (end_code >= 0)
431 dp[1] = (gx_cpath_is_outside(pcpath) ? 1 : 0);
432 }
433 if (code < 0)
434 return code;
435 pcls->clip_enabled = 1;
436 pcls->known |= clip_path_known;
437 }
438 if (unknown & color_space_known) {
439 byte *dp;
440
441 if (cldev->color_space & 8) { /* indexed */
442 uint num_values = (cldev->indexed_params.hival + 1) *
443 gs_color_space_num_components(
444 (const gs_color_space *)&cldev->indexed_params.base_space);
445 bool use_proc = cldev->color_space & 4;
446 const void *map_data;
447 uint map_size;
448
449 if (use_proc) {
450 map_data = cldev->indexed_params.lookup.map->values;
451 map_size = num_values *
452 sizeof(cldev->indexed_params.lookup.map->values[0]);
453 } else {
454 map_data = cldev->indexed_params.lookup.table.data;
455 map_size = num_values;
456 }
457 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space,
458 2 + cmd_sizew(cldev->indexed_params.hival) + map_size);
459 if (code < 0)
460 return code;
461 memcpy(cmd_put_w(cldev->indexed_params.hival, dp + 2),
462 map_data, map_size);
463 } else {
464 code = set_cmd_put_op(dp, cldev, pcls, cmd_opv_set_color_space, 2);
465 if (code < 0)
466 return code;
467 }
468 dp[1] = cldev->color_space;
469 pcls->known |= color_space_known;
470 }
471 return 0;
472 }
473
474 /* ------ Driver procedures ------ */
475
476 int
477 clist_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
478 const gx_fill_params * params, const gx_drawing_color * pdcolor,
479 const gx_clip_path * pcpath)
480 {
481 gx_device_clist_writer * const cdev =
482 &((gx_device_clist *)dev)->writer;
483 uint unknown = 0;
484 int y, height, y0, y1;
485 gs_logical_operation_t lop = pis->log_op;
486 byte op = (byte)
487 (params->rule == gx_rule_even_odd ?
488 cmd_opv_eofill : cmd_opv_fill);
489 gs_fixed_point adjust;
490
491 if ( (cdev->disable_mask & clist_disable_fill_path) ||
492 gs_debug_c(',')
493 ) {
494 /* Disable path-based banding. */
495 return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
496 pcpath);
497 }
498 adjust = params->adjust;
499 {
500 gs_fixed_rect bbox;
501
502 gx_path_bbox(ppath, &bbox);
503 y = fixed2int(bbox.p.y) - 1;
504 height = fixed2int_ceiling(bbox.q.y) - y + 1;
505 fit_fill_y(dev, y, height);
506 fit_fill_h(dev, y, height);
507 if (height <= 0)
508 return 0;
509 }
510 y0 = y;
511 y1 = y + height;
512 if (cdev->imager_state.flatness != params->flatness) {
513 unknown |= flatness_known;
514 cdev->imager_state.flatness = params->flatness;
515 }
516 if (cdev->imager_state.fill_adjust.x != adjust.x ||
517 cdev->imager_state.fill_adjust.y != adjust.y
518 ) {
519 unknown |= fill_adjust_known;
520 cdev->imager_state.fill_adjust = adjust;
521 }
522 if (cdev->imager_state.alpha != pis->alpha) {
523 unknown |= alpha_known;
524 state_update(alpha);
525 }
526 if (cmd_check_clip_path(cdev, pcpath))
527 unknown |= clip_path_known;
528 if (unknown)
529 cmd_clear_known(cdev, unknown);
530 FOR_RECTS {
531 int code =
532 cmd_do_write_unknown(cdev, pcls,
533 flatness_known | fill_adjust_known |
534 alpha_known | clip_path_known);
535
536 if (code < 0)
537 return code;
538 if ((code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL)) < 0 ||
539 (code = cmd_update_lop(cdev, pcls, lop)) < 0
540 )
541 return code;
542 code = cmd_put_drawing_color(cdev, pcls, pdcolor);
543 if (code < 0) { /* Something went wrong, use the default implementation. */
544 return gx_default_fill_path(dev, pis, ppath, params, pdcolor,
545 pcpath);
546 }
547 code = cmd_put_path(cdev, pcls, ppath,
548 int2fixed(max(y - 1, y0)),
549 int2fixed(min(y + height + 1, y1)),
550 op + code, /* cmd_dc_type */
551 true, sn_none /* fill doesn't need the notes */ );
552 if (code < 0)
553 return code;
554 } END_RECTS;
555 return 0;
556 }
557
558 int
559 clist_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath,
560 const gx_stroke_params * params,
561 const gx_drawing_color * pdcolor, const gx_clip_path * pcpath)
562 {
563 gx_device_clist_writer * const cdev =
564 &((gx_device_clist *)dev)->writer;
565 int pattern_size = pis->line_params.dash.pattern_size;
566 uint unknown = 0;
567 gs_fixed_rect bbox;
568 gs_fixed_point expansion;
569 int adjust_y;
570 int y, height, y0, y1;
571 gs_logical_operation_t lop = pis->log_op;
572
573 if ((cdev->disable_mask & clist_disable_stroke_path) ||
574 gs_debug_c(',')
575 ) {
576 /* Disable path-based banding. */
577 return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
578 pcpath);
579 }
580 gx_path_bbox(ppath, &bbox);
581 /* We must use the supplied imager state, not our saved one, */
582 /* for computing the stroke expansion. */
583 if (gx_stroke_path_expansion(pis, ppath, &expansion) < 0) {
584 /* Expansion is too large: use the entire page. */
585 adjust_y = 0;
586 y = 0;
587 height = dev->height;
588 } else {
589 adjust_y = fixed2int_ceiling(expansion.y) + 1;
590 y = fixed2int(bbox.p.y) - adjust_y;
591 height = fixed2int_ceiling(bbox.q.y) - y + adjust_y;
592 fit_fill_y(dev, y, height);
593 fit_fill_h(dev, y, height);
594 if (height <= 0)
595 return 0;
596 }
597 y0 = y;
598 y1 = y + height;
599 /* Check the dash pattern, since we bail out if */
600 /* the pattern is too large. */
601 cdev->imager_state.line_params.dash.pattern = cdev->dash_pattern;
602 if (cdev->imager_state.line_params.dash.pattern_size != pattern_size ||
603 (pattern_size != 0 &&
604 memcmp(cdev->imager_state.line_params.dash.pattern,
605 pis->line_params.dash.pattern,
606 pattern_size * sizeof(float))) ||
607 cdev->imager_state.line_params.dash.offset !=
608 pis->line_params.dash.offset ||
609 cdev->imager_state.line_params.dash.adapt !=
610 pis->line_params.dash.adapt ||
611 cdev->imager_state.line_params.dot_length !=
612 pis->line_params.dot_length ||
613 cdev->imager_state.line_params.dot_length_absolute !=
614 pis->line_params.dot_length_absolute
615 ) { /* Bail out if the dash pattern is too long. */
616 if (pattern_size > cmd_max_dash)
617 return gx_default_stroke_path(dev, pis, ppath, params,
618 pdcolor, pcpath);
619 unknown |= dash_known;
620 gx_set_dash(&cdev->imager_state.line_params.dash,
621 pis->line_params.dash.pattern,
622 pis->line_params.dash.pattern_size,
623 pis->line_params.dash.offset, NULL);
624 gx_set_dash_adapt(&cdev->imager_state.line_params.dash,
625 pis->line_params.dash.adapt);
626 gx_set_dot_length(&cdev->imager_state.line_params,
627 pis->line_params.dot_length,
628 pis->line_params.dot_length_absolute);
629 }
630 if (state_neq(flatness)) {
631 unknown |= flatness_known;
632 state_update(flatness);
633 }
634 if (state_neq(fill_adjust.x) || state_neq(fill_adjust.y)) {
635 unknown |= fill_adjust_known;
636 state_update(fill_adjust);
637 }
638 if (state_neq(ctm.xx) || state_neq(ctm.xy) ||
639 state_neq(ctm.yx) || state_neq(ctm.yy) ||
640 /* We don't actually need tx or ty, but we don't want to bother */
641 /* tracking them separately from the other coefficients. */
642 state_neq(ctm.tx) || state_neq(ctm.ty)
643 ) {
644 unknown |= ctm_known;
645 state_update(ctm);
646 }
647 if (state_neq(line_params.half_width)) {
648 unknown |= line_width_known;
649 state_update(line_params.half_width);
650 }
651 if (state_neq(line_params.miter_limit)) {
652 unknown |= miter_limit_known;
653 gx_set_miter_limit(&cdev->imager_state.line_params,
654 pis->line_params.miter_limit);
655 }
656 if (state_neq(line_params.cap) || state_neq(line_params.join)) {
657 unknown |= misc0_known;
658 state_update(line_params.cap);
659 state_update(line_params.join);
660 }
661 if (state_neq(accurate_curves) || state_neq(overprint) ||
662 state_neq(stroke_adjust)
663 ) {
664 unknown |= misc1_known;
665 state_update(accurate_curves);
666 state_update(overprint);
667 state_update(stroke_adjust);
668 }
669 if (cdev->imager_state.alpha != pis->alpha) {
670 unknown |= alpha_known;
671 state_update(alpha);
672 }
673 if (cmd_check_clip_path(cdev, pcpath))
674 unknown |= clip_path_known;
675 if (unknown)
676 cmd_clear_known(cdev, unknown);
677 FOR_RECTS {
678 int code;
679
680 if ((code = cmd_do_write_unknown(cdev, pcls, stroke_all_known)) < 0 ||
681 (code = cmd_do_enable_clip(cdev, pcls, pcpath != NULL)) < 0 ||
682 (code = cmd_update_lop(cdev, pcls, lop)) < 0
683 )
684 return code;
685 code = cmd_put_drawing_color(cdev, pcls, pdcolor);
686 if (code < 0) {
687 /* Something went wrong, use the default implementation. */
688 return gx_default_stroke_path(dev, pis, ppath, params, pdcolor,
689 pcpath);
690 }
691 {
692 fixed ymin, ymax;
693
694 /*
695 * If a dash pattern is active, we can't skip segments
696 * outside the clipping region, because that would throw off
697 * the pattern.
698 */
699 if (pattern_size == 0)
700 ymin = int2fixed(max(y - adjust_y, y0)),
701 ymax = int2fixed(min(y + height + adjust_y, y1));
702 else
703 ymin = min_fixed,
704 ymax = max_fixed;
705 code = cmd_put_path(cdev, pcls, ppath, ymin, ymax,
706 cmd_opv_stroke + code, /* cmd_dc_type */
707 false, (segment_notes) ~ 0);
708 if (code < 0)
709 return code;
710 }
711 } END_RECTS;
712 return 0;
713 }
714
715 /* ------ Path utilities ------ */
716
717 /* Define the state bookkeeping for writing path segments. */
718 typedef struct cmd_segment_writer_s {
719 /* Set at initialization */
720 gx_device_clist_writer *cldev;
721 gx_clist_state *pcls;
722 /* Updated dynamically */
723 segment_notes notes;
724 byte *dp;
725 int len;
726 gs_fixed_point delta_first;
727 byte cmd[6 * (1 + sizeof(fixed))];
728 }
729 cmd_segment_writer;
730
731 /* Put out a path segment command. */
732 private int
733 cmd_put_segment(cmd_segment_writer * psw, byte op,
734 const fixed * operands, segment_notes notes)
735 {
736 const fixed *optr = operands;
737
738 /* Fetch num_operands before possible command merging. */
739 int i = clist_segment_op_num_operands[op & 0xf];
740 byte *q = psw->cmd - 1;
741
742 #ifdef DEBUG
743 if (gs_debug_c('L')) {
744 int j;
745
746 dlprintf2("[L] %s:%d:", cmd_sub_op_names[op >> 4][op & 0xf],
747 (int)notes);
748 for (j = 0; j < i; ++j)
749 dprintf1(" %g", fixed2float(operands[j]));
750 dputs("\n");
751 }
752 #endif
753
754 /* Merge or shorten commands if possible. */
755 if (op == cmd_opv_rlineto) {
756 if (operands[0] == 0)
757 op = cmd_opv_vlineto, optr = ++operands, i = 1;
758 else if (operands[1] == 0)
759 op = cmd_opv_hlineto, i = 1;
760 else
761 switch (*psw->dp) {
762 case cmd_opv_rmoveto:
763 psw->delta_first.x = operands[0];
764 psw->delta_first.y = operands[1];
765 op = cmd_opv_rmlineto;
766 merge:cmd_uncount_op(*psw->dp, psw->len);
767 cmd_shorten_op(psw->cldev, psw->pcls, psw->len); /* delete it */
768 q += psw->len - 1;
769 break;
770 case cmd_opv_rmlineto:
771 if (notes != psw->notes)
772 break;
773 op = cmd_opv_rm2lineto;
774 goto merge;
775 case cmd_opv_rm2lineto:
776 if (notes != psw->notes)
777 break;
778 if (operands[0] == -psw->delta_first.x &&
779 operands[1] == -psw->delta_first.y
780 ) {
781 cmd_uncount_op(cmd_opv_rm2lineto, psw->len);
782 *psw->dp = cmd_count_op(cmd_opv_rm3lineto, psw->len);
783 return 0;
784 }
785 break;
786 default:
787 ;
788 }
789 }
790 for (; --i >= 0; ++optr) {
791 fixed d = *optr, d2;
792
793 if (is_bits(d, _fixed_shift + 11) &&
794 !(d & (float2fixed(0.25) - 1))
795 ) {
796 cmd_count_add1(stats_cmd_diffs[3]);
797 d = ((d >> (_fixed_shift - 2)) & 0x1fff) + 0xc000;
798 q += 2;
799 } else if (is_bits(d, 19) && i > 0 && is_bits(d2 = optr[1], 19)) {
800 cmd_count_add1(stats_cmd_diffs[0]);
801 q[1] = (byte) ((d >> 13) & 0x3f);
802 q[2] = (byte) (d >> 5);
803 q[3] = (byte) ((d << 3) + ((d2 >> 16) & 7));
804 q[4] = (byte) (d2 >> 8);
805 q[5] = (byte) d2;
806 q += 5;
807 --i, ++optr;
808 continue;
809 } else if (is_bits(d, 22)) {
810 cmd_count_add1(stats_cmd_diffs[1]);
811 q[1] = (byte) (((d >> 16) & 0x3f) + 0x40);
812 q += 3;
813 } else if (is_bits(d, 30)) {
814 cmd_count_add1(stats_cmd_diffs[2]);
815 q[1] = (byte) (((d >> 24) & 0x3f) + 0x80);
816 q[2] = (byte) (d >> 16);
817 q += 4;
818 } else {
819 int b;
820
821 cmd_count_add1(stats_cmd_diffs[4]);
822 *++q = 0xe0;
823 for (b = sizeof(fixed) - 1; b > 1; --b)
824 *++q = (byte) (d >> (b * 8));
825 q += 2;
826 }
827 q[-1] = (byte) (d >> 8);
828 *q = (byte) d;
829 }
830 if (notes != psw->notes) {
831 byte *dp;
832 int code =
833 set_cmd_put_op(dp, psw->cldev, psw->pcls, cmd_opv_set_misc2, 2);
834
835 if (code < 0)
836 return code;
837 dp[1] = cmd_set_misc2_notes + notes;
838 psw->notes = notes;
839 } {
840 int len = q + 2 - psw->cmd;
841 byte *dp;
842 int code = set_cmd_put_op(dp, psw->cldev, psw->pcls, op, len);
843
844 if (code < 0)
845 return code;
846 memcpy(dp + 1, psw->cmd, len - 1);
847 psw->len = len;
848 psw->dp = dp;
849 }
850 return 0;
851 }
852 /* Put out a line segment command. */
853 #define cmd_put_rmoveto(psw, operands)\
854 cmd_put_segment(psw, cmd_opv_rmoveto, operands, sn_none)
855 #define cmd_put_rlineto(psw, operands, notes)\
856 cmd_put_segment(psw, cmd_opv_rlineto, operands, notes)
857
858 /*
859 * Write a path. We go to a lot of trouble to omit segments that are
860 * entirely outside the band.
861 */
862 private int
863 cmd_put_path(gx_device_clist_writer * cldev, gx_clist_state * pcls,
864 const gx_path * ppath, fixed ymin, fixed ymax, byte path_op,
865 bool implicit_close, segment_notes keep_notes)
866 {
867 gs_path_enum cenum;
868 cmd_segment_writer writer;
869
870 /*
871 * initial_op is logically const, so we declare it as such,
872 * since some systems really dislike non-const statics.
873 * This entails an otherwise pointless cast in set_first_point().
874 */
875 static const byte initial_op = cmd_opv_end_run;
876
877 /*
878 * We define the 'side' of a point according to its Y value as
879 * follows:
880 */
881 #define which_side(y) ((y) < ymin ? -1 : (y) >= ymax ? 1 : 0)
882
883 /*
884 * While writing a subpath, we need to keep track of any segments
885 * skipped at the beginning of the subpath and any segments skipped
886 * just before the current segment. We do this with two sets of
887 * state variables, one that tracks the actual path segments and one
888 * that tracks the emitted segments.
889 *
890 * The following track the actual segments:
891 */
892
893 /*
894 * The point and side of the last moveto (skipped if
895 * start_side != 0):
896 */
897 gs_fixed_point start;
898 int start_side;
899
900 /*
901 * Whether any lines or curves were skipped immediately
902 * following the moveto:
903 */
904 bool start_skip;
905
906 /* The side of the last point: */
907 int side;
908
909 /* The last point with side != 0: */
910 gs_fixed_point out;
911
912 /* If the last out-going segment was a lineto, */
913 /* its notes: */
914 segment_notes out_notes;
915
916 /*
917 * The following track the emitted segments:
918 */
919
920 /* The last point emitted: */
921 fixed px = int2fixed(pcls->rect.x);
922 fixed py = int2fixed(pcls->rect.y);
923
924 /* The point of the last emitted moveto: */
925 gs_fixed_point first;
926
927 /* Information about the last emitted operation: */
928 int open = 0; /* -1 if last was moveto, 1 if line/curveto, */
929
930 /* 0 if newpath/closepath */
931
932
933 if_debug4('p', "[p]initial (%g,%g), clip [%g..%g)\n",
934 fixed2float(px), fixed2float(py),
935 fixed2float(ymin), fixed2float(ymax));
936 gx_path_enum_init(&cenum, ppath);
937 writer.cldev = cldev;
938 writer.pcls = pcls;
939 writer.notes = sn_none;
940 #define set_first_point() (writer.dp = (byte *)&initial_op)
941 #define first_point() (writer.dp == &initial_op)
942 set_first_point();
943 for (;;) {
944 fixed vs[6];
945
946 #define A vs[0]
947 #define B vs[1]
948 #define C vs[2]
949 #define D vs[3]
950 #define E vs[4]
951 #define F vs[5]
952 int pe_op = gx_path_enum_next(&cenum, (gs_fixed_point *) vs);
953 byte *dp;
954 int code;
955
956 switch (pe_op) {
957 case 0:
958 /* If the path is open and needs an implicit close, */
959 /* do the close and then come here again. */
960 if (open > 0 && implicit_close)
961 goto close;
962 /* All done. */
963 pcls->rect.x = fixed2int_var(px);
964 pcls->rect.y = fixed2int_var(py);
965 if_debug2('p', "[p]final (%d,%d)\n",
966 pcls->rect.x, pcls->rect.y);
967 return set_cmd_put_op(dp, cldev, pcls, path_op, 1);
968 case gs_pe_moveto:
969 /* If the path is open and needs an implicit close, */
970 /* do a closepath and then redo the moveto. */
971 if (open > 0 && implicit_close) {
972 gx_path_enum_backup(&cenum);
973 goto close;
974 }
975 open = -1;
976 start.x = A, start.y = B;
977 start_skip = false;
978 if ((start_side = side = which_side(B)) != 0) {
979 out.x = A, out.y = B;
980 if_debug3('p', "[p]skip moveto (%g,%g) side %d\n",
981 fixed2float(out.x), fixed2float(out.y),
982 side);
983 continue;
984 }
985 C = A - px, D = B - py;
986 first.x = px = A, first.y = py = B;
987 code = cmd_put_rmoveto(&writer, &C);
988 if_debug2('p', "[p]moveto (%g,%g)\n",
989 fixed2float(px), fixed2float(py));
990 break;
991 case gs_pe_lineto:
992 {
993 int next_side = which_side(B);
994 segment_notes notes =
995 gx_path_enum_notes(&cenum) & keep_notes;
996
997 if (next_side == side && side != 0) { /* Skip a line completely outside the clip region. */
998 if (open < 0)
999 start_skip = true;
1000 out.x = A, out.y = B;
1001 out_notes = notes;
1002 if_debug3('p', "[p]skip lineto (%g,%g) side %d\n",
1003 fixed2float(out.x), fixed2float(out.y),
1004 side);
1005 continue;
1006 }
1007 /* If we skipped any segments, put out a moveto/lineto. */
1008 if (side && (px != out.x || py != out.y || first_point())) {
1009 C = out.x - px, D = out.y - py;
1010 if (open < 0) {
1011 first = out;
1012 code = cmd_put_rmoveto(&writer, &C);
1013 } else
1014 code = cmd_put_rlineto(&writer, &C, out_notes);
1015 if (code < 0)
1016 return code;
1017 px = out.x, py = out.y;
1018 if_debug3('p', "[p]catchup %s (%g,%g) for line\n",
1019 (open < 0 ? "moveto" : "lineto"),
1020 fixed2float(px), fixed2float(py));
1021 }
1022 if ((side = next_side) != 0) { /* Note a vertex going outside the clip region. */
1023 out.x = A, out.y = B;
1024 }
1025 C = A - px, D = B - py;
1026 px = A, py = B;
1027 open = 1;
1028 code = cmd_put_rlineto(&writer, &C, notes);
1029 }
1030 if_debug3('p', "[p]lineto (%g,%g) side %d\n",
1031 fixed2float(px), fixed2float(py), side);
1032 break;
1033 case gs_pe_closepath:
1034 #ifdef DEBUG
1035 {
1036 gs_path_enum cpenum;
1037 gs_fixed_point cvs[3];
1038 int op;
1039
1040 cpenum = cenum;
1041 switch (op = gx_path_enum_next(&cpenum, cvs)) {
1042 case 0:
1043 case gs_pe_moveto:
1044 break;
1045 default:
1046 lprintf1("closepath followed by %d, not end/moveto!\n",
1047 op);
1048 }
1049 }
1050 #endif
1051 /* A closepath may require drawing an explicit line if */
1052 /* we skipped any segments at the beginning of the path. */
1053 close:if (side != start_side) { /* If we skipped any segments, put out a moveto/lineto. */
1054 if (side && (px != out.x || py != out.y || first_point())) {
1055 C = out.x - px, D = out.y - py;
1056 code = cmd_put_rlineto(&writer, &C, out_notes);
1057 if (code < 0)
1058 return code;
1059 px = out.x, py = out.y;
1060 if_debug2('p', "[p]catchup line (%g,%g) for close\n",
1061 fixed2float(px), fixed2float(py));
1062 }
1063 if (open > 0 && start_skip) { /* Draw the closing line back to the start. */
1064 C = start.x - px, D = start.y - py;
1065 code = cmd_put_rlineto(&writer, &C, sn_none);
1066 if (code < 0)
1067 return code;
1068 px = start.x, py = start.y;
1069 if_debug2('p', "[p]draw close to (%g,%g)\n",
1070 fixed2float(px), fixed2float(py));
1071 }
1072 }
1073 /*
1074 * We don't bother to update side because we know that the
1075 * next element after a closepath, if any, must be a moveto.
1076 * We must handle explicitly the possibility that the entire
1077 * subpath was skipped.
1078 */
1079 if (implicit_close || open <= 0) {
1080 open = 0;
1081 /*
1082 * Force writing an explicit moveto if the next subpath
1083 * starts with a moveto to the same point where this one
1084 * ends.
1085 */
1086 set_first_point();
1087 continue;
1088 }
1089 open = 0;
1090 px = first.x, py = first.y;
1091 code = cmd_put_segment(&writer, cmd_opv_closepath, &A, sn_none);
1092 if_debug0('p', "[p]close\n");
1093 break;
1094 case gs_pe_curveto:
1095 {
1096 segment_notes notes =
1097 gx_path_enum_notes(&cenum) & keep_notes;
1098
1099 {
1100 fixed bpy, bqy;
1101 int all_side, out_side;
1102
1103 /* Compute the Y bounds for the clipping check. */
1104 if (B < D)
1105 bpy = B, bqy = D;
1106 else
1107 bpy = D, bqy = B;
1108 if (F < bpy)
1109 bpy = F;
1110 else if (F > bqy)
1111 bqy = F;
1112 all_side = (bqy < ymin ? -1 : bpy > ymax ? 1 : 0);
1113 if (all_side != 0) {
1114 if (all_side == side) { /* Skip a curve entirely outside the clip region. */
1115 if (open < 0)
1116 start_skip = true;
1117 out.x = E, out.y = F;
1118 out_notes = notes;
1119 if_debug3('p', "[p]skip curveto (%g,%g) side %d\n",
1120 fixed2float(out.x), fixed2float(out.y),
1121 side);
1122 continue;
1123 }
1124 out_side = all_side;
1125 } else
1126 out_side = which_side(F);
1127 /* If we skipped any segments, put out a moveto/lineto. */
1128 if (side && (px != out.x || py != out.y || first_point())) {
1129 fixed diff[2];
1130
1131 diff[0] = out.x - px, diff[1] = out.y - py;
1132 if (open < 0) {
1133 first = out;
1134 code = cmd_put_rmoveto(&writer, diff);
1135 } else
1136 code = cmd_put_rlineto(&writer, diff, out_notes);
1137 if (code < 0)
1138 return code;
1139 px = out.x, py = out.y;
1140 if_debug3('p', "[p]catchup %s (%g,%g) for curve\n",
1141 (open < 0 ? "moveto" : "lineto"),
1142 fixed2float(px), fixed2float(py));
1143 }
1144 if ((side = out_side) != 0) { /* Note a vertex going outside the clip region. */
1145 out.x = E, out.y = F;
1146 }
1147 }
1148 {
1149 fixed nx = E, ny = F;
1150 const fixed *optr = vs;
1151 byte op;
1152
1153 if_debug7('p', "[p]curveto (%g,%g; %g,%g; %g,%g) side %d\n",
1154 fixed2float(A), fixed2float(B),
1155 fixed2float(C), fixed2float(D),
1156 fixed2float(E), fixed2float(F), side);
1157 E -= C, F -= D;
1158 C -= A, D -= B;
1159 A -= px, B -= py;
1160 if (B == 0 && E == 0) {
1161 B = A, E = F, optr++, op = cmd_opv_hvcurveto;
1162 if ((B ^ D) >= 0) {
1163 if (C == D && E == B)
1164 op = cmd_opv_hqcurveto;
1165 } else if (C == -D && E == -B)
1166 C = D, op = cmd_opv_hqcurveto;
1167 } else if (A == 0 && F == 0) {
1168 optr++, op = cmd_opv_vhcurveto;
1169 if ((B ^ C) >= 0) {
1170 if (D == C && E == B)
1171 op = cmd_opv_vqcurveto;
1172 } else if (D == -C && E == -B)
1173 op = cmd_opv_vqcurveto;
1174 } else if (A == 0 && B == 0)
1175 optr += 2, op = cmd_opv_nrcurveto;
1176 else if (E == 0 && F == 0)
1177 op = cmd_opv_rncurveto;
1178 else
1179 op = cmd_opv_rrcurveto;
1180 px = nx, py = ny;
1181 open = 1;
1182 code = cmd_put_segment(&writer, op, optr, notes);
1183 }
1184 }
1185 break;
1186 default:
1187 return_error(gs_error_rangecheck);
1188 }
1189 if (code < 0)
1190 return code;
1191 #undef A
1192 #undef B
1193 #undef C
1194 #undef D
1195 #undef E
1196 #undef F
1197 }
1198 }