Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Copyright (C) 1993, 2000 Aladdin Enterprises.  All rights reserved.
2
 
3
  This software is provided AS-IS with no warranty, either express or
4
  implied.
5
 
6
  This software is distributed under license and may not be copied,
7
  modified or distributed except as expressly authorized under the terms
8
  of the license contained in the file LICENSE in this distribution.
9
 
10
  For more information about licensing, please refer to
11
  http://www.ghostscript.com/licensing/. For information on
12
  commercial licensing, go to http://www.artifex.com/licensing/ or
13
  contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14
  San Rafael, CA  94903, U.S.A., +1(415)492-9861.
15
*/
16
 
17
/*$Id: gxacpath.c,v 1.10 2004/08/04 19:36:12 stefan Exp $ */
18
/* Accumulator for clipping paths */
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gsrop.h"
22
#include "gsstruct.h"
23
#include "gsutil.h"
24
#include "gsdcolor.h"
25
#include "gxdevice.h"
26
#include "gxfixed.h"
27
#include "gxistate.h"
28
#include "gzpath.h"
29
#include "gxpaint.h"
30
#include "gzcpath.h"
31
#include "gzacpath.h"
32
 
33
/* Device procedures */
34
private dev_proc_open_device(accum_open);
35
private dev_proc_close_device(accum_close);
36
private dev_proc_fill_rectangle(accum_fill_rectangle);
37
 
38
/* The device descriptor */
39
/* Many of these procedures won't be called; they are set to NULL. */
40
private const gx_device_cpath_accum gs_cpath_accum_device =
41
{std_device_std_body(gx_device_cpath_accum, 0, "clip list accumulator",
42
		     0, 0, 1, 1),
43
 {accum_open,
44
  NULL,
45
  NULL,
46
  NULL,
47
  accum_close,
48
  NULL,
49
  NULL,
50
  accum_fill_rectangle,
51
  NULL,
52
  NULL,
53
  NULL,
54
  NULL,
55
  NULL,
56
  NULL,
57
  NULL,
58
  NULL,
59
  NULL,
60
  NULL,
61
  NULL,
62
  NULL,
63
  NULL,
64
  NULL,
65
  NULL,
66
  NULL,
67
  gx_default_fill_path,
68
  gx_default_stroke_path,
69
  NULL,
70
  gx_default_fill_trapezoid,
71
  gx_default_fill_parallelogram,
72
  gx_default_fill_triangle,
73
  gx_default_draw_thin_line,
74
  gx_default_begin_image,
75
  gx_default_image_data,
76
  gx_default_end_image,
77
  NULL,
78
  NULL,
79
  gx_get_largest_clipping_box,
80
  gx_default_begin_typed_image,
81
  NULL,
82
  NULL,
83
  NULL,
84
  NULL,
85
  gx_default_text_begin,
86
  gx_default_finish_copydevice,
87
  NULL,
88
  NULL,
89
  NULL,
90
  NULL,
91
  NULL,
92
  NULL,
93
  NULL,
94
  NULL,
95
  NULL
96
 }
97
};
98
 
99
/* Start accumulating a clipping path. */
100
void
101
gx_cpath_accum_begin(gx_device_cpath_accum * padev, gs_memory_t * mem)
102
{
103
    gx_device_init((gx_device *) padev,
104
		   (const gx_device *) & gs_cpath_accum_device,
105
		   NULL /* allocated on stack */ , true);
106
    padev->list_memory = mem;
107
    (*dev_proc(padev, open_device)) ((gx_device *) padev);
108
}
109
 
110
void
111
gx_cpath_accum_set_cbox(gx_device_cpath_accum * padev,
112
			const gs_fixed_rect * pbox)
113
{
114
    padev->clip_box.p.x = fixed2int_var(pbox->p.x);
115
    padev->clip_box.p.y = fixed2int_var(pbox->p.y);
116
    padev->clip_box.q.x = fixed2int_var_ceiling(pbox->q.x);
117
    padev->clip_box.q.y = fixed2int_var_ceiling(pbox->q.y);
118
}
119
 
