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) 1989, 1995, 1996, 1997, 1998, 1999 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: gspath.c,v 1.10 2004/08/31 13:23:16 igor Exp $ */
18
/* Basic path routines for Ghostscript library */
19
#include "gx.h"
20
#include "math_.h"
21
#include "gserrors.h"
22
#include "gxfixed.h"
23
#include "gxmatrix.h"
24
#include "gscoord.h"		/* requires gsmatrix.h */
25
#include "gspath.h"		/* for checking prototypes */
26
#include "gzstate.h"
27
#include "gzpath.h"
28
#include "gxdevice.h"		/* for gxcpath.h */
29
#include "gxdevmem.h"		/* for gs_device_is_memory */
30
#include "gzcpath.h"
31
 
32
/* ------ Miscellaneous ------ */
33
 
34
int
35
gs_newpath(gs_state * pgs)
36
{
37
    pgs->current_point_valid = false;
38
    return gx_path_new(pgs->path);
39
}
40
 
41
int
42
gs_closepath(gs_state * pgs)
43
{
44
    gx_path *ppath = pgs->path;
45
    int code = gx_path_close_subpath(ppath);
46
 
47
    if (code < 0)
48
	return code;
49
    pgs->current_point = pgs->subpath_start;
50
    return code;
51
}
52
 
53
int
54
gs_upmergepath(gs_state * pgs)
55
{
56
    return gx_path_add_path(pgs->saved->path, pgs->path);
57
}
58
 
59
/* Get the current path (for internal use only). */
60
gx_path *
61
gx_current_path(const gs_state * pgs)
62
{
63
    return pgs->path;
64
}
65
 
66
/* ------ Points and lines ------ */
67
 
68
/*
69
 * Define clamped values for out-of-range coordinates.
70
 * Currently the path drawing routines can't handle values
71
 * close to the edge of the representable space.
72
 */
73
#define max_coord_fixed (max_fixed - int2fixed(1000))	/* arbitrary */
74
#define min_coord_fixed (-max_coord_fixed)
75
private inline void
76
clamp_point(gs_fixed_point * ppt, floatp x, floatp y)
77
{
78
#define clamp_coord(xy)\
79
  ppt->xy = (xy > fixed2float(max_coord_fixed) ? max_coord_fixed :\
80
	     xy < fixed2float(min_coord_fixed) ? min_coord_fixed :\
81
	     float2fixed(xy))
82
    clamp_coord(x);
83
    clamp_coord(y);
84
#undef clamp_coord
85
}
86
 
87
int
88
gs_currentpoint(gs_state * pgs, gs_point * ppt)
89
{
90
    if (!pgs->current_point_valid)
91
	return_error(gs_error_nocurrentpoint);
92
    return gs_itransform(pgs, pgs->current_point.x, 
93
			      pgs->current_point.y, ppt);
94
}
95
 
96
private inline int 
97
gs_point_transform_compat(floatp x, floatp y, const gs_matrix_fixed *m, gs_point *pt)
98
{
99
#if !PRECISE_CURRENTPOINT
100
    gs_fixed_point p;
101
    int code = gs_point_transform2fixed(m, x, y, &p);
102
 
103
    if (code < 0)
104
	return code;
105
    pt->x = fixed2float(p.x);
106
    pt->y = fixed2float(p.y);
107
    return 0;
108
#else
109
    return gs_point_transform(x, y, (const gs_matrix *)m, pt);
110
#endif
111
}
112
 
113
private inline int 
114
gs_distance_transform_compat(floatp x, floatp y, const gs_matrix_fixed *m, gs_point *pt)
115
{
116
#if !PRECISE_CURRENTPOINT
117
    gs_fixed_point p;
118
    int code = gs_distance_transform2fixed(m, x, y, &p);
119
 
120
    if (code < 0)
121
	return code;
122
    pt->x = fixed2float(p.x);
123
    pt->y = fixed2float(p.y);
124
    return 0;
125
#else
126
    return gs_distance_transform(x, y, (const gs_matrix *)m, pt);
127
#endif
128
}
129
 
