/* Copyright (C) 1989, 2000 artofcode LLC.  All rights reserved.
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA, 02111-1307.

*/

/*$Id: gzpath.h,v 1.3.6.1.2.1 2003/01/17 00:49:04 giles Exp $ */
/* Structure and internal procedure definitions for paths */
/* Requires gxfixed.h */

#ifndef gzpath_INCLUDED
#  define gzpath_INCLUDED

#include "gxpath.h"
#include "gsmatrix.h"
#include "gsrefct.h"
#include "gsstype.h"		/* for extern_st */

/*
 * Paths are represented as a linked list of line or curve segments,
 * similar to what pathforall reports.
 */

/*
 * Define path segment types: segment start, line, or Bezier curve.
 * We have a special type for the line added by closepath.
 */
typedef enum {
    s_start,
    s_line,
    s_line_close,
    s_curve
} segment_type;

/* Define the common structure for all segments. */
#define segment_common\
	segment *prev;\
	segment *next;\
	ushort /*segment_type*/ type;\
	ushort /*segment_notes*/ notes;\
	gs_fixed_point pt;		/* initial point for starts, */\
				/* final point for others */

/* Forward declarations for structure types */
#ifndef segment_DEFINED
#  define segment_DEFINED
typedef struct segment_s segment;

#endif
typedef struct subpath_s subpath;

/*
 * Define a generic segment.  This is never instantiated,
 * but we define a descriptor anyway for the benefit of subclasses.
 */
struct segment_s {
    segment_common
};

#define private_st_segment()	/* in gxpath.c */\
  gs_private_st_ptrs2(st_segment, struct segment_s, "segment",\
    segment_enum_ptrs, segment_reloc_ptrs, prev, next)

/* Line segments have no special data. */
typedef struct {
    segment_common
} line_segment;

#define private_st_line()	/* in gxpath.c */\
  gs_private_st_suffix_add0(st_line, line_segment, "line",\
    line_enum_ptrs, line_reloc_ptrs, st_segment)

/* Line_close segments are for the lines appended by closepath. */
/* They point back to the subpath being closed. */
typedef struct {
    segment_common
    subpath * sub;
} line_close_segment;

#define private_st_line_close()	/* in gxpath.c */\
  gs_private_st_suffix_add1(st_line_close, line_close_segment, "close",\
    close_enum_ptrs, close_reloc_ptrs, st_segment, sub)

/*
 * We use two different representations for curve segments: one defined by
 * two endpoints (p0, p3) and two control points (p1, p2), and one defined
 * by two sets of parametric cubic coefficients (ax ... dy).  Here is how
 * they are related (v = x or y).  We spell out some multiplies by 3 for
 * the benefit of compilers too simple to optimize this.
 */
#define curve_points_to_coefficients(v0, v1, v2, v3, a, b, c, t01, t12)\
  (/*d = (v0),*/\
   t01 = (v1) - (v0), c = (t01 << 1) + t01,\
   t12 = (v2) - (v1), b = (t12 << 1) + t12 - c,\
   a = (v3) - b - c - (v0))
/*
 * or conversely
 */
#define curve_coefficients_to_points(a, b, c, d, v1, v2, v3)\
  (/*v0 = (d),*/\
   v1 = (d) + ((c) / 3),\
   v2 = v1 + (((b) + (c)) / 3),\
   v3 = (a) + (b) + (c) + (d))

/* Curve segments store the control points. */
typedef struct {
    segment_common
    gs_fixed_point p1, p2;
} curve_segment;

#define private_st_curve()	/* in gxpath.c */\
  gs_private_st_suffix_add0_local(st_curve, curve_segment, "curve",\
    segment_enum_ptrs, segment_reloc_ptrs, st_segment)

/*
 * Define a start segment.  This serves as the head of a subpath.
 * The closer is only used temporarily when filling,
 * to close an open subpath.
 */
struct subpath_s {
    segment_common
    segment * last;		/* last segment of subpath, */
    /* points back to here if empty */
    int curve_count;		/* # of curves */
    line_close_segment closer;
    char /*bool */ is_closed;	/* true if subpath is closed */
};

#define private_st_subpath()	/* in gxpath.c */\
  gs_private_st_suffix_add1(st_subpath, subpath, "subpath",\
    subpath_enum_ptrs, subpath_reloc_ptrs, st_segment, last)

/* Test whether a subpath is a rectangle; if so, also return */
/* the start of the next subpath. */
gx_path_rectangular_type
gx_subpath_is_rectangular(P3(const subpath * pstart, gs_fixed_rect * pbox,
			     const subpath ** ppnext));

#define gx_subpath_is_rectangle(pstart, pbox, ppnext)\
  (gx_subpath_is_rectangular(pstart, pbox, ppnext) != prt_none)

/* Curve manipulation */

/* Return the smallest value k such that 2^k segments will approximate */
/* the curve to within the desired flatness. */
int gx_curve_log2_samples(P4(fixed, fixed, const curve_segment *, fixed));