120
/* Finish accumulating a clipping path. */
121
int
122
gx_cpath_accum_end(const gx_device_cpath_accum * padev, gx_clip_path * pcpath)
123
{
124
    int code = (*dev_proc(padev, close_device)) ((gx_device *) padev);
125
    /* Make an entire clipping path so we can use cpath_assign. */
126
    gx_clip_path apath;
127
 
128
    if (code < 0)
129
	return code;
130
    gx_cpath_init_local(&apath, padev->list_memory);
131
    apath.rect_list->list = padev->list;
132
    if (padev->list.count == 0)
133
	apath.path.bbox.p.x = apath.path.bbox.p.y =
134
	apath.path.bbox.q.x = apath.path.bbox.q.y = 0;
135
    else {
136
	apath.path.bbox.p.x = int2fixed(padev->bbox.p.x);
137
	apath.path.bbox.p.y = int2fixed(padev->bbox.p.y);
138
	apath.path.bbox.q.x = int2fixed(padev->bbox.q.x);
139
	apath.path.bbox.q.y = int2fixed(padev->bbox.q.y);
140
    }
141
    /* indicate that the bbox is accurate */
142
    apath.path.bbox_accurate = 1;
143
    /* Note that the result of the intersection might be */
144
    /* a single rectangle.  This will cause clip_path_is_rect.. */
145
    /* to return true.  This, in turn, requires that */
146
    /* we set apath.inner_box correctly. */
147
    if (clip_list_is_rectangle(&padev->list))
148
	apath.inner_box = apath.path.bbox;
149
    else {
150
	/* The quick check must fail. */
151
	apath.inner_box.p.x = apath.inner_box.p.y = 0;
152
	apath.inner_box.q.x = apath.inner_box.q.y = 0;
153
    }
154
    gx_cpath_set_outer_box(&apath);
155
    apath.path_valid = false;
156
    apath.id = gs_next_ids(padev->list_memory, 1);	/* path changed => change id */
157
    gx_cpath_assign_free(pcpath, &apath);
158
    return 0;
159
}
160
 
161
/* Discard an accumulator in case of error. */
162
void
163
gx_cpath_accum_discard(gx_device_cpath_accum * padev)
164
{
165
    gx_clip_list_free(&padev->list, padev->list_memory);
166
}
167
 
168
/* Intersect two clipping paths using an accumulator. */
169
int
170
gx_cpath_intersect_path_slow(gx_clip_path * pcpath, gx_path * ppath,
171
			     int rule, gs_imager_state *pis)
172
{
173
    gs_logical_operation_t save_lop = gs_current_logical_op_inline(pis);
174
    gx_device_cpath_accum adev;
175
    gx_device_color devc;
176
    gx_fill_params params;
177
    int code;
178
 
179
    gx_cpath_accum_begin(&adev, pcpath->path.memory);
180
    set_nonclient_dev_color(&devc, 0);	/* arbitrary, but not transparent */
181
    gs_set_logical_op_inline(pis, lop_default);
182
    params.rule = rule;
183
    params.adjust.x = params.adjust.y = fixed_half;
184
    params.flatness = gs_currentflat_inline(pis);
185
    params.fill_zero_width = true;
186
    code = gx_fill_path_only(ppath, (gx_device *)&adev, pis,
187
			     &params, &devc, pcpath);
188
    if (code < 0 || (code = gx_cpath_accum_end(&adev, pcpath)) < 0)
189
	gx_cpath_accum_discard(&adev);
190
    gs_set_logical_op_inline(pis, save_lop);
191
    return code;
192
}
193
 
194
/* ------ Device implementation ------ */
195
 
196
#ifdef DEBUG
197
/* Validate a clipping path after accumulation. */
198
private bool
199
clip_list_validate(const gx_clip_list * clp)
200
{
201
    if (clp->count <= 1)
202
	return (clp->head == 0 && clp->tail == 0 &&
203
		clp->single.next == 0 && clp->single.prev == 0);
204
    else {
205
	const gx_clip_rect *prev = clp->head;
206
	const gx_clip_rect *ptr;
207
	bool ok = true;
208
 
209
	while ((ptr = prev->next) != 0) {
210
	    if (ptr->ymin > ptr->ymax || ptr->xmin > ptr->xmax ||
211
		!(ptr->ymin >= prev->ymax ||
212
		  (ptr->ymin == prev->ymin &&
213
		   ptr->ymax == prev->ymax &&
214
		   ptr->xmin >= prev->xmax)) ||
215
		ptr->prev != prev
216
		) {
217
		clip_rect_print('q', "WRONG:", ptr);
218
		ok = false;
219
	    }
220
	    prev = ptr;
221
	}
222
	return ok && prev == clp->tail;
223
    }
224
}
225
#endif /* DEBUG */
226
 
