386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gxcpath.c
CommitLineData
cfd123de
WJ
1/* Copyright (C) 1991, 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gxcpath.c */
21/* Implementation of clipping paths */
22#include "gx.h"
23#include "gserrors.h"
24#include "gxdevice.h"
25#include "gxfixed.h"
26#include "gzcolor.h"
27#include "gzpath.h"
28#include "gxcpath.h"
29
30#define min_int (-1 << (sizeof(int) * 8 - 1))
31#define max_int (~min_int)
32
33const uint gs_clip_path_sizeof = sizeof(gx_clip_path);
34
35/* Imported procedures */
36gx_device *gs_currentdevice(P1(gs_state *));
37void gx_set_device_only(P2(gs_state *, gx_device *));
38
39/* Forward references */
40private void clip_prepare(P1(gx_clip_list *));
41
42private const gx_clip_list clip_list_empty =
43{ { 0, 0, min_int, min_int, min_int, min_int },
44 { 0, 0, min_int, max_int, 0, 0 },
45 { 0, 0, max_int, max_int, max_int, max_int },
46 0
47};
48
49/* Debugging */
50
51#ifdef DEBUG
52# define clip_rect_print(str, ar)\
53 if ( gs_debug['q'] )\
54 dprintf6("[q]%s %lx: (%d,%d),(%d,%d)\n", str, (ulong)ar,\
55 (ar)->xmin, (ar)->ymin, (ar)->xmax, (ar)->ymax)
56#else
57# define clip_rect_print(s, ar) 0
58#endif
59
60#ifdef DEBUG
61/* Validate a clipping path that has gone through clip_prepare. */
62private void
63clip_list_validate(gx_clip_list *clp)
64{ gx_clip_rect *prev = &clp->first;
65 gx_clip_rect *ptr = prev;
66 int wrong = 0;
67 while ( ptr != 0 )
68 { if ( ptr->ymin > ptr->ymax || ptr->xmin > ptr->xmax ||
69 !(ptr->ymin >= prev->ymax ||
70 ptr->ymin == prev->ymin && ptr->ymax == prev->ymax &&
71 ptr->xmin >= prev->xmax)
72 )
73 { clip_rect_print("WRONG:", ptr);
74 wrong = 1;
75 }
76 prev = ptr, ptr = ptr->next;
77 }
78}
79#endif
80
81/* ------ Clipping path accessing ------ */
82
83/* Return the path of a clipping path. */
84int
85gx_cpath_path(gx_clip_path *pcpath, gx_path *ppath)
86{ if ( !pcpath->segments_valid )
87 { int code = gx_clip_list_add_to_path(&pcpath->list, &pcpath->path);
88 if ( code < 0 ) return code;
89 pcpath->segments_valid = 1;
90 }
91 *ppath = pcpath->path;
92 return 0;
93}
94
95/* Return the quick-check rectangle for a clipping path. */
96int
97gx_cpath_box_for_check(gx_clip_path *pcpath, gs_fixed_rect *pbox)
98{ *pbox = pcpath->cbox;
99 return 0;
100}
101
102/* Test if a clipping path includes a rectangle. */
103/* The rectangle need not be oriented correctly, i.e. x0 > x1 is OK. */
104int
105gx_cpath_includes_rectangle(register gx_clip_path *pcpath,
106 fixed x0, fixed y0, fixed x1, fixed y1)
107{ return
108 (x0 <= x1 ?
109 (pcpath->cbox.p.x <= x0 && x1 <= pcpath->cbox.q.x) :
110 (pcpath->cbox.p.x <= x1 && x0 <= pcpath->cbox.q.x)) &&
111 (y0 <= y1 ?
112 (pcpath->cbox.p.y <= y0 && y1 <= pcpath->cbox.q.y) :
113 (pcpath->cbox.p.y <= y1 && y0 <= pcpath->cbox.q.y));
114}
115
116/* Release a clipping path. */
117void
118gx_cpath_release(gx_clip_path *pcpath)
119{ if ( !pcpath->shares_list )
120 gx_clip_list_free(&pcpath->list, &pcpath->path.memory_procs);
121 gx_path_release(&pcpath->path);
122}
123
124/* Share a clipping path. */
125void
126gx_cpath_share(gx_clip_path *pcpath)
127{ gx_path_share(&pcpath->path);
128 pcpath->shares_list = 1;
129}
130
131/* Create a rectangular clipping path. */
132/* The supplied rectangle may not be oriented correctly, */
133/* but it will be oriented correctly upon return. */
134int
135gx_cpath_from_rectangle(gx_clip_path *pcpath, gs_fixed_rect *pbox, gs_memory_procs *mp)
136{ gx_clip_list_from_rectangle(&pcpath->list, pbox);
137 pcpath->cbox = *pbox;
138 pcpath->segments_valid = 0;
139 pcpath->shares_list = 0;
140 gx_path_init(&pcpath->path, mp);
141 pcpath->path.bbox = *pbox;
142 return 0;
143}
144
145/* Intersect a new clipping path with an old one. */
146/* Note that it may overwrite its path argument. */
147int
148gx_cpath_intersect(gs_state *pgs, gx_clip_path *pcpath, gx_path *ppath, int rule)
149{ gs_fixed_rect old_box, new_box;
150 if ( gx_cpath_is_rectangle(pcpath, &old_box) &&
151 gx_path_is_rectangle(ppath, &new_box)
152 )
153 { int changed = 0;
154 /* Intersect the two rectangles if necessary. */
155 if ( old_box.p.x > new_box.p.x )
156 new_box.p.x = old_box.p.x, changed = 1;
157 if ( old_box.p.y > new_box.p.y )
158 new_box.p.y = old_box.p.y, changed = 1;
159 if ( old_box.q.x < new_box.q.x )
160 new_box.q.x = old_box.q.x, changed = 1;
161 if ( old_box.q.y < new_box.q.y )
162 new_box.q.y = old_box.q.y, changed = 1;
163 if ( changed )
164 { /* Store the new rectangle back into the new path. */
165 register segment *pseg =
166 (segment *)ppath->first_subpath;
167#define set_pt(pqx,pqy)\
168 pseg->pt.x = new_box.pqx.x, pseg->pt.y = new_box.pqy.y
169 set_pt(p, p); pseg = pseg->next;
170 set_pt(q, p); pseg = pseg->next;
171 set_pt(q, q); pseg = pseg->next;
172 set_pt(p, q); pseg = pseg->next;
173 if ( pseg != 0 ) /* might be an open rectangle */
174 set_pt(p, p);
175#undef set_pt
176 }
177 ppath->bbox = new_box;
178 gx_clip_list_from_rectangle(&pcpath->list, &new_box);
179 pcpath->cbox = new_box;
180 pcpath->path = *ppath;
181 pcpath->segments_valid = 1;
182 }
183 else
184 { /* Not a rectangle. Intersect the slow way. */
185 gx_device_accum adev;
186 gx_device_color devc;
187 gx_device *save_dev = gs_currentdevice(pgs);
188 int code;
189 adev = gs_accum_device;
190 adev.memory_procs = pcpath->path.memory_procs;
191 (*adev.procs->open_device)((gx_device *)&adev);
192 devc.color1 = devc.color2 = 0; /* arbitrary, but not */
193 /* transparent */
194 devc.halftone_level = 0;
195 gx_set_device_only(pgs, (gx_device *)&adev);
196 code = gx_fill_path(ppath, &devc, pgs, rule, (fixed)0);
197 gx_set_device_only(pgs, save_dev);
198 if ( code < 0 ) return code;
199 code = (*adev.procs->close_device)((gx_device *)&adev);
200 if ( code < 0 ) return code;
201 pcpath->list = adev.list;
202 gx_path_init(&pcpath->path, &pcpath->path.memory_procs);
203 pcpath->path.bbox.p.x = int2fixed(adev.bbox.p.x);
204 pcpath->path.bbox.p.y = int2fixed(adev.bbox.p.y);
205 pcpath->path.bbox.q.x = int2fixed(adev.bbox.q.x);
206 pcpath->path.bbox.q.y = int2fixed(adev.bbox.q.y);
207 /* Note that the result of the intersection might be */
208 /* a single rectangle. This will cause clip_path_is_rect.. */
209 /* to return true. This, in turn, requires that */
210 /* we set pcpath->cbox correctly. */
211 if ( clip_list_is_rectangle(&adev.list) )
212 pcpath->cbox = pcpath->path.bbox;
213 else
214 { /* The quick check must fail. */
215 pcpath->cbox.p.x = pcpath->cbox.p.y = 0;
216 pcpath->cbox.q.x = pcpath->cbox.q.y = 0;
217 }
218 pcpath->segments_valid = 0;
219 pcpath->shares_list = 0;
220 }
221 return 0;
222}
223
224/* ------ Clipping list routines ------ */
225
226/* Initialize a clip list. */
227void
228gx_clip_list_init(gx_clip_list *clp)
229{ *clp = clip_list_empty;
230}
231
232/* Initialize a clip list to a rectangle. */
233/* The supplied rectangle may not be oriented correctly, */
234/* but it will be oriented correctly upon return. */
235void
236gx_clip_list_from_rectangle(gx_clip_list *clp, register gs_fixed_rect *rp)
237{ gx_clip_list_init(clp);
238 if ( rp->p.x > rp->q.x )
239 { fixed t = rp->p.x; rp->p.x = rp->q.x; rp->q.x = t; }
240 if ( rp->p.y > rp->q.y )
241 { fixed t = rp->p.y; rp->p.y = rp->q.y; rp->q.y = t; }
242 clp->sole.xmin = fixed2int_var_rounded(rp->p.x);
243 clp->sole.ymin = fixed2int_var_rounded(rp->p.y);
244 clp->sole.xmax = fixed2int_var_rounded(rp->q.x);
245 clp->sole.ymax = fixed2int_var_rounded(rp->q.y);
246 clp->count = 1;
247}
248
249/* Add a clip list to a path. */
250/* The current implementation is very inefficient. */
251int
252gx_clip_list_add_to_path(gx_clip_list *clp, gx_path *ppath)
253{ gx_clip_rect *rp;
254 int code;
255 clip_prepare(clp);
256 for ( rp = &clp->first; rp != 0; rp = rp->next )
257 { if ( rp->xmin < rp->xmax && rp->ymin < rp->ymax )
258 { code = gx_path_add_rectangle(ppath,
259 int2fixed(rp->xmin),
260 int2fixed(rp->ymin),
261 int2fixed(rp->xmax),
262 int2fixed(rp->ymax));
263 if ( code < 0 ) return code;
264 }
265 }
266 return 0;
267}
268
269/* Free a clip list. */
270void
271gx_clip_list_free(gx_clip_list *clp, gs_memory_procs *mp)
272{ gx_clip_rect *rp = clp->last.prev;
273 if ( clp->count <= 1 ) return;
274 clip_prepare(clp);
275 while ( rp != &clp->first )
276 { gx_clip_rect *prev = rp->prev;
277 (*mp->free)((char *)rp, 1, sizeof(gx_clip_rect), "gx_clip_list_free");
278 rp = prev;
279 }
280 gx_clip_list_init(clp);
281}
282
283/* Prepare a clip list for enumeration, */
284/* by splicing pointers to account for possible relocation. */
285private void
286clip_prepare(register gx_clip_list *clp)
287{ if ( clp->count <= 1 )
288 { clp->first.next = clp->last.prev = &clp->sole;
289 clp->sole.prev = &clp->first;
290 clp->sole.next = &clp->last;
291 }
292 else
293 { clp->first.next->prev = &clp->first;
294 clp->last.prev->next = &clp->last;
295 }
296}
297
298/* ------ Rectangle list accumulator ------ */
299
300/* Device for accumulating a clipping region. */
301private dev_proc_open_device(accum_open);
302private dev_proc_close_device(accum_close);
303private dev_proc_fill_rectangle(accum_fill_rectangle);
304
305/* The device descriptor */
306/* Many of these procedures won't be called; they are set to NULL. */
307private gx_device_procs accum_procs = {
308 accum_open,
309 NULL, /* get_initial_matrix */
310 NULL, /* sync_output */
311 NULL, /* output_page */
312 accum_close,
313 NULL, /* map_rgb_color */
314 NULL, /* map_color_rgb */
315 accum_fill_rectangle,
316 NULL, /* tile_rectangle */
317 NULL, /* copy_mono */
318 NULL, /* copy_color */
319 NULL, /* draw_line */
320 NULL, /* get_bits */
321 NULL, /* get_props */
322 NULL /* put_props */
323};
324gx_device_accum gs_accum_device =
325{ sizeof(gx_device_accum),
326 &accum_procs,
327 "clip list accumulator",
328 0, 0, 1, 1, no_margins, dci_black_and_white, 0 /* generic */
329};
330#define adev ((gx_device_accum *)dev)
331
332/* Initialize the accumulation device. */
333private int
334accum_open(register gx_device *dev)
335{ gx_clip_list_init(&adev->list);
336 adev->last = &adev->list.first;
337 adev->bbox.p.x = adev->bbox.p.y = max_int;
338 adev->bbox.q.x = adev->bbox.q.y = min_int;
339 return 0;
340}
341
342/* Close the accumulation device. */
343private int
344accum_close(gx_device *dev)
345{ if ( adev->list.count >= 2 )
346 { /* 'sole' isn't good for much of anything, */
347 /* and it complicates the bookkeeping.... */
348 gx_clip_rect *last = adev->last;
349 gx_clip_rect *ar =
350 (gx_clip_rect *)(*adev->memory_procs.alloc)(1, sizeof(gx_clip_rect), "accum_close");
351 if ( ar == 0 ) return_error(gs_error_VMerror);
352 *ar = adev->list.sole;
353 adev->list.sole.prev->next = ar;
354 if ( last == &adev->list.sole )
355 last = ar;
356 else
357 adev->list.sole.next->prev = ar;
358 adev->list.last.prev = last;
359 }
360#ifdef DEBUG
361if ( gs_debug['q'] )
362 { gx_clip_rect *rp = &adev->list.first;
363 adev->last->next = 0;
364 while ( rp != 0 )
365 { clip_rect_print(" ", rp);
366 rp = rp->next;
367 }
368 }
369 clip_prepare(&adev->list); /* just for clip_list_validate */
370 clip_list_validate(&adev->list);
371#endif
372 return 0;
373}
374
375/* Accumulate one rectangle. */
376#define accum_alloc(s, ar, px, py, qx, qy)\
377 { ar = (adev->list.count == 0 ? &adev->list.sole :\
378 (gx_clip_rect *)(*adev->memory_procs.alloc)(1, sizeof(gx_clip_rect), "accum_rect"));\
379 if ( ar == 0 ) return_error(gs_error_VMerror);\
380 ar->xmin = px, ar->ymin = py, ar->xmax = qx, ar->ymax = qy;\
381 adev->list.count++;\
382 clip_rect_print(s, ar);\
383 }
384#define accum_add_last(ar)\
385 adev->last->next = ar, ar->prev = adev->last, adev->last = ar
386#define accum_add_after(ar, rprev)\
387 ar->prev = rprev, ar->next = rprev->next;\
388 if ( rprev != adev->last ) rprev->next->prev = ar;\
389 else adev->last = ar;\
390 rprev->next = ar
391#define accum_add_before(ar, rnext)\
392 ar->prev = rnext->prev, ar->next = rnext,\
393 rnext->prev->next = ar, rnext->prev = ar
394/* Add a rectangle to the list. It would be wonderful if rectangles */
395/* were always presented in the correct order, but they aren't, */
396/* because the fill loop works by trapezoids, not by scan lines. */
397/* All we can count on is that they are disjoint and *approximately* */
398/* in order. */
399#undef adev
400private int
401accum_add_rect(gx_device_accum *adev, int x, int y, int xe, int ye)
402{ gx_clip_rect *nr, *ar, *rptr;
403 int ymin, ymax;
404top: rptr = adev->last;
405 accum_alloc("accum", nr, x, y, xe, ye);
406 if ( y >= rptr->ymax ||
407 y == rptr->ymin && ye == rptr->ymax && x >= rptr->xmax
408 )
409 { accum_add_last(nr);
410 return 0;
411 }
412 /* Work backwards till we find the insertion point. */
413 while ( ye <= rptr->ymin ) rptr = rptr->prev;
414 ymin = rptr->ymin;
415 ymax = rptr->ymax;
416 if ( ye > ymax )
417 { if ( y >= ymax )
418 { /* Insert between two bands. */
419 accum_add_after(nr, rptr);
420 return 0;
421 }
422 /* Split off the top part of the new rectangle. */
423 accum_alloc("a.top", ar, x, ymax, xe, ye);
424 accum_add_after(ar, rptr);
425 ye = nr->ymax = ymax;
426 clip_rect_print(" ymax", nr);
427 }
428 /* Here we know ymin < ye <= ymax; */
429 /* rptr points to the last node with this value of ymin/ymax. */
430 /* Split the existing band if necessary. */
431 if ( ye < ymax )
432 { gx_clip_rect *rsplit = rptr;
433 while ( rsplit->ymax == ymax )
434 { accum_alloc("s.top", ar, rsplit->xmin, ye, rsplit->xmax, ymax);
435 accum_add_after(ar, rptr);
436 rsplit->ymax = ye;
437 rsplit = rsplit->prev;
438 }
439 ymax = ye;
440 }
441 if ( y > ymin )
442 { gx_clip_rect *rbot = rptr, *rsplit;
443 while ( rbot->prev->ymin == ymin )
444 rbot = rbot->prev;
445 for ( rsplit = rbot; ; )
446 { accum_alloc("s.bot", ar, rsplit->xmin, ymin, rsplit->xmax, y);
447 accum_add_before(ar, rbot);
448 rsplit->ymin = y;
449 if ( rsplit == rptr ) break;
450 rsplit = rsplit->next;
451 }
452 ymin = y;
453 }
454 /* Search for the X insertion point. */
455 /* The new rectangle is guaranteed disjoint from all the old ones. */
456 while ( rptr->ymin == ymin && x < rptr->xmax )
457 { rptr = rptr->prev;
458 }
459 if ( y < ymin )
460 { /* Continue with the bottom part of the new rectangle. */
461 nr->ymin = ymin;
462 clip_rect_print(" ymin", nr);
463 accum_add_after(nr, rptr);
464 ye = ymin;
465 goto top;
466 }
467 accum_add_after(nr, rptr);
468 return 0;
469}
470#define adev ((gx_device_accum *)dev)
471private int
472accum_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
473 gx_color_index color)
474{ int xe, ye;
475 if ( w <= 0 || h <= 0 ) return 0;
476 xe = x + w, ye = y + h;
477 /* Update the bounding box. */
478 if ( x < adev->bbox.p.x ) adev->bbox.p.x = x;
479 if ( y < adev->bbox.p.y ) adev->bbox.p.y = y;
480 if ( xe > adev->bbox.q.x ) adev->bbox.q.x = xe;
481 if ( ye > adev->bbox.q.y ) adev->bbox.q.y = ye;
482 return accum_add_rect(adev, x, y, xe, ye);
483}
484
485/* ------ Rectangle list clipper ------ */
486
487/* Device for clipping with a region. */
488private dev_proc_open_device(clip_open);
489private dev_proc_map_rgb_color(clip_map_rgb_color);
490private dev_proc_map_color_rgb(clip_map_color_rgb);
491private dev_proc_fill_rectangle(clip_fill_rectangle);
492private dev_proc_tile_rectangle(clip_tile_rectangle);
493private dev_proc_copy_mono(clip_copy_mono);
494private dev_proc_copy_color(clip_copy_color);
495private dev_proc_get_bits(clip_get_bits);
496private dev_proc_get_props(clip_get_props);
497private dev_proc_put_props(clip_put_props);
498
499/* The device descriptor. */
500private gx_device_procs clip_procs = {
501 clip_open,
502 gx_default_get_initial_matrix,
503 gx_default_sync_output,
504 gx_default_output_page,
505 gx_default_close_device,
506 clip_map_rgb_color,
507 clip_map_color_rgb,
508 clip_fill_rectangle,
509 clip_tile_rectangle,
510 clip_copy_mono,
511 clip_copy_color,
512 gx_default_draw_line,
513 clip_get_bits,
514 clip_get_props,
515 clip_put_props
516};
517gx_device_clip gs_clip_device =
518{ sizeof(gx_device_clip),
519 &clip_procs,
520 "clipper",
521 0, 0, 1, 1, no_margins, dci_black_and_white, 0 /* generic */
522};
523#define rdev ((gx_device_clip *)dev)
524
525/* Declare and initialize the cursor variables. */
526#ifdef DEBUG
527private ulong clip_in, clip_down, clip_down2, clip_up, clip_x, clip_no_x;
528# define inc(v) v++
529#else
530# define inc(v) 0
531#endif
532#define DECLARE_CLIP\
533 register gx_clip_rect *rptr = rdev->current.rptr;\
534 gx_device *tdev = rdev->target;
535/* Check whether the rectangle x,y,w,h falls within the current entry. */
536#define xywh_in_ryptr()\
537 ((y >= rptr->ymin && y + h <= rptr->ymax &&\
538 x >= rptr->xmin && x + w <= rptr->xmax) ? (inc(clip_in), 1) : 0)
539/*
540 * Warp the cursor forward or backward to the first rectangle row that
541 * could include a given y value. Assumes rptr is set, and updates it.
542 * Specifically, after warp_cursor, y < rptr->ymax and y >= rptr->prev->ymax.
543 * Note that ye <= rptr->ymin is possible.
544 */
545#define warp_cursor(y)\
546 while ( (y) >= rptr->ymax ) { inc(clip_up); rptr = rptr->next; };\
547 while ( rptr->prev != 0 && (y) < rptr->prev->ymax )\
548 { inc(clip_down); rptr = rptr->prev; }
549/*
550 * Enumerate the rectangles of the x,w,y,h argument that fall within
551 * the clipping region. Usage:
552 * BEGIN_CLIP
553 * ... xc, yc, xec, yec ... [must be an expression statement]
554 * NEXT_CLIP
555 * (about to set yc to yec)
556 * END_CLIP
557 */
558#ifdef DEBUG
559# define clip_2_print(str, v1, v2)\
560 if ( gs_debug['q'] ) dprintf2(str, v1, v2)
561#else
562# define clip_2_print(str, v1, v2) 0
563#endif
564#define BEGIN_CLIP\
565 if ( w <= 0 || h <= 0 ) return 0;\
566 { int yc;\
567 const int xe = x + w, ye = y + h;\
568 warp_cursor(y);\
569 rdev->current.rptr = rptr;\
570 yc = rptr->ymin;\
571 if ( yc < y ) yc = y;\
572 else if ( yc >= ye ) return 0;\
573 for ( ; ; )\
574 { const int ymax = rptr->ymax;\
575 int yec = ymax;\
576 if ( yec > ye ) yec = ye;\
577 clip_2_print("[q]yc=%d yec=%d\n", yc, yec);\
578 do \
579 { int xc = rptr->xmin;\
580 int xec = rptr->xmax;\
581 if ( xc < x ) xc = x;\
582 if ( xec > xe ) xec = xe;\
583 if ( xec > xc )\
584 { int code;\
585 clip_rect_print("match", rptr);\
586 clip_2_print("[q]xc=%d xec=%d\n", xc, xec);\
587 inc(clip_x);\
588 code =
589#define NEXT_CLIP\
590 if ( code < 0 ) return code;\
591 }\
592 else inc(clip_no_x);\
593 }\
594 while ( (rptr = rptr->next) != 0 && rptr->ymax == ymax );\
595 if ( rptr == 0 || (yec = rptr->ymin) >= ye ) break;
596#define END_CLIP\
597 yc = yec;\
598 }\
599 }
600
601/* Open a clipping device */
602private int
603clip_open(register gx_device *dev)
604{ gx_device *tdev = rdev->target;
605 /* Fix up possible dangling pointers. */
606 clip_prepare(&rdev->list);
607 /* Initialize the cursor. */
608 rdev->current.rptr = &rdev->list.first;
609 rdev->color_info = tdev->color_info;
610 return 0;
611}
612
613/* Forward non-displaying operations to the target device. */
614private gx_color_index
615clip_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
616 gx_color_value b)
617{ gx_device *tdev = rdev->target;
618 return (*tdev->procs->map_rgb_color)(tdev, r, g, b);
619}
620private int
621clip_map_color_rgb(gx_device *dev, gx_color_index color,
622 gx_color_value prgb[3])
623{ gx_device *tdev = rdev->target;
624 return (*tdev->procs->map_color_rgb)(tdev, color, prgb);
625}
626private int
627clip_get_props(gx_device *dev, gs_prop_item *plist)
628{ gx_device *tdev = rdev->target;
629 return (*tdev->procs->get_props)(tdev, plist);
630}
631private int
632clip_put_props(gx_device *dev, gs_prop_item *plist, int count)
633{ gx_device *tdev = rdev->target;
634 return (*tdev->procs->put_props)(tdev, plist, count);
635}
636
637/* Fill a rectangle */
638private int
639clip_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
640 gx_color_index color)
641{ DECLARE_CLIP
642 dev_proc_fill_rectangle((*fill)) = tdev->procs->fill_rectangle;
643 if ( xywh_in_ryptr() )
644 return (*fill)(tdev, x, y, w, h, color);
645 BEGIN_CLIP
646 (*fill)(tdev, xc, yc, xec - xc, yec - yc, color);
647 NEXT_CLIP
648 END_CLIP
649 return 0;
650}
651
652/* Tile a rectangle */
653private int
654clip_tile_rectangle(gx_device *dev, gx_bitmap *tile,
655 int x, int y, int w, int h,
656 gx_color_index color0, gx_color_index color1, int phase_x, int phase_y)
657{ DECLARE_CLIP
658 dev_proc_tile_rectangle((*fill)) = tdev->procs->tile_rectangle;
659 if ( xywh_in_ryptr() )
660 return (*fill)(tdev, tile, x, y, w, h, color0, color1, phase_x, phase_y);
661 BEGIN_CLIP
662 (*fill)(tdev, tile, xc, yc, xec - xc, yec - yc, color0, color1, phase_x, phase_y);
663 NEXT_CLIP
664 END_CLIP
665 return 0;
666}
667
668/* Copy a monochrome rectangle */
669private int
670clip_copy_mono(gx_device *dev,
671 byte *data, int sourcex, int raster, gx_bitmap_id id,
672 int x, int y, int w, int h,
673 gx_color_index color0, gx_color_index color1)
674{ DECLARE_CLIP
675 dev_proc_copy_mono((*copy)) = tdev->procs->copy_mono;
676 if ( xywh_in_ryptr() )
677 return (*copy)(tdev, data, sourcex, raster, id, x, y, w, h, color0, color1);
678 BEGIN_CLIP
679 (*copy)(tdev, data, sourcex + xc - x, raster, gx_no_bitmap_id, xc, yc, xec - xc, yec - yc, color0, color1);
680 NEXT_CLIP
681 data += (yec - yc) * raster;
682 END_CLIP
683 return 0;
684}
685
686/* Copy a color rectangle */
687private int
688clip_copy_color(gx_device *dev,
689 byte *data, int sourcex, int raster, gx_bitmap_id id,
690 int x, int y, int w, int h)
691{ DECLARE_CLIP
692 dev_proc_copy_color((*copy)) = tdev->procs->copy_color;
693 if ( xywh_in_ryptr() )
694 return (*copy)(tdev, data, sourcex, raster, id, x, y, w, h);
695 BEGIN_CLIP
696 (*copy)(tdev, data, sourcex + xc - x, raster, gx_no_bitmap_id, xc, yc, xec - xc, yec - yc);
697 NEXT_CLIP
698 data += (yec - yc) * raster;
699 END_CLIP
700 return 0;
701}
702
703/* Get bits back from the device. */
704private int
705clip_get_bits(gx_device *dev, int y, byte *data, uint size, int pad)
706{ gx_device *tdev = rdev->target;
707 return (*tdev->procs->get_bits)(tdev, y, data, size, pad);
708}