130
private inline int
131
clamp_point_aux(bool clamp_coordinates, gs_fixed_point *ppt, floatp x, floatp y)
132
{
133
    if (!f_fits_in_bits(x, fixed_int_bits) || !f_fits_in_bits(y, fixed_int_bits)) {
134
	if (!clamp_coordinates)
135
	    return_error(gs_error_limitcheck);
136
	clamp_point(ppt, x, y);
137
    } else {
138
	/* 181-01.ps" fails with no rounding in 
139
	   "Verify as last element of a userpath and effect on setbbox." */
140
	ppt->x = float2fixed_rounded(x);
141
	ppt->y = float2fixed_rounded(y);
142
    }
143
    return 0;
144
}
145
 
146
int
147
gs_moveto_aux(gs_imager_state *pis, gx_path *ppath, floatp x, floatp y)
148
{
149
    gs_fixed_point pt;
150
    int code;
151
 
152
    code = clamp_point_aux(pis->clamp_coordinates, &pt, x, y);
153
    if (code < 0)
154
	return code;
155
    code = gx_path_add_point(ppath, pt.x, pt.y);
156
    if (code < 0)
157
	return code;
158
    ppath->start_flags = ppath->state_flags;
159
    gx_setcurrentpoint(pis, x, y);
160
    pis->subpath_start = pis->current_point;
161
    pis->current_point_valid = true;
162
    return 0;
163
}
164
 
165
int
166
gs_moveto(gs_state * pgs, floatp x, floatp y)
167
{
168
    gs_point pt;
169
    int code = gs_point_transform_compat(x, y, &pgs->ctm, &pt);
170
 
171
    if (code < 0)
172
	return code;
173
    return gs_moveto_aux((gs_imager_state *)pgs, pgs->path, pt.x, pt.y);
174
}
175
 
176
int
177
gs_rmoveto(gs_state * pgs, floatp x, floatp y)
178
{
179
    gs_point dd;
180
    int code; 
181
 
182
    if (!pgs->current_point_valid)
183
	return_error(gs_error_nocurrentpoint);
184
    code = gs_distance_transform_compat(x, y, &pgs->ctm, &dd);
185
    if (code < 0)
186
	return code;
187
    /* fixme : check in range. */
188
    return gs_moveto_aux((gs_imager_state *)pgs, pgs->path, 
189
		dd.x + pgs->current_point.x, dd.y + pgs->current_point.y);
190
}
191
 
192
private inline int
193
gs_lineto_aux(gs_state * pgs, floatp x, floatp y)
194
{
195
    gx_path *ppath = pgs->path;
196
    gs_fixed_point pt;
197
    int code;
198
 
199
    code = clamp_point_aux(pgs->clamp_coordinates, &pt, x, y);
200
    if (code < 0)
201
	return code;
202
    code = gx_path_add_line(ppath, pt.x, pt.y);
203
    if (code < 0)
204
	return code;
205
    gx_setcurrentpoint(pgs, x, y);
206
    return 0;
207
}
208
 
209
int
210
gs_lineto(gs_state * pgs, floatp x, floatp y)
211
{
212
    gs_point pt;
213
    int code = gs_point_transform_compat(x, y, &pgs->ctm, &pt);
214
 
215
    if (code < 0)
216
	return code;
217
    return gs_lineto_aux(pgs, pt.x, pt.y);
218
}
219
 
220
int
221
gs_rlineto(gs_state * pgs, floatp x, floatp y)
222
{
223
    gs_point dd;
224
    int code; 
225
 
226
    if (!pgs->current_point_valid)
227
	return_error(gs_error_nocurrentpoint);
228
    code = gs_distance_transform_compat(x, y, &pgs->ctm, &dd);
229
    if (code < 0)
230
	return code;
231
    /* fixme : check in range. */
232
    return gs_lineto_aux(pgs, dd.x + pgs->current_point.x, 
233
                              dd.y + pgs->current_point.y);
234
}
235
 