/*
 * If necessary, find the values of t (never more than 2) which split the
 * curve into monotonic parts.  Return the number of split points.
 */
int gx_curve_monotonic_points(P5(fixed, fixed, fixed, fixed, double[2]));

/* Split a curve at an arbitrary value of t. */
void gx_curve_split(P6(fixed, fixed, const curve_segment *, double,
		       curve_segment *, curve_segment *));

/* Flatten a partial curve by sampling (internal procedure). */
int gx_flatten_sample(P4(gx_path *, int, curve_segment *, segment_notes));

/* Initialize a cursor for rasterizing a monotonic curve. */
typedef struct curve_cursor_s {
    /* Following are set at initialization */
    int k;			/* 2^k segments */
    gs_fixed_point p0;		/* starting point */
    const curve_segment *pc;	/* other points */
    fixed a, b, c;		/* curve coefficients */
    double da, db, dc;		/* scaled double versions of a, b, c */
    bool double_set;		/* true if da/b/c set */
    int fixed_limit;		/* can do in fixed point if t <= limit */
    /* Following are updated dynamically. */
    struct ccc_ {		/* one-element cache */
	fixed ky0, ky3;		/* key (range) */
	fixed xl, xd;		/* value */
    } cache;
} curve_cursor;
void gx_curve_cursor_init(P5(curve_cursor * prc, fixed x0, fixed y0,
			     const curve_segment * pc, int k));

/* Return the value of X at a given Y value on a monotonic curve. */
/* y must lie between prc->p0.y and prc->pt.y. */
fixed gx_curve_x_at_y(P2(curve_cursor * prc, fixed y));

/*
 * The path state flags reflect the most recent operation on the path
 * as follows:
 *      Operation       position_valid  subpath_open    is_drawing
 *      newpath         no              no              no
 *      moveto          yes             yes             no
 *      lineto/curveto  yes             yes             yes
 *      closepath       yes             no              no
 * If position_valid is true, outside_range reflects whether the most
 * recent operation went outside of the representable coordinate range.
 * If this is the case, the corresponding member of position (x and/or y)
 * is min_fixed or max_fixed, and outside_position is the true position.
 */
/*
 */
typedef enum {
    /* Individual flags.  These may be or'ed together, per above. */
    psf_position_valid = 1,
    psf_subpath_open = 2,
    psf_is_drawing = 4,
    psf_outside_range = 8,
    /* Values stored by path building operations. */
    psf_last_newpath = 0,
    psf_last_moveto = psf_position_valid | psf_subpath_open,
    psf_last_draw = psf_position_valid | psf_subpath_open | psf_is_drawing,
    psf_last_closepath = psf_position_valid
} gx_path_state_flags;

/*
 * Individual tests
 */
#define path_position_valid(ppath)\
  (((ppath)->state_flags & psf_position_valid) != 0)
#define path_subpath_open(ppath)\
  (((ppath)->state_flags & psf_subpath_open) != 0)
#define path_is_drawing(ppath)\
  (((ppath)->state_flags & psf_is_drawing) != 0)
#define path_outside_range(ppath)\
  (((ppath)->state_flags & psf_outside_range) != 0)
/*
 * Composite tests
 */
#define path_last_is_moveto(ppath)\
  (((ppath)->state_flags & ~psf_outside_range) == psf_last_moveto)
#define path_position_in_range(ppath)\
  (((ppath)->state_flags & (psf_position_valid + psf_outside_range)) ==\
   psf_position_valid)
#define path_start_outside_range(ppath)\
  ((ppath)->state_flags != 0 &&\
   ((ppath)->start_flags & psf_outside_range) != 0)
/*
 * Updating operations
 */
#define path_update_newpath(ppath)\
  ((ppath)->state_flags = psf_last_newpath)
#define path_update_moveto(ppath)\
  ((ppath)->state_flags = (ppath)->start_flags = psf_last_moveto)
#define path_update_draw(ppath)\
  ((ppath)->state_flags = psf_last_draw)
#define path_update_closepath(ppath)\
  ((ppath)->state_flags = psf_last_closepath)
#define path_set_outside_position(ppath, px, py)\
  ((ppath)->outside_position.x = (px),\
   (ppath)->outside_position.y = (py),\
   (ppath)->state_flags |= psf_outside_range)

/*
 * In order to be able to reclaim path segments at the right time, we need
 * to reference-count them.  To minimize disruption, we would like to do
 * this by creating a structure (gx_path_segments) consisting of only a
 * reference count that counts the number of paths that share the same
 * segments.  (Logically, we should put the segments themselves --
 * first/last_subpath, subpath/curve_count -- in this object, but that would
 * cause too much disruption to existing code.)  However, we need to put at
 * least first_subpath and current_subpath in this structure so that we can
 * free the segments when the reference count becomes zero.
 */
