/* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
Distributed by Free Software Foundation, Inc.
This file is part of Ghostscript.
Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing. Refer
to the Ghostscript General Public License for full details.
Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License. A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities. It should be in a file named COPYING. Among other
things, the copyright notice and this notice must be preserved on all
/* Private path routines for Ghostscript library */
/* These routines all assume that all points are */
/* already in device coordinates, and in fixed representation. */
/* As usual, they return either 0 or a (negative) error code. */
private subpath
*path_alloc_copy(P1(gx_path
*));
private int gx_path_new_subpath(P1(gx_path
*));
void gx_print_segment(P1(segment
*));
/* ------ Initialize/free paths ------ */
gx_path_init(register gx_path
*ppath
, gs_memory_procs
*pprocs
)
{ ppath
->memory_procs
= *pprocs
;
ppath
->position_valid
= 0;
ppath
->first_subpath
= ppath
->current_subpath
= 0;
ppath
->subpath_count
= 0;
ppath
->shares_segments
= 0;
/* Release the contents of a path. We do this in reverse order */
/* so as to maximize LIFO allocator behavior. */
gx_path_release(gx_path
*ppath
)
if ( ppath
->first_subpath
== 0 ) return; /* empty path */
if ( ppath
->shares_segments
) return; /* segments are shared */
pseg
= (segment
*)ppath
->current_subpath
->last
;
{ segment
*prev
= pseg
->prev
;
static uint sizes
[] = { segment_type_sizes
};
dprintf("[p]release"), gx_print_segment(pseg
);
(*ppath
->memory_procs
.free
)((char *)pseg
, 1, sizes
[(int)pseg
->type
], "gx_path_release");
ppath
->first_subpath
= 0; /* prevent re-release */
/* Mark a path as shared */
gx_path_share(gx_path
*ppath
)
{ if ( ppath
->first_subpath
) ppath
->shares_segments
= 1;
/* ------ Incremental path building ------ */
/* Macro for opening the current subpath. */
/* ppath points to the path; psub has been set to ppath->current_subpath. */
if ( !ppath->subpath_open )\
if ( !ppath->position_valid )\
return_error(gs_error_nocurrentpoint);\
code = gx_path_new_subpath(ppath);\
if ( code < 0 ) return code;\
psub = ppath->current_subpath;\
/* Macros for allocating path segments. */
/* Note that they assume that ppath points to the path, */
/* and that psub points to the current subpath. */
/* We have to split the macro into two because of limitations */
/* on the size of a single statement (sigh). */
#define p_alloc(pseg,size)\
if ( gs_debug['A'] ) dprintf2("[p]%lx<%u>\n", (ulong)pseg, size)
#define p_alloc(pseg,size) 0
if(ppath->shares_segments)\
if(!(psub = path_alloc_copy(ppath)))return_error(gs_error_limitcheck)
#define path_alloc_segment(pseg,ctype,stype,cname)\
if( !(pseg = (ctype *)(*ppath->memory_procs.alloc)(1, sizeof(ctype), cname)) )\
return_error(gs_error_limitcheck);\
p_alloc((char *)pseg, sizeof(ctype));\
pseg->type = stype, pseg->next = 0
#define path_alloc_link(pseg)\
{ segment *prev = psub->last;\
prev->next = (segment *)pseg;\
psub->last = (segment *)pseg;\
gx_path_new_subpath(gx_path
*ppath
)
{ subpath
*psub
= ppath
->current_subpath
;
path_alloc_segment(spp
, subpath
, s_start
, "gx_path_new_subpath");
spp
->last
= (segment
*)spp
;
spp
->pt
= ppath
->position
;
if ( !psub
) /* first subpath */
{ ppath
->first_subpath
= spp
;
{ segment
*prev
= psub
->last
;
prev
->next
= (segment
*)spp
;
ppath
->current_subpath
= spp
;
dprintf("[p]"), gx_print_segment((segment
*)spp
);
/* Add a point to the current path (moveto). */
gx_path_add_point(register gx_path
*ppath
, fixed x
, fixed y
)
{ ppath
->subpath_open
= 0;
ppath
->position_valid
= 1;
/* Add a relative point to the current path (rmoveto). */
gx_path_add_relative_point(register gx_path
*ppath
, fixed dx
, fixed dy
)
{ if ( !ppath
->position_valid
)
return_error(gs_error_nocurrentpoint
);
/* Set the segment point and the current point in the path. */
/* Assumes ppath points to the path. */
#define path_set_point(pseg, fx, fy)\
(pseg)->pt.x = ppath->position.x = (fx),\
(pseg)->pt.y = ppath->position.y = (fy)
/* Add a line to the current path (lineto). */
gx_path_add_line(gx_path
*ppath
, fixed x
, fixed y
)
{ subpath
*psub
= ppath
->current_subpath
;
register line_segment
*lp
;
path_alloc_segment(lp
, line_segment
, s_line
, "gx_path_add_line");
path_set_point(lp
, x
, y
);
dprintf("[p]"), gx_print_segment((segment
*)lp
);
/* Add a rectangle to the current path. */
/* This is a special case of adding a parallelogram. */
gx_path_add_rectangle(gx_path
*ppath
, fixed x0
, fixed y0
, fixed x1
, fixed y1
)
{ return gx_path_add_pgram(ppath
, x0
, y0
, x0
, y1
, x1
, y1
);
/* Add a parallelogram to the current path. */
/* This is equivalent to an add_point, three add_lines, */
/* and a close_subpath. */
gx_path_add_pgram(gx_path
*ppath
,
fixed x0
, fixed y0
, fixed x1
, fixed y1
, fixed x2
, fixed y2
)
if ( (code
= gx_path_add_point(ppath
, x0
, y0
)) < 0 ||
(code
= gx_path_add_line(ppath
, x1
, y1
)) < 0 ||
(code
= gx_path_add_line(ppath
, x2
, y2
)) < 0 ||
(code
= gx_path_add_line(ppath
, x0
+ x2
- x1
, y0
+ y2
- y1
)) < 0 ||
(code
= gx_path_close_subpath(ppath
)) < 0
/* Add a curve to the current path (curveto). */
gx_path_add_curve(gx_path
*ppath
,
fixed x1
, fixed y1
, fixed x2
, fixed y2
, fixed x3
, fixed y3
)
{ subpath
*psub
= ppath
->current_subpath
;
register curve_segment
*lp
;
path_alloc_segment(lp
, curve_segment
, s_curve
, "gx_path_add_curve");
path_set_point(lp
, x3
, y3
);
dprintf("[p]"), gx_print_segment((segment
*)lp
);
* Add an approximation of an arc to the current path.
* Parameters are the initial and final points of the arc,
* and the point at which the extended tangents meet.
* We assume that the arc is less than a semicircle.
* The arc may go either clockwise or counterclockwise.
* The approximation is a very simple one: a single curve
* whose other two control points are a fraction F of the way
* to the intersection of the tangents, where
* F = (4/3)(1 / (1 + sqrt(1+(d/r)^2)))
* where r is the radius and d is the distance from either tangent
* point to the intersection of the tangents. This produces
* a curve whose center point, as well as its ends, lies on
* Because F has to be computed in user space, we let the client
* compute it and pass it in as an argument.
gx_path_add_arc(gx_path
*ppath
,
fixed x0
, fixed y0
, fixed x3
, fixed y3
, fixed xt
, fixed yt
, floatp fraction
)
{ return gx_path_add_curve(ppath
,
x0
+ (fixed
)((xt
- x0
) * fraction
),
y0
+ (fixed
)((yt
- y0
) * fraction
),
x3
+ (fixed
)((xt
- x3
) * fraction
),
y3
+ (fixed
)((yt
- y3
) * fraction
),
/* Append a path to another path, and reset the first path. */
/* Currently this is only used to append a path to its parent */
/* (the path in the previous graphics context). */
gx_path_add_path(gx_path
*ppath
, gx_path
*ppfrom
)
{ subpath
*psub
= ppath
->current_subpath
;
if ( ppfrom
->first_subpath
) /* i.e. ppfrom not empty */
{ if ( ppath
->first_subpath
) /* i.e. ppath not empty */
{ segment
*pseg
= psub
->last
;
segment
*pfseg
= (segment
*)ppfrom
->first_subpath
;
ppath
->first_subpath
= ppfrom
->first_subpath
;
ppath
->current_subpath
= ppfrom
->current_subpath
;
/* Transfer the remaining state. */
ppath
->subpath_count
+= ppfrom
->subpath_count
;
ppath
->curve_count
+= ppfrom
->curve_count
;
ppath
->position
= ppfrom
->position
;
ppath
->position_valid
= ppfrom
->position_valid
;
ppath
->subpath_open
= ppfrom
->subpath_open
;
/* Reset the source path. */
ppfrom
->first_subpath
= 0;
/* Close the current subpath. */
gx_path_close_subpath(gx_path
*ppath
)
{ subpath
*psub
= ppath
->current_subpath
;
register line_close_segment
*lp
;
if ( !ppath
->subpath_open
) return 0;
path_alloc_segment(lp
, line_close_segment
, s_line_close
,
"gx_path_close_subpath");
path_set_point(lp
, psub
->pt
.x
, psub
->pt
.y
);
dprintf("[p]"), gx_print_segment((segment
*)lp
);
/* ------ Internal routines ------ */
/* Copy the current path, because it was shared. */
/* Return a pointer to the current subpath, or 0. */
path_alloc_copy(gx_path
*ppath
)
code
= gx_path_copy(ppath
, &path_new
);
if ( code
< 0 ) return 0;
ppath
->shares_segments
= 0;
return ppath
->current_subpath
;
/* ------ Debugging printout ------ */
/* Print out a path with a label */
gx_dump_path(gx_path
*ppath
, char *tag
)
{ dprintf2("[p]Path %lx %s:\n", (ulong
)ppath
, tag
);
gx_path_print(gx_path
*ppath
)
{ segment
*pseg
= (segment
*)ppath
->first_subpath
;
dprintf4(" subpaths=%d, curves=%d, point=(%f,%f)\n",
ppath
->subpath_count
, ppath
->curve_count
,
fixed2float(ppath
->position
.x
),
fixed2float(ppath
->position
.y
));
dprintf5(" box=(%f,%f),(%f,%f) last=%lx\n",
fixed2float(ppath
->bbox
.p
.x
), fixed2float(ppath
->bbox
.p
.y
),
fixed2float(ppath
->bbox
.q
.x
), fixed2float(ppath
->bbox
.q
.y
),
{ gx_print_segment(pseg
);
gx_print_segment(segment
*pseg
)
sprintf(out
, " %lx<%lx,%lx>: %%s (%6g,%6g) ",
(ulong
)pseg
, (ulong
)pseg
->prev
, (ulong
)pseg
->next
,
fixed2float(pseg
->pt
.x
), fixed2float(pseg
->pt
.y
));
#define psub ((subpath *)pseg)
dprintf2("#curves=%d last=%lx",
psub
->curve_count
, (ulong
)psub
->last
);
#define pcur ((curve_segment *)pseg)
dprintf4("\n\tp1=(%f,%f) p2=(%f,%f)",
fixed2float(pcur
->p1
.x
), fixed2float(pcur
->p1
.y
),
fixed2float(pcur
->p2
.x
), fixed2float(pcur
->p2
.y
));
#define plc ((line_close_segment *)pseg)
dprintf1(" %lx", (ulong
)(plc
->sub
));
sprintf(t
, "type 0x%x", pseg
->type
);
/* Print a clipping path */
gx_cpath_print(gx_clip_path
*pcpath
)
{ if ( pcpath
->segments_valid
)
gx_path_print(&pcpath
->path
);
dputs(" (segments not valid)\n");
dprintf5(" cbox=(%f,%f),(%f,%f) count=%d\n",
fixed2float(pcpath
->cbox
.p
.x
), fixed2float(pcpath
->cbox
.p
.y
),
fixed2float(pcpath
->cbox
.q
.x
), fixed2float(pcpath
->cbox
.q
.y
),
/****** SHOULD PRINT CLIP LIST ******/