236
/* ------ Curves ------ */
237
 
238
private inline int
239
gs_curveto_aux(gs_state * pgs,
240
	   floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3)
241
{
242
    gs_fixed_point p1, p2, p3;
243
    int code;
244
    gx_path *ppath = pgs->path;
245
 
246
    code = clamp_point_aux(pgs->clamp_coordinates, &p1, x1, y1);
247
    if (code < 0)
248
	return code;
249
    code = clamp_point_aux(pgs->clamp_coordinates, &p2, x2, y2);
250
    if (code < 0)
251
	return code;
252
    code = clamp_point_aux(pgs->clamp_coordinates, &p3, x3, y3);
253
    if (code < 0)
254
	return code;
255
    code = gx_path_add_curve(ppath, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
256
    if (code < 0)
257
	return code;
258
    gx_setcurrentpoint(pgs, x3, y3);
259
    return 0;
260
}
261
 
262
int
263
gs_curveto(gs_state * pgs,
264
	   floatp x1, floatp y1, floatp x2, floatp y2, floatp x3, floatp y3)
265
{
266
    gs_point pt1, pt2, pt3;
267
    int code;
268
 
269
    code = gs_point_transform_compat(x1, y1, &pgs->ctm, &pt1);
270
    if (code < 0)
271
	return code;
272
    code = gs_point_transform_compat(x2, y2, &pgs->ctm, &pt2);
273
    if (code < 0)
274
	return code;
275
    code = gs_point_transform_compat(x3, y3, &pgs->ctm, &pt3);
276
    if (code < 0)
277
	return code;
278
    return gs_curveto_aux(pgs,   pt1.x, pt1.y,   pt2.x, pt2.y,   pt3.x, pt3.y);
279
}
280
 
281
int
282
gs_rcurveto(gs_state * pgs,
283
     floatp dx1, floatp dy1, floatp dx2, floatp dy2, floatp dx3, floatp dy3)
284
{
285
    gs_point dd1, dd2, dd3;
286
    int code; 
287
 
288
    if (!pgs->current_point_valid)
289
	return_error(gs_error_nocurrentpoint);
290
    code = gs_distance_transform_compat(dx1, dy1, &pgs->ctm, &dd1);
291
    if (code < 0)
292
	return code;
293
    code = gs_distance_transform_compat(dx2, dy2, &pgs->ctm, &dd2);
294
    if (code < 0)
295
	return code;
296
    code = gs_distance_transform_compat(dx3, dy3, &pgs->ctm, &dd3);
297
    if (code < 0)
298
	return code;
299
    /* fixme : check in range. */
300
    return gs_curveto_aux(pgs, dd1.x + pgs->current_point.x, dd1.y + pgs->current_point.y, 
301
			       dd2.x + pgs->current_point.x, dd2.y + pgs->current_point.y, 
302
			       dd3.x + pgs->current_point.x, dd3.y + pgs->current_point.y);
303
}
304
 
305
/* ------ Clipping ------ */
306
 
307
/* Forward references */
308
private int common_clip(gs_state *, int);
309
 
310
/*
311
 * Return the effective clipping path of a graphics state.  Sometimes this
312
 * is the intersection of the clip path and the view clip path; sometimes it
313
 * is just the clip path.  We aren't sure what the correct algorithm is for
314
 * this: for now, we use view clipping unless the current device is a memory
315
 * device.  This takes care of the most important case, where the current
316
 * device is a cache device.
317
 */
318
int
319
gx_effective_clip_path(gs_state * pgs, gx_clip_path ** ppcpath)
320
{
321
    gs_id view_clip_id =
322
	(pgs->view_clip == 0 || pgs->view_clip->rule == 0 ? gs_no_id :
323
	 pgs->view_clip->id);
324
 
325
    if (gs_device_is_memory(pgs->device)) {
326
	*ppcpath = pgs->clip_path;
327
	return 0;
328
    }
329
    if (pgs->effective_clip_id == pgs->clip_path->id &&
330
	pgs->effective_view_clip_id == view_clip_id
331
	) {
332
	*ppcpath = pgs->effective_clip_path;
333
	return 0;
334
    }
335
    /* Update the cache. */
336
    if (view_clip_id == gs_no_id) {
337
	if (!pgs->effective_clip_shared)
338
	    gx_cpath_free(pgs->effective_clip_path, "gx_effective_clip_path");
339
	pgs->effective_clip_path = pgs->clip_path;
340
	pgs->effective_clip_shared = true;
341
    } else {
342
	gs_fixed_rect cbox, vcbox;
343
 
344
	gx_cpath_inner_box(pgs->clip_path, &cbox);
345
	gx_cpath_outer_box(pgs->view_clip, &vcbox);
346
	if (rect_within(vcbox, cbox)) {
347
	    if (!pgs->effective_clip_shared)
348
		gx_cpath_free(pgs->effective_clip_path,
349
			      "gx_effective_clip_path");
350
	    pgs->effective_clip_path = pgs->view_clip;
351
	    pgs->effective_clip_shared = true;
352
	} else {
353
	    /* Construct the intersection of the two clip paths. */
354
	    int code;
355
	    gx_clip_path ipath;
356
	    gx_path vpath;
357
	    gx_clip_path *npath = pgs->effective_clip_path;
358
 
359
	    if (pgs->effective_clip_shared) {
360
		npath = gx_cpath_alloc(pgs->memory, "gx_effective_clip_path");
361
		if (npath == 0)
362
		    return_error(gs_error_VMerror);
363
	    }
364
	    gx_cpath_init_local(&ipath, pgs->memory);
365
	    code = gx_cpath_assign_preserve(&ipath, pgs->clip_path);
366
	    if (code < 0)
367
		return code;
368
	    gx_path_init_local(&vpath, pgs->memory);
369
	    code = gx_cpath_to_path(pgs->view_clip, &vpath);
370
	    if (code < 0 ||
371
		(code = gx_cpath_clip(pgs, &ipath, &vpath,
372
				      gx_rule_winding_number)) < 0 ||
373
		(code = gx_cpath_assign_free(npath, &ipath)) < 0
374
		)
375
		DO_NOTHING;
376
	    gx_path_free(&vpath, "gx_effective_clip_path");
377
	    gx_cpath_free(&ipath, "gx_effective_clip_path");
378
	    if (code < 0)
379
		return code;
380
	    pgs->effective_clip_path = npath;
381
	    pgs->effective_clip_shared = false;
382
	}
383
    }
384
    pgs->effective_clip_id = pgs->effective_clip_path->id;
385
    pgs->effective_view_clip_id = view_clip_id;
386
    *ppcpath = pgs->effective_clip_path;
387
    return 0;
388
}
389
 
390
#ifdef DEBUG
391
/* Note that we just set the clipping path (internal). */
392
private void
393
note_set_clip_path(const gs_state * pgs)
394
{
395
    if (gs_debug_c('P')) {
396
	dlprintf("[P]Clipping path:\n");
397
	gx_cpath_print(pgs->clip_path);
398
    }
399
}
400
#else
401
#  define note_set_clip_path(pgs) DO_NOTHING
402
#endif
403
 
404
int
405
gs_clippath(gs_state * pgs)
406
{
407
    gx_path cpath;
408
    int code;
409
 
410
    gx_path_init_local(&cpath, pgs->path->memory);
411
    code = gx_cpath_to_path(pgs->clip_path, &cpath);
412
    if (code >= 0)
413
	code = gx_path_assign_free(pgs->path, &cpath);
414
    if (code < 0)
415
	gx_path_free(&cpath, "gs_clippath");
416
    return code;
417
}
418
 
419
int
420
gs_initclip(gs_state * pgs)
421
{
422
    gs_fixed_rect box;
423
    int code = gx_default_clip_box(pgs, &box);
424
 
425
    if (code < 0)
426
	return code;
427
    return gx_clip_to_rectangle(pgs, &box);
428
}
429
 
430
int
431
gs_clip(gs_state * pgs)
432
{
433
    return common_clip(pgs, gx_rule_winding_number);
434
}
435
 
436
int
437
gs_eoclip(gs_state * pgs)
438
{
439
    return common_clip(pgs, gx_rule_even_odd);
440
}
441
 
442
private int
443
common_clip(gs_state * pgs, int rule)
444
{
445
    int code = gx_cpath_clip(pgs, pgs->clip_path, pgs->path, rule);
446
    if (code < 0)
447
	return code;
448
    pgs->clip_path->rule = rule;
449
    note_set_clip_path(pgs);
450
    return 0;
451
}
452
 
453
/* Establish a rectangle as the clipping path. */
454
/* Used by initclip and by the character and Pattern cache logic. */
455
int
456
gx_clip_to_rectangle(gs_state * pgs, gs_fixed_rect * pbox)
457
{
458
    int code = gx_cpath_from_rectangle(pgs->clip_path, pbox);
459
 
460
    if (code < 0)
461
	return code;
462
    pgs->clip_path->rule = gx_rule_winding_number;
463
    note_set_clip_path(pgs);
464
    return 0;
465
}
466
 
467
/* Set the clipping path to the current path, without intersecting. */
468
/* This is very inefficient right now. */
469
int
470
gx_clip_to_path(gs_state * pgs)
471
{
472
    gs_fixed_rect bbox;
473
    int code;
474
 
475
    if ((code = gx_path_bbox(pgs->path, &bbox)) < 0 ||
476
	(code = gx_clip_to_rectangle(pgs, &bbox)) < 0 ||
477
	(code = gs_clip(pgs)) < 0
478
	)
479
	return code;
480
    note_set_clip_path(pgs);
481
    return 0;
482
}
483
 
484
/* Get the default clipping box. */
485
int
486
gx_default_clip_box(const gs_state * pgs, gs_fixed_rect * pbox)
487
{
488
    register gx_device *dev = gs_currentdevice(pgs);
489
    gs_rect bbox;
490
    gs_matrix imat;
491
    int code;
492
 
493
    if (dev->ImagingBBox_set) {	/* Use the ImagingBBox, relative to default user space. */
494
	gs_defaultmatrix(pgs, &imat);
495
	bbox.p.x = dev->ImagingBBox[0];
496
	bbox.p.y = dev->ImagingBBox[1];
497
	bbox.q.x = dev->ImagingBBox[2];
498
	bbox.q.y = dev->ImagingBBox[3];
499
    } else {			/* Use the MediaSize indented by the HWMargins, */
500
	/* relative to unrotated user space adjusted by */
501
	/* the Margins.  (We suspect this isn't quite right, */
502
	/* but the whole issue of "margins" is such a mess that */
503
	/* we don't think we can do any better.) */
504
	(*dev_proc(dev, get_initial_matrix)) (dev, &imat);
505
	/* Adjust for the Margins. */
506
	imat.tx += dev->Margins[0] * dev->HWResolution[0] /
507
	    dev->MarginsHWResolution[0];
508
	imat.ty += dev->Margins[1] * dev->HWResolution[1] /
509
	    dev->MarginsHWResolution[1];
510
	bbox.p.x = dev->HWMargins[0];
511
	bbox.p.y = dev->HWMargins[1];
512
	bbox.q.x = dev->MediaSize[0] - dev->HWMargins[2];
513
	bbox.q.y = dev->MediaSize[1] - dev->HWMargins[3];
514
    }
515
    code = gs_bbox_transform(&bbox, &imat, &bbox);
516
    if (code < 0)
517
	return code;
518
    /* Round the clipping box so that it doesn't get ceilinged. */
519
    pbox->p.x = fixed_rounded(float2fixed(bbox.p.x));
520
    pbox->p.y = fixed_rounded(float2fixed(bbox.p.y));
521
    pbox->q.x = fixed_rounded(float2fixed(bbox.q.x));
522
    pbox->q.y = fixed_rounded(float2fixed(bbox.q.y));
523
    return 0;
524
}