typedef struct gx_path_segments_s {
    rc_header rc;
    struct psc_ {
	subpath *subpath_first;
	subpath *subpath_current;
    } contents;
} gx_path_segments;

#define private_st_path_segments()	/* in gxpath.c */\
  gs_private_st_ptrs2(st_path_segments, gx_path_segments, "path segments",\
    path_segments_enum_ptrs, path_segments_reloc_ptrs,\
    contents.subpath_first, contents.subpath_current)

/* Record how a path was allocated, so freeing will do the right thing. */
typedef enum {
    path_allocated_on_stack,	/* on stack */
    path_allocated_contained,	/* inside another object */
    path_allocated_on_heap	/* on the heap */
} gx_path_allocation_t;

/* Here is the actual structure of a path. */
struct gx_path_s {
    /*
     * In order to be able to have temporary paths allocated entirely
     * on the stack, we include a segments structure within the path,
     * used only for this purpose.  In order to avoid having the
     * path's segments pointer point into the middle of an object,
     * the segments structure must come first.
     *
     * Note that since local_segments is used only for temporary paths
     * on the stack, and not for path structures in allocated memory,
     * we don't declare any pointers in it for the GC.  (As it happens,
     * there aren't any such pointers at the moment, but this could
     * change.)
     */
    gx_path_segments local_segments;
    gs_memory_t *memory;
    gx_path_allocation_t allocation;	/* how this path was allocated */
    gx_path_segments *segments;
    gs_fixed_rect bbox;		/* bounding box (in device space) */
    segment *box_last;		/* bbox incorporates segments */
				/* up to & including this one */
#define first_subpath segments->contents.subpath_first	/* (hack) */
#define current_subpath segments->contents.subpath_current	/* (ditto) */
    /*
     * Note: because of bugs in the AIX 4.3.1 xlc compiler, the byte-valued
     * members must not be the last ones in the structure.
     */
    byte /*gx_path_state_flags*/ start_flags;		/* flags of moveto */
    byte /*gx_path_state_flags*/ state_flags;		/* (see above) */
    byte /*bool*/ bbox_set;	/* true if setbbox is in effect */
    byte _pad;			/* just in case the compiler doesn't do it */
    int subpath_count;
    int curve_count;
    gs_fixed_point position;	/* current position */
    gs_point outside_position;	/* position if outside_range is set */
    gs_point outside_start;	/* outside_position of last moveto */
};

/* st_path should be private, but it's needed for the clip_path subclass. */
extern_st(st_path);
#define public_st_path()	/* in gxpath.c */\
  gs_public_st_ptrs2(st_path, gx_path, "path",\
    path_enum_ptrs, path_reloc_ptrs, segments, box_last)
#define st_path_max_ptrs 2

/* Path enumeration structure */
struct gs_path_enum_s {
    gs_memory_t *memory;
    gs_matrix mat;		/* CTM for inverse-transforming points */
    const segment *pseg;
    const gx_path *path;	/* path being enumerated */
    gx_path *copied_path;	/* if the path was copied, this is the */
    /* the same as path, to be released */
    /* when done enumerating */
    bool moveto_done;		/* have we reported a final moveto yet? */
    segment_notes notes;	/* notes from most recent segment */
};

/* We export st_path_enum only so that st_cpath_enum can subclass it. */
extern_st(st_path_enum);
#define public_st_path_enum()	/* in gxpath2.c */\
  gs_public_st_ptrs3(st_path_enum, gs_path_enum, "gs_path_enum",\
    path_enum_enum_ptrs, path_enum_reloc_ptrs, pseg, path, copied_path)

/* Inline path accessors. */
#define gx_path_has_curves_inline(ppath)\
  ((ppath)->curve_count != 0)
#define gx_path_has_curves(ppath)\
  gx_path_has_curves_inline(ppath)
#define gx_path_is_void_inline(ppath)\
  ((ppath)->first_subpath == 0)
#define gx_path_is_void(ppath)\
  gx_path_is_void_inline(ppath)
#define gx_path_subpath_count(ppath)\
  ((ppath)->subpath_count)
#define gx_path_is_shared(ppath)\
  ((ppath)->segments->rc.ref_count > 1)

/* Macros equivalent to a few heavily used procedures. */
/* Be aware that these macros may evaluate arguments more than once. */
#define gx_path_current_point_inline(ppath,ppt)\
 ( !path_position_valid(ppath) ? gs_note_error(gs_error_nocurrentpoint) :\
   ((ppt)->x = ppath->position.x, (ppt)->y = ppath->position.y, 0) )
/* ...rel_point rather than ...relative_point is because */
/* some compilers dislike identifiers of >31 characters. */
#define gx_path_add_rel_point_inline(ppath,dx,dy)\
 ( !path_position_in_range(ppath) || ppath->bbox_set ?\
   gx_path_add_relative_point(ppath, dx, dy) :\
   (ppath->position.x += dx, ppath->position.y += dy,\
    path_update_moveto(ppath), 0) )

#endif /* gzpath_INCLUDED */