227
/* Initialize the accumulation device. */
228
private int
229
accum_open(register gx_device * dev)
230
{
231
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
232
 
233
    gx_clip_list_init(&adev->list);
234
    adev->bbox.p.x = adev->bbox.p.y = max_int;
235
    adev->bbox.q.x = adev->bbox.q.y = min_int;
236
    adev->clip_box.p.x = adev->clip_box.p.y = min_int;
237
    adev->clip_box.q.x = adev->clip_box.q.y = max_int;
238
    return 0;
239
}
240
 
241
/* Close the accumulation device. */
242
private int
243
accum_close(gx_device * dev)
244
{
245
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
246
 
247
    adev->list.xmin = adev->bbox.p.x;
248
    adev->list.xmax = adev->bbox.q.x;
249
#ifdef DEBUG
250
    if (gs_debug_c('q')) {
251
	gx_clip_rect *rp =
252
	    (adev->list.count <= 1 ? &adev->list.single : adev->list.head);
253
 
254
	dlprintf6("[q]list at 0x%lx, count=%d, head=0x%lx, tail=0x%lx, xrange=(%d,%d):\n",
255
		  (ulong) & adev->list, adev->list.count,
256
		  (ulong) adev->list.head, (ulong) adev->list.tail,
257
		  adev->list.xmin, adev->list.xmax);
258
	while (rp != 0) {
259
	    clip_rect_print('q', "   ", rp);
260
	    rp = rp->next;
261
	}
262
    }
263
    if (!clip_list_validate(&adev->list)) {
264
	lprintf1("[q]Bad clip list 0x%lx!\n", (ulong) & adev->list);
265
	return_error(gs_error_Fatal);
266
    }
267
#endif
268
    return 0;
269
}
270
 
271
/* Accumulate one rectangle. */
272
/* Allocate a rectangle to be added to the list. */
273
static const gx_clip_rect clip_head_rect = {
274
    0, 0, min_int, min_int, min_int, min_int
275
};
276
static const gx_clip_rect clip_tail_rect = {
277
    0, 0, max_int, max_int, max_int, max_int
278
};
279
private gx_clip_rect *
280
accum_alloc_rect(gx_device_cpath_accum * adev)
281
{
282
    gs_memory_t *mem = adev->list_memory;
283
    gx_clip_rect *ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
284
				       "accum_alloc_rect");
285
 
286
    if (ar == 0)
287
	return 0;
288
    if (adev->list.count == 2) {
289
	/* We're switching from a single rectangle to a list. */
290
	/* Allocate the head and tail entries. */
291
	gx_clip_rect *head = ar;
292
	gx_clip_rect *tail =
293
	    gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
294
			    "accum_alloc_rect(tail)");
295
	gx_clip_rect *single =
296
	    gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
297
			    "accum_alloc_rect(single)");
298
 
299
	ar = gs_alloc_struct(mem, gx_clip_rect, &st_clip_rect,
300
			     "accum_alloc_rect(head)");
301
	if (tail == 0 || single == 0 || ar == 0) {
302
	    gs_free_object(mem, ar, "accum_alloc_rect");
303
	    gs_free_object(mem, single, "accum_alloc_rect(single)");
304
	    gs_free_object(mem, tail, "accum_alloc_rect(tail)");
305
	    gs_free_object(mem, head, "accum_alloc_rect(head)");
306
	    return 0;
307
	}
308
	*head = clip_head_rect;
309
	head->next = single;
310
	*single = adev->list.single;
311
	single->prev = head;
312
	single->next = tail;
313
	*tail = clip_tail_rect;
314
	tail->prev = single;
315
	adev->list.head = head;
316
	adev->list.tail = tail;
317
    }
318
    return ar;
319
}
320
#define ACCUM_ALLOC(s, ar, px, py, qx, qy)\
321
	if (++(adev->list.count) == 1)\
322
	  ar = &adev->list.single;\
323
	else if ((ar = accum_alloc_rect(adev)) == 0)\
324
	  return_error(gs_error_VMerror);\
325
	ACCUM_SET(s, ar, px, py, qx, qy)
326
#define ACCUM_SET(s, ar, px, py, qx, qy)\
327
	(ar)->xmin = px, (ar)->ymin = py, (ar)->xmax = qx, (ar)->ymax = qy;\
328
	clip_rect_print('Q', s, ar)
329
/* Link or unlink a rectangle in the list. */
330
#define ACCUM_ADD_LAST(ar)\
331
	ACCUM_ADD_BEFORE(ar, adev->list.tail)
