Commit | Line | Data |
---|---|---|
cfd123de WJ |
1 | /* Copyright (C) 1991, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
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 | ||
33 | const uint gs_clip_path_sizeof = sizeof(gx_clip_path); | |
34 | ||
35 | /* Imported procedures */ | |
36 | gx_device *gs_currentdevice(P1(gs_state *)); | |
37 | void gx_set_device_only(P2(gs_state *, gx_device *)); | |
38 | ||
39 | /* Forward references */ | |
40 | private void clip_prepare(P1(gx_clip_list *)); | |
41 | ||
42 | private 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. */ | |
62 | private void | |
63 | clip_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. */ | |
84 | int | |
85 | gx_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. */ | |
96 | int | |
97 | gx_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. */ | |
104 | int | |
105 | gx_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. */ | |
117 | void | |
118 | gx_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. */ | |
125 | void | |
126 | gx_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. */ | |
134 | int | |
135 | gx_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. */ | |
147 | int | |
148 | gx_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. */ | |
227 | void | |
228 | gx_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. */ | |
235 | void | |
236 | gx_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. */ | |
251 | int | |
252 | gx_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. */ | |
270 | void | |
271 | gx_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. */ | |
285 | private void | |
286 | clip_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. */ | |
301 | private dev_proc_open_device(accum_open); | |
302 | private dev_proc_close_device(accum_close); | |
303 | private 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. */ | |
307 | private 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 | }; | |
324 | gx_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. */ | |
333 | private int | |
334 | accum_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. */ | |
343 | private int | |
344 | accum_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 | |
361 | if ( 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 | |
400 | private int | |
401 | accum_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; | |
404 | top: 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) | |
471 | private int | |
472 | accum_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. */ | |
488 | private dev_proc_open_device(clip_open); | |
489 | private dev_proc_map_rgb_color(clip_map_rgb_color); | |
490 | private dev_proc_map_color_rgb(clip_map_color_rgb); | |
491 | private dev_proc_fill_rectangle(clip_fill_rectangle); | |
492 | private dev_proc_tile_rectangle(clip_tile_rectangle); | |
493 | private dev_proc_copy_mono(clip_copy_mono); | |
494 | private dev_proc_copy_color(clip_copy_color); | |
495 | private dev_proc_get_bits(clip_get_bits); | |
496 | private dev_proc_get_props(clip_get_props); | |
497 | private dev_proc_put_props(clip_put_props); | |
498 | ||
499 | /* The device descriptor. */ | |
500 | private 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 | }; | |
517 | gx_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 | |
527 | private 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 */ | |
602 | private int | |
603 | clip_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. */ | |
614 | private gx_color_index | |
615 | clip_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 | } | |
620 | private int | |
621 | clip_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 | } | |
626 | private int | |
627 | clip_get_props(gx_device *dev, gs_prop_item *plist) | |
628 | { gx_device *tdev = rdev->target; | |
629 | return (*tdev->procs->get_props)(tdev, plist); | |
630 | } | |
631 | private int | |
632 | clip_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 */ | |
638 | private int | |
639 | clip_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 */ | |
653 | private int | |
654 | clip_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 */ | |
669 | private int | |
670 | clip_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 */ | |
687 | private int | |
688 | clip_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. */ | |
704 | private int | |
705 | clip_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 | } |