332
#define ACCUM_ADD_AFTER(ar, rprev)\
333
	ar->prev = (rprev), (ar->next = (rprev)->next)->prev = ar,\
334
	  (rprev)->next = ar
335
#define ACCUM_ADD_BEFORE(ar, rnext)\
336
	(ar->prev = (rnext)->prev)->next = ar, ar->next = (rnext),\
337
	  (rnext)->prev = ar
338
#define ACCUM_REMOVE(ar)\
339
	ar->next->prev = ar->prev, ar->prev->next = ar->next
340
/* Free a rectangle that was removed from the list. */
341
#define ACCUM_FREE(s, ar)\
342
	if (--(adev->list.count)) {\
343
	  clip_rect_print('Q', s, ar);\
344
	  gs_free_object(adev->list_memory, ar, "accum_rect");\
345
	}
346
/*
347
 * Add a rectangle to the list.  It would be wonderful if rectangles
348
 * were always disjoint and always presented in the correct order,
349
 * but they aren't: the fill loop works by trapezoids, not by scan lines,
350
 * and may produce slightly overlapping rectangles because of "fattening".
351
 * All we can count on is that they are approximately disjoint and
352
 * approximately in order.
353
 *
354
 * Because of the way the fill loop handles a path that is just a single
355
 * rectangle, we take special care to merge Y-adjacent rectangles when
356
 * this is possible.
357
 */
358
private int
359
accum_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
360
		     gx_color_index color)
361
{
362
    gx_device_cpath_accum * const adev = (gx_device_cpath_accum *)dev;
363
    int xe = x + w, ye = y + h;
364
    gx_clip_rect *nr;
365
    gx_clip_rect *ar;
366
    register gx_clip_rect *rptr;
367
    int ymin, ymax;
368
 
369
    /* Clip the rectangle being added. */
370
    if (y < adev->clip_box.p.y)
371
	y = adev->clip_box.p.y;
372
    if (ye > adev->clip_box.q.y)
373
	ye = adev->clip_box.q.y;
374
    if (y >= ye)
375
	return 0;
376
    if (x < adev->clip_box.p.x)
377
	x = adev->clip_box.p.x;
378
    if (xe > adev->clip_box.q.x)
379
	xe = adev->clip_box.q.x;
380
    if (x >= xe)
381
	return 0;
382
 
383
    /* Update the bounding box. */
384
    if (x < adev->bbox.p.x)
385
	adev->bbox.p.x = x;
386
    if (y < adev->bbox.p.y)
387
	adev->bbox.p.y = y;
388
    if (xe > adev->bbox.q.x)
389
	adev->bbox.q.x = xe;
390
    if (ye > adev->bbox.q.y)
391
	adev->bbox.q.y = ye;
392
 
393
top:
394
    if (adev->list.count == 0) {	/* very first rectangle */
395
	adev->list.count = 1;
396
	ACCUM_SET("single", &adev->list.single, x, y, xe, ye);
397
	return 0;
398
    }
399
    if (adev->list.count == 1) {	/* check for Y merging */
400
	rptr = &adev->list.single;
401
	if (x == rptr->xmin && xe == rptr->xmax &&
402
	    y <= rptr->ymax && ye >= rptr->ymin
403
	    ) {
404
	    if (y < rptr->ymin)
405
		rptr->ymin = y;
406
	    if (ye > rptr->ymax)
407
		rptr->ymax = ye;
408
	    return 0;
409
	}
410
    }
411
    else
412
	rptr = adev->list.tail->prev;
413
    if (y >= rptr->ymax) {
414
	if (y == rptr->ymax && x == rptr->xmin && xe == rptr->xmax &&
415
	    (rptr->prev == 0 || y != rptr->prev->ymax)
416
	    ) {
417
	    rptr->ymax = ye;
418
	    return 0;
419
	}
420
	ACCUM_ALLOC("app.y", nr, x, y, xe, ye);
421
	ACCUM_ADD_LAST(nr);
422
	return 0;
423
    } else if (y == rptr->ymin && ye == rptr->ymax && x >= rptr->xmin) {
424
	if (x <= rptr->xmax) {
425
	    if (xe > rptr->xmax)
426
		rptr->xmax = xe;
427
	    return 0;
428
	}
429
	ACCUM_ALLOC("app.x", nr, x, y, xe, ye);
430
	ACCUM_ADD_LAST(nr);
431
	return 0;
432
    }
433
    ACCUM_ALLOC("accum", nr, x, y, xe, ye);
434
    rptr = adev->list.tail->prev;
435
    /* Work backwards till we find the insertion point. */
436
    while (ye <= rptr->ymin)
437
	rptr = rptr->prev;
438
    ymin = rptr->ymin;
439
    ymax = rptr->ymax;
440
    if (ye > ymax) {
441
	if (y >= ymax) {	/* Insert between two bands. */
442
	    ACCUM_ADD_AFTER(nr, rptr);
443
	    return 0;
444
	}
445
	/* Split off the top part of the new rectangle. */
446
	ACCUM_ALLOC("a.top", ar, x, ymax, xe, ye);
447
	ACCUM_ADD_AFTER(ar, rptr);
448
	ye = nr->ymax = ymax;
449
	clip_rect_print('Q', " ymax", nr);
450
    }
451
    /* Here we know ymin < ye <= ymax; */
452
    /* rptr points to the last node with this value of ymin/ymax. */
453
    /* If necessary, split off the part of the existing band */
454
    /* that is above the new band. */
455
    if (ye < ymax) {
456
	gx_clip_rect *rsplit = rptr;
457
 
458
	while (rsplit->ymax == ymax) {
459
	    ACCUM_ALLOC("s.top", ar, rsplit->xmin, ye, rsplit->xmax, ymax);
460
	    ACCUM_ADD_AFTER(ar, rptr);
461
	    rsplit->ymax = ye;
462
	    rsplit = rsplit->prev;
463
	}
464
	ymax = ye;
465
    }
466
    /* Now ye = ymax.  If necessary, split off the part of the */
467
    /* existing band that is below the new band. */
468
    if (y > ymin) {
469
	gx_clip_rect *rbot = rptr, *rsplit;
470
 
471
	while (rbot->prev->ymin == ymin)
472
	    rbot = rbot->prev;
473
	for (rsplit = rbot;;) {
474
	    ACCUM_ALLOC("s.bot", ar, rsplit->xmin, ymin, rsplit->xmax, y);
475
	    ACCUM_ADD_BEFORE(ar, rbot);
476
	    rsplit->ymin = y;
477
	    if (rsplit == rptr)
478
		break;
479
	    rsplit = rsplit->next;
480
	}
481
	ymin = y;
482
    }
483
    /* Now y <= ymin as well.  (y < ymin is possible.) */
484
    nr->ymin = ymin;
485
    /* Search for the X insertion point. */
486
    for (; rptr->ymin == ymin; rptr = rptr->prev) {
487
	if (xe < rptr->xmin)
488
	    continue;		/* still too far to right */
489
	if (x > rptr->xmax)
490
	    break;		/* disjoint */
491
	/* The new rectangle overlaps an existing one.  Merge them. */
492
	if (xe > rptr->xmax) {
493
	    rptr->xmax = nr->xmax;	/* might be > xe if */
494
	    /* we already did a merge */
495
	    clip_rect_print('Q', "widen", rptr);
496
	}
497
	ACCUM_FREE("free", nr);
498
	if (x >= rptr->xmin)
499
	    goto out;
500
	/* Might overlap other rectangles to the left. */
501
	rptr->xmin = x;
502
	nr = rptr;
503
	ACCUM_REMOVE(rptr);
504
	clip_rect_print('Q', "merge", nr);
505
    }
506
    ACCUM_ADD_AFTER(nr, rptr);
507
out:
508
    /* Check whether there are only 0 or 1 rectangles left. */
509
    if (adev->list.count <= 1) {
510
	/* We're switching from a list to at most 1 rectangle. */
511
	/* Free the head and tail entries. */
512
	gs_memory_t *mem = adev->list_memory;
513
	gx_clip_rect *single = adev->list.head->next;
514
 
515
	if (single != adev->list.tail) {
516
	    adev->list.single = *single;
517
	    gs_free_object(mem, single, "accum_free_rect(single)");
518
	    adev->list.single.next = adev->list.single.prev = 0;
519
	}
520
	gs_free_object(mem, adev->list.tail, "accum_free_rect(tail)");
521
	gs_free_object(mem, adev->list.head, "accum_free_rect(head)");
522
	adev->list.head = 0;
523
	adev->list.tail = 0;
524
    }
525
    /* Check whether there is still more of the new band to process. */
526
    if (y < ymin) {
527
	/* Continue with the bottom part of the new rectangle. */
528
	clip_rect_print('Q', " ymin", nr);
529
	ye = ymin;
530
	goto top;
531
    }
532
    return 0;
533
}