[Scummvm-cvs-logs] SF.net SVN: scummvm:[53328] scummvm/trunk/engines/sword25

sev at users.sourceforge.net sev at users.sourceforge.net
Wed Oct 13 01:40:28 CEST 2010


Revision: 53328
          http://scummvm.svn.sourceforge.net/scummvm/?rev=53328&view=rev
Author:   sev
Date:     2010-10-12 23:40:27 +0000 (Tue, 12 Oct 2010)

Log Message:
-----------
SWORD25: Added minimal subset of libart inplace

Modified Paths:
--------------
    scummvm/trunk/engines/sword25/gfx/image/b25sloader.cpp
    scummvm/trunk/engines/sword25/gfx/image/imageloader.cpp
    scummvm/trunk/engines/sword25/gfx/image/pngloader.cpp
    scummvm/trunk/engines/sword25/gfx/image/vectorimage.cpp
    scummvm/trunk/engines/sword25/gfx/image/vectorimage.h
    scummvm/trunk/engines/sword25/gfx/image/vectorimagerenderer.cpp
    scummvm/trunk/engines/sword25/module.mk

Added Paths:
-----------
    scummvm/trunk/engines/sword25/gfx/image/art.cpp
    scummvm/trunk/engines/sword25/gfx/image/art.h
    scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.cpp
    scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.h
    scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.cpp
    scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.h
    scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.cpp
    scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.h
    scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp
    scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.h
    scummvm/trunk/engines/sword25/gfx/image/art_vpath_bpath.cpp

Added: scummvm/trunk/engines/sword25/gfx/image/art.cpp
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art.cpp	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,134 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Various utility functions RLL finds useful. */
+
+#include "art.h"
+
+#ifdef HAVE_UINSTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+
+/**
+ * art_die: Print the error message to stderr and exit with a return code of 1.
+ * @fmt: The printf-style format for the error message.
+ *
+ * Used for dealing with severe errors.
+ **/
+void
+art_die(const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	exit(1);
+}
+
+/**
+ * art_warn: Print the warning message to stderr.
+ * @fmt: The printf-style format for the warning message.
+ *
+ * Used for generating warnings.
+ **/
+void
+art_warn(const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+}
+
+/**
+ * art_svp_free: Free an #ArtSVP structure.
+ * @svp: #ArtSVP to free.
+ *
+ * Frees an #ArtSVP structure and all the segments in it.
+ **/
+void
+art_svp_free(ArtSVP *svp) {
+	int n_segs = svp->n_segs;
+	int i;
+
+	for (i = 0; i < n_segs; i++)
+		free(svp->segs[i].points);
+	free(svp);
+}
+
+#ifdef ART_USE_NEW_INTERSECTOR
+#define EPSILON 0
+#else
+#define EPSILON 1e-6
+#endif
+
+/**
+ * art_svp_seg_compare: Compare two segments of an svp.
+ * @seg1: First segment to compare.
+ * @seg2: Second segment to compare.
+ *
+ * Compares two segments of an svp. Return 1 if @seg2 is below or to the
+ * right of @seg1, -1 otherwise.
+ **/
+int
+art_svp_seg_compare(const void *s1, const void *s2) {
+	const ArtSVPSeg *seg1 = (ArtSVPSeg *)s1;
+	const ArtSVPSeg *seg2 = (ArtSVPSeg *)s2;
+
+	if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
+	else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
+	else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
+	else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
+	else if ((seg1->points[1].x - seg1->points[0].x) *
+	         (seg2->points[1].y - seg2->points[0].y) -
+	         (seg1->points[1].y - seg1->points[0].y) *
+	         (seg2->points[1].x - seg2->points[0].x) > 0) return 1;
+	else return -1;
+}
+
+/**
+ * art_vpath_add_point: Add point to vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @code: The pathcode for the new point.
+ * @x: The X coordinate of the new point.
+ * @y: The Y coordinate of the new point.
+ *
+ * Adds a new point to *@p_vpath, reallocating and updating *@p_vpath
+ * and *@pn_points_max as necessary. *@pn_points is incremented.
+ *
+ * This routine always adds the point after all points already in the
+ * vpath. Thus, it should be called in the order the points are
+ * desired.
+ **/
+void
+art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+                    ArtPathcode code, double x, double y) {
+	int i;
+
+	i = (*pn_points)++;
+	if (i == *pn_points_max)
+		art_expand(*p_vpath, ArtVpath, *pn_points_max);
+	(*p_vpath)[i].code = code;
+	(*p_vpath)[i].x = x;
+	(*p_vpath)[i].y = y;
+}


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art.h
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art.h	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art.h	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,163 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Simple macros to set up storage allocation and basic types for libart
+   functions. */
+
+#ifndef __ART_MISC_H__
+#define __ART_MISC_H__
+
+#include "config.h"
+
+typedef byte art_u8;
+typedef uint16 art_u16;
+typedef uint32 art_u32;
+
+#include <stdlib.h> /* for malloc, etc. */
+
+/* These aren't, strictly speaking, configuration macros, but they're
+   damn handy to have around, and may be worth playing with for
+   debugging. */
+#define art_new(type, n) ((type *)malloc ((n) * sizeof(type)))
+
+#define art_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
+
+/* This one must be used carefully - in particular, p and max should
+   be variables. They can also be pstruct->el lvalues. */
+#define art_expand(p, type, max) do { if(max) { p = art_renew (p, type, max <<= 1); } else { max = 1; p = art_new(type, 1); } } while (0)
+
+typedef int art_boolean;
+#define ART_FALSE 0
+#define ART_TRUE 1
+
+/* define pi */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif  /*  M_PI  */
+
+#ifndef M_SQRT2
+#define M_SQRT2         1.41421356237309504880  /* sqrt(2) */
+#endif  /* M_SQRT2 */
+
+/* Provide macros to feature the GCC function attribute.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4))
+#define ART_GNUC_PRINTF( format_idx, arg_idx )    \
+	__attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define ART_GNUC_NORETURN                         \
+	__attribute__((__noreturn__))
+#else   /* !__GNUC__ */
+#define ART_GNUC_PRINTF( format_idx, arg_idx )
+#define ART_GNUC_NORETURN
+#endif  /* !__GNUC__ */
+
+void ART_GNUC_NORETURN
+art_die(const char *fmt, ...) ART_GNUC_PRINTF(1, 2);
+
+void
+art_warn(const char *fmt, ...) ART_GNUC_PRINTF(1, 2);
+
+#define ART_USE_NEW_INTERSECTOR
+
+typedef struct _ArtDRect ArtDRect;
+typedef struct _ArtIRect ArtIRect;
+
+struct _ArtDRect {
+	/*< public >*/
+	double x0, y0, x1, y1;
+};
+
+struct _ArtIRect {
+	/*< public >*/
+	int x0, y0, x1, y1;
+};
+
+typedef struct _ArtPoint ArtPoint;
+
+struct _ArtPoint {
+	/*< public >*/
+	double x, y;
+};
+
+/* Basic data structures and constructors for sorted vector paths */
+
+typedef struct _ArtSVP ArtSVP;
+typedef struct _ArtSVPSeg ArtSVPSeg;
+
+struct _ArtSVPSeg {
+	int n_points;
+	int dir; /* == 0 for "up", 1 for "down" */
+	ArtDRect bbox;
+	ArtPoint *points;
+};
+
+struct _ArtSVP {
+	int n_segs;
+	ArtSVPSeg segs[1];
+};
+
+void
+art_svp_free(ArtSVP *svp);
+
+int
+art_svp_seg_compare(const void *s1, const void *s2);
+
+/* Basic data structures and constructors for bezier paths */
+
+typedef enum {
+	ART_MOVETO,
+	ART_MOVETO_OPEN,
+	ART_CURVETO,
+	ART_LINETO,
+	ART_END
+} ArtPathcode;
+
+typedef struct _ArtBpath ArtBpath;
+
+struct _ArtBpath {
+	/*< public >*/
+	ArtPathcode code;
+	double x1;
+	double y1;
+	double x2;
+	double y2;
+	double x3;
+	double y3;
+};
+
+/* Basic data structures and constructors for simple vector paths */
+
+typedef struct _ArtVpath ArtVpath;
+
+/* CURVETO is not allowed! */
+struct _ArtVpath {
+	ArtPathcode code;
+	double x;
+	double y;
+};
+
+/* Some of the functions need to go into their own modules */
+
+void
+art_vpath_add_point(ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+                    ArtPathcode code, double x, double y);
+
+ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness);
+
+#endif /* __ART_MISC_H__ */


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.cpp
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.cpp	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,1329 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2001 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* This file contains a testbed implementation of the new intersection
+   code.
+*/
+
+#include "art.h"
+#include "art_svp_intersect.h"
+
+#include <math.h> /* for sqrt */
+
+/* This can be used in production, to prevent hangs. Eventually, it
+   should not be necessary. */
+#define CHEAP_SANITYCHECK
+
+/* A priority queue - perhaps move to a separate file if it becomes
+   needed somewhere else */
+
+#define ART_PRIQ_USE_HEAP
+
+typedef struct _ArtPriQ ArtPriQ;
+typedef struct _ArtPriPoint ArtPriPoint;
+
+struct _ArtPriQ {
+	int n_items;
+	int n_items_max;
+	ArtPriPoint **items;
+};
+
+struct _ArtPriPoint {
+	double x;
+	double y;
+	void *user_data;
+};
+
+static ArtPriQ *
+art_pri_new(void) {
+	ArtPriQ *result = art_new(ArtPriQ, 1);
+
+	result->n_items = 0;
+	result->n_items_max = 16;
+	result->items = art_new(ArtPriPoint *, result->n_items_max);
+	return result;
+}
+
+static void
+art_pri_free(ArtPriQ *pq) {
+	free(pq->items);
+	free(pq);
+}
+
+static art_boolean
+art_pri_empty(ArtPriQ *pq) {
+	return pq->n_items == 0;
+}
+
+#ifdef ART_PRIQ_USE_HEAP
+
+/* This heap implementation is based on Vasek Chvatal's course notes:
+   http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
+
+static void
+art_pri_bubble_up(ArtPriQ *pq, int vacant, ArtPriPoint *missing) {
+	ArtPriPoint **items = pq->items;
+	int parent;
+
+	parent = (vacant - 1) >> 1;
+	while (vacant > 0 && (missing->y < items[parent]->y ||
+	                      (missing->y == items[parent]->y &&
+	                       missing->x < items[parent]->x))) {
+		items[vacant] = items[parent];
+		vacant = parent;
+		parent = (vacant - 1) >> 1;
+	}
+
+	items[vacant] = missing;
+}
+
+static void
+art_pri_insert(ArtPriQ *pq, ArtPriPoint *point) {
+	if (pq->n_items == pq->n_items_max)
+		art_expand(pq->items, ArtPriPoint *, pq->n_items_max);
+
+	art_pri_bubble_up(pq, pq->n_items++, point);
+}
+
+static void
+art_pri_sift_down_from_root(ArtPriQ *pq, ArtPriPoint *missing) {
+	ArtPriPoint **items = pq->items;
+	int vacant = 0, child = 2;
+	int n = pq->n_items;
+
+	while (child < n) {
+		if (items[child - 1]->y < items[child]->y ||
+		        (items[child - 1]->y == items[child]->y &&
+		         items[child - 1]->x < items[child]->x))
+			child--;
+		items[vacant] = items[child];
+		vacant = child;
+		child = (vacant + 1) << 1;
+	}
+	if (child == n) {
+		items[vacant] = items[n - 1];
+		vacant = n - 1;
+	}
+
+	art_pri_bubble_up(pq, vacant, missing);
+}
+
+static ArtPriPoint *
+art_pri_choose(ArtPriQ *pq) {
+	ArtPriPoint *result = pq->items[0];
+
+	art_pri_sift_down_from_root(pq, pq->items[--pq->n_items]);
+	return result;
+}
+
+#else
+
+/* Choose least point in queue */
+static ArtPriPoint *
+art_pri_choose(ArtPriQ *pq) {
+	int i;
+	int best = 0;
+	double best_x, best_y;
+	double y;
+	ArtPriPoint *result;
+
+	if (pq->n_items == 0)
+		return NULL;
+
+	best_x = pq->items[best]->x;
+	best_y = pq->items[best]->y;
+
+	for (i = 1; i < pq->n_items; i++) {
+		y = pq->items[i]->y;
+		if (y < best_y || (y == best_y && pq->items[i]->x < best_x)) {
+			best = i;
+			best_x = pq->items[best]->x;
+			best_y = y;
+		}
+	}
+	result = pq->items[best];
+	pq->items[best] = pq->items[--pq->n_items];
+	return result;
+}
+
+static void
+art_pri_insert(ArtPriQ *pq, ArtPriPoint *point) {
+	if (pq->n_items == pq->n_items_max)
+		art_expand(pq->items, ArtPriPoint *, pq->n_items_max);
+
+	pq->items[pq->n_items++] = point;
+}
+
+#endif
+
+/* A virtual class for an "svp writer". A client of this object creates an
+   SVP by repeatedly calling "add segment" and "add point" methods on it.
+*/
+
+typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
+
+/* An implementation of the svp writer virtual class that applies the
+   winding rule. */
+
+struct _ArtSvpWriterRewind {
+	ArtSvpWriter super;
+	ArtWindRule rule;
+	ArtSVP *svp;
+	int n_segs_max;
+	int *n_points_max;
+};
+
+static int
+art_svp_writer_rewind_add_segment(ArtSvpWriter *self, int wind_left,
+                                  int delta_wind, double x, double y) {
+	ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+	ArtSVP *svp;
+	ArtSVPSeg *seg;
+	art_boolean left_filled, right_filled;
+	int wind_right = wind_left + delta_wind;
+	int seg_num;
+	const int init_n_points_max = 4;
+
+	switch (swr->rule) {
+	case ART_WIND_RULE_NONZERO:
+		left_filled = (wind_left != 0);
+		right_filled = (wind_right != 0);
+		break;
+	case ART_WIND_RULE_INTERSECT:
+		left_filled = (wind_left > 1);
+		right_filled = (wind_right > 1);
+		break;
+	case ART_WIND_RULE_ODDEVEN:
+		left_filled = (wind_left & 1);
+		right_filled = (wind_right & 1);
+		break;
+	case ART_WIND_RULE_POSITIVE:
+		left_filled = (wind_left > 0);
+		right_filled = (wind_right > 0);
+		break;
+	default:
+		art_die("Unknown wind rule %d\n", swr->rule);
+	}
+	if (left_filled == right_filled) {
+		/* discard segment now */
+		return -1;
+	}
+
+	svp = swr->svp;
+	seg_num = svp->n_segs++;
+	if (swr->n_segs_max == seg_num) {
+		swr->n_segs_max <<= 1;
+		svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+		                            (swr->n_segs_max - 1) *
+		                            sizeof(ArtSVPSeg));
+		swr->svp = svp;
+		swr->n_points_max = art_renew(swr->n_points_max, int,
+		                              swr->n_segs_max);
+	}
+	seg = &svp->segs[seg_num];
+	seg->n_points = 1;
+	seg->dir = right_filled;
+	swr->n_points_max[seg_num] = init_n_points_max;
+	seg->bbox.x0 = x;
+	seg->bbox.y0 = y;
+	seg->bbox.x1 = x;
+	seg->bbox.y1 = y;
+	seg->points = art_new(ArtPoint, init_n_points_max);
+	seg->points[0].x = x;
+	seg->points[0].y = y;
+	return seg_num;
+}
+
+static void
+art_svp_writer_rewind_add_point(ArtSvpWriter *self, int seg_id,
+                                double x, double y) {
+	ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+	ArtSVPSeg *seg;
+	int n_points;
+
+	if (seg_id < 0)
+		/* omitted segment */
+		return;
+
+	seg = &swr->svp->segs[seg_id];
+	n_points = seg->n_points++;
+	if (swr->n_points_max[seg_id] == n_points)
+		art_expand(seg->points, ArtPoint, swr->n_points_max[seg_id]);
+	seg->points[n_points].x = x;
+	seg->points[n_points].y = y;
+	if (x < seg->bbox.x0)
+		seg->bbox.x0 = x;
+	if (x > seg->bbox.x1)
+		seg->bbox.x1 = x;
+	seg->bbox.y1 = y;
+}
+
+static void
+art_svp_writer_rewind_close_segment(ArtSvpWriter *self, int seg_id) {
+	/* Not needed for this simple implementation. A potential future
+	   optimization is to merge segments that can be merged safely. */
+}
+
+ArtSVP *
+art_svp_writer_rewind_reap(ArtSvpWriter *self) {
+	ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+	ArtSVP *result = swr->svp;
+
+	free(swr->n_points_max);
+	free(swr);
+	return result;
+}
+
+ArtSvpWriter *
+art_svp_writer_rewind_new(ArtWindRule rule) {
+	ArtSvpWriterRewind *result = art_new(ArtSvpWriterRewind, 1);
+
+	result->super.add_segment = art_svp_writer_rewind_add_segment;
+	result->super.add_point = art_svp_writer_rewind_add_point;
+	result->super.close_segment = art_svp_writer_rewind_close_segment;
+
+	result->rule = rule;
+	result->n_segs_max = 16;
+	result->svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
+	                                  (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
+	result->svp->n_segs = 0;
+	result->n_points_max = art_new(int, result->n_segs_max);
+
+	return &result->super;
+}
+
+/* Now, data structures for the active list */
+
+typedef struct _ArtActiveSeg ArtActiveSeg;
+
+/* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
+   x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
+#define ART_ACTIVE_FLAGS_BNEG 1
+
+/* This flag is set if the segment has been inserted into the active
+   list. */
+#define ART_ACTIVE_FLAGS_IN_ACTIVE 2
+
+/* This flag is set when the segment is to be deleted in the
+   horiz commit process. */
+#define ART_ACTIVE_FLAGS_DEL 4
+
+/* This flag is set if the seg_id is a valid output segment. */
+#define ART_ACTIVE_FLAGS_OUT 8
+
+/* This flag is set if the segment is in the horiz list. */
+#define ART_ACTIVE_FLAGS_IN_HORIZ 16
+
+struct _ArtActiveSeg {
+	int flags;
+	int wind_left, delta_wind;
+	ArtActiveSeg *left, *right; /* doubly linked list structure */
+
+	const ArtSVPSeg *in_seg;
+	int in_curs;
+
+	double x[2];
+	double y0, y1;
+	double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
+             and a>0 */
+
+	/* bottom point and intersection point stack */
+	int n_stack;
+	int n_stack_max;
+	ArtPoint *stack;
+
+	/* horiz commit list */
+	ArtActiveSeg *horiz_left, *horiz_right;
+	double horiz_x;
+	int horiz_delta_wind;
+	int seg_id;
+};
+
+typedef struct _ArtIntersectCtx ArtIntersectCtx;
+
+struct _ArtIntersectCtx {
+	const ArtSVP *in;
+	ArtSvpWriter *out;
+
+	ArtPriQ *pq;
+
+	ArtActiveSeg *active_head;
+
+	double y;
+	ArtActiveSeg *horiz_first;
+	ArtActiveSeg *horiz_last;
+
+	/* segment index of next input segment to be added to pri q */
+	int in_curs;
+};
+
+#define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
+
+/**
+ * art_svp_intersect_setup_seg: Set up an active segment from input segment.
+ * @seg: Active segment.
+ * @pri_pt: Priority queue point to initialize.
+ *
+ * Sets the x[], a, b, c, flags, and stack fields according to the
+ * line from the current cursor value. Sets the priority queue point
+ * to the bottom point of this line. Also advances the input segment
+ * cursor.
+ **/
+static void
+art_svp_intersect_setup_seg(ArtActiveSeg *seg, ArtPriPoint *pri_pt) {
+	const ArtSVPSeg *in_seg = seg->in_seg;
+	int in_curs = seg->in_curs++;
+	double x0, y0, x1, y1;
+	double dx, dy, s;
+	double a, b, r2;
+
+	x0 = in_seg->points[in_curs].x;
+	y0 = in_seg->points[in_curs].y;
+	x1 = in_seg->points[in_curs + 1].x;
+	y1 = in_seg->points[in_curs + 1].y;
+	pri_pt->x = x1;
+	pri_pt->y = y1;
+	dx = x1 - x0;
+	dy = y1 - y0;
+	r2 = dx * dx + dy * dy;
+	s = r2 == 0 ? 1 : 1 / sqrt(r2);
+	seg->a = a = dy * s;
+	seg->b = b = -dx * s;
+	seg->c = -(a * x0 + b * y0);
+	seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
+	seg->x[0] = x0;
+	seg->x[1] = x1;
+	seg->y0 = y0;
+	seg->y1 = y1;
+	seg->n_stack = 1;
+	seg->stack[0].x = x1;
+	seg->stack[0].y = y1;
+}
+
+/**
+ * art_svp_intersect_add_horiz: Add point to horizontal list.
+ * @ctx: Intersector context.
+ * @seg: Segment with point to insert into horizontal list.
+ *
+ * Inserts @seg into horizontal list, keeping it in ascending horiz_x
+ * order.
+ *
+ * Note: the horiz_commit routine processes "clusters" of segs in the
+ * horiz list, all sharing the same horiz_x value. The cluster is
+ * processed in active list order, rather than horiz list order. Thus,
+ * the order of segs in the horiz list sharing the same horiz_x
+ * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
+ * as a "belt and suspenders" defensive coding tactic.
+ **/
+static void
+art_svp_intersect_add_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+	ArtActiveSeg **pp = &ctx->horiz_last;
+	ArtActiveSeg *place;
+	ArtActiveSeg *place_right = NULL;
+
+
+#ifdef CHEAP_SANITYCHECK
+	if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ) {
+		art_warn("*** attempt to put segment in horiz list twice\n");
+		return;
+	}
+	seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
+#endif
+
+	for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
+	                                    (place->horiz_x == seg->horiz_x &&
+	                                     place->b < seg->b));
+	        place = *pp) {
+		place_right = place;
+		pp = &place->horiz_left;
+	}
+	*pp = seg;
+	seg->horiz_left = place;
+	seg->horiz_right = place_right;
+	if (place == NULL)
+		ctx->horiz_first = seg;
+	else
+		place->horiz_right = seg;
+}
+
+static void
+art_svp_intersect_push_pt(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+                          double x, double y) {
+	ArtPriPoint *pri_pt;
+	int n_stack = seg->n_stack;
+
+	if (n_stack == seg->n_stack_max)
+		art_expand(seg->stack, ArtPoint, seg->n_stack_max);
+	seg->stack[n_stack].x = x;
+	seg->stack[n_stack].y = y;
+	seg->n_stack++;
+
+	seg->x[1] = x;
+	seg->y1 = y;
+
+	pri_pt = art_new(ArtPriPoint, 1);
+	pri_pt->x = x;
+	pri_pt->y = y;
+	pri_pt->user_data = seg;
+	art_pri_insert(ctx->pq, pri_pt);
+}
+
+typedef enum {
+	ART_BREAK_LEFT = 1,
+	ART_BREAK_RIGHT = 2
+} ArtBreakFlags;
+
+/**
+ * art_svp_intersect_break: Break an active segment.
+ *
+ * Note: y must be greater than the top point's y, and less than
+ * the bottom's.
+ *
+ * Return value: x coordinate of break point.
+ */
+static double
+art_svp_intersect_break(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+                        double x_ref, double y, ArtBreakFlags break_flags) {
+	double x0, y0, x1, y1;
+	const ArtSVPSeg *in_seg = seg->in_seg;
+	int in_curs = seg->in_curs;
+	double x;
+
+	x0 = in_seg->points[in_curs - 1].x;
+	y0 = in_seg->points[in_curs - 1].y;
+	x1 = in_seg->points[in_curs].x;
+	y1 = in_seg->points[in_curs].y;
+	x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
+	if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
+	        (break_flags == ART_BREAK_RIGHT && x < x_ref)) {
+	}
+
+	/* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
+	   arithmetic, but it might be worthwhile to check just in case. */
+
+	if (y > ctx->y)
+		art_svp_intersect_push_pt(ctx, seg, x, y);
+	else {
+		seg->x[0] = x;
+		seg->y0 = y;
+		seg->horiz_x = x;
+		art_svp_intersect_add_horiz(ctx, seg);
+	}
+
+	return x;
+}
+
+/**
+ * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
+ * @ctx: Intersector context.
+ * @x: X coordinate of point to add.
+ * @y: Y coordinate of point to add.
+ * @seg: "nearby" segment, or NULL if leftmost.
+ *
+ * Return value: Segment immediately to the left of the new point, or
+ * NULL if the new point is leftmost.
+ **/
+static ArtActiveSeg *
+art_svp_intersect_add_point(ArtIntersectCtx *ctx, double x, double y,
+                            ArtActiveSeg *seg, ArtBreakFlags break_flags) {
+	ArtActiveSeg *left, *right;
+	double x_min = x, x_max = x;
+	art_boolean left_live, right_live;
+	double d;
+	double new_x;
+	ArtActiveSeg *test, *result = NULL;
+	double x_test;
+
+	left = seg;
+	if (left == NULL)
+		right = ctx->active_head;
+	else
+		right = left->right;
+	left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
+	right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
+	while (left_live || right_live) {
+		if (left_live) {
+			if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
+			        /* It may be that one of these conjuncts turns out to be always
+			              true. We test both anyway, to be defensive. */
+			        y != left->y0 && y < left->y1) {
+				d = x_min * left->a + y * left->b + left->c;
+				if (d < EPSILON_A) {
+					new_x = art_svp_intersect_break(ctx, left, x_min, y,
+					                                ART_BREAK_LEFT);
+					if (new_x > x_max) {
+						x_max = new_x;
+						right_live = (right != NULL);
+					} else if (new_x < x_min)
+						x_min = new_x;
+					left = left->left;
+					left_live = (left != NULL);
+				} else
+					left_live = ART_FALSE;
+			} else
+				left_live = ART_FALSE;
+		} else if (right_live) {
+			if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
+			        /* It may be that one of these conjuncts turns out to be always
+			              true. We test both anyway, to be defensive. */
+			        y != right->y0 && y < right->y1) {
+				d = x_max * right->a + y * right->b + right->c;
+				if (d > -EPSILON_A) {
+					new_x = art_svp_intersect_break(ctx, right, x_max, y,
+					                                ART_BREAK_RIGHT);
+					if (new_x < x_min) {
+						x_min = new_x;
+						left_live = (left != NULL);
+					} else if (new_x >= x_max)
+						x_max = new_x;
+					right = right->right;
+					right_live = (right != NULL);
+				} else
+					right_live = ART_FALSE;
+			} else
+				right_live = ART_FALSE;
+		}
+	}
+
+	/* Ascending order is guaranteed by break_flags. Thus, we don't need
+	   to actually fix up non-ascending pairs. */
+
+	/* Now, (left, right) defines an interval of segments broken. Sort
+	   into ascending x order. */
+	test = left == NULL ? ctx->active_head : left->right;
+	result = left;
+	if (test != NULL && test != right) {
+		if (y == test->y0)
+			x_test = test->x[0];
+		else /* assert y == test->y1, I think */
+			x_test = test->x[1];
+		for (;;) {
+			if (x_test <= x)
+				result = test;
+			test = test->right;
+			if (test == right)
+				break;
+			new_x = x_test;
+			if (new_x < x_test) {
+				art_warn("art_svp_intersect_add_point: non-ascending x\n");
+			}
+			x_test = new_x;
+		}
+	}
+	return result;
+}
+
+static void
+art_svp_intersect_swap_active(ArtIntersectCtx *ctx,
+                              ArtActiveSeg *left_seg, ArtActiveSeg *right_seg) {
+	right_seg->left = left_seg->left;
+	if (right_seg->left != NULL)
+		right_seg->left->right = right_seg;
+	else
+		ctx->active_head = right_seg;
+	left_seg->right = right_seg->right;
+	if (left_seg->right != NULL)
+		left_seg->right->left = left_seg;
+	left_seg->left = right_seg;
+	right_seg->right = left_seg;
+}
+
+/**
+ * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
+ * @ctx: Intersector context.
+ * @left_seg: Left segment of the pair.
+ * @right_seg: Right segment of the pair.
+ * @break_flags: Flags indicating whether to break neighbors.
+ *
+ * Tests crossing of @left_seg and @right_seg. If there is a crossing,
+ * inserts the intersection point into both segments.
+ *
+ * Return value: True if the intersection took place at the current
+ * scan line, indicating further iteration is needed.
+ **/
+static art_boolean
+art_svp_intersect_test_cross(ArtIntersectCtx *ctx,
+                             ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
+                             ArtBreakFlags break_flags) {
+	double left_x0, left_y0, left_x1;
+	double left_y1 = left_seg->y1;
+	double right_y1 = right_seg->y1;
+	double d;
+
+	const ArtSVPSeg *in_seg;
+	int in_curs;
+	double d0, d1, t;
+	double x, y; /* intersection point */
+
+	if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0]) {
+		/* Top points of left and right segments coincide. This case
+		     feels like a bit of duplication - we may want to merge it
+		     with the cases below. However, this way, we're sure that this
+		     logic makes only localized changes. */
+
+		if (left_y1 < right_y1) {
+			/* Test left (x1, y1) against right segment */
+			double left_x1 = left_seg->x[1];
+
+			if (left_x1 <
+			        right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+			        left_y1 == right_seg->y0)
+				return ART_FALSE;
+			d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+			if (d < -EPSILON_A)
+				return ART_FALSE;
+			else if (d < EPSILON_A) {
+				/* I'm unsure about the break flags here. */
+				double right_x1 = art_svp_intersect_break(ctx, right_seg,
+				                  left_x1, left_y1,
+				                  ART_BREAK_RIGHT);
+				if (left_x1 <= right_x1)
+					return ART_FALSE;
+			}
+		} else if (left_y1 > right_y1) {
+			/* Test right (x1, y1) against left segment */
+			double right_x1 = right_seg->x[1];
+
+			if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+			        right_y1 == left_seg->y0)
+				return ART_FALSE;
+			d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+			if (d > EPSILON_A)
+				return ART_FALSE;
+			else if (d > -EPSILON_A) {
+				/* See above regarding break flags. */
+				double left_x1 = art_svp_intersect_break(ctx, left_seg,
+				                 right_x1, right_y1,
+				                 ART_BREAK_LEFT);
+				if (left_x1 <= right_x1)
+					return ART_FALSE;
+			}
+		} else { /* left_y1 == right_y1 */
+			double left_x1 = left_seg->x[1];
+			double right_x1 = right_seg->x[1];
+
+			if (left_x1 <= right_x1)
+				return ART_FALSE;
+		}
+		art_svp_intersect_swap_active(ctx, left_seg, right_seg);
+		return ART_TRUE;
+	}
+
+	if (left_y1 < right_y1) {
+		/* Test left (x1, y1) against right segment */
+		double left_x1 = left_seg->x[1];
+
+		if (left_x1 <
+		        right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+		        left_y1 == right_seg->y0)
+			return ART_FALSE;
+		d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+		if (d < -EPSILON_A)
+			return ART_FALSE;
+		else if (d < EPSILON_A) {
+			double right_x1 = art_svp_intersect_break(ctx, right_seg,
+			                  left_x1, left_y1,
+			                  ART_BREAK_RIGHT);
+			if (left_x1 <= right_x1)
+				return ART_FALSE;
+		}
+	} else if (left_y1 > right_y1) {
+		/* Test right (x1, y1) against left segment */
+		double right_x1 = right_seg->x[1];
+
+		if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+		        right_y1 == left_seg->y0)
+			return ART_FALSE;
+		d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+		if (d > EPSILON_A)
+			return ART_FALSE;
+		else if (d > -EPSILON_A) {
+			double left_x1 = art_svp_intersect_break(ctx, left_seg,
+			                 right_x1, right_y1,
+			                 ART_BREAK_LEFT);
+			if (left_x1 <= right_x1)
+				return ART_FALSE;
+		}
+	} else { /* left_y1 == right_y1 */
+		double left_x1 = left_seg->x[1];
+		double right_x1 = right_seg->x[1];
+
+		if (left_x1 <= right_x1)
+			return ART_FALSE;
+	}
+
+	/* The segments cross. Find the intersection point. */
+
+	in_seg = left_seg->in_seg;
+	in_curs = left_seg->in_curs;
+	left_x0 = in_seg->points[in_curs - 1].x;
+	left_y0 = in_seg->points[in_curs - 1].y;
+	left_x1 = in_seg->points[in_curs].x;
+	left_y1 = in_seg->points[in_curs].y;
+	d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
+	d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+	if (d0 == d1) {
+		x = left_x0;
+		y = left_y0;
+	} else {
+		/* Is this division always safe? It could possibly overflow. */
+		t = d0 / (d0 - d1);
+		if (t <= 0) {
+			x = left_x0;
+			y = left_y0;
+		} else if (t >= 1) {
+			x = left_x1;
+			y = left_y1;
+		} else {
+			x = left_x0 + t * (left_x1 - left_x0);
+			y = left_y0 + t * (left_y1 - left_y0);
+		}
+	}
+
+	/* Make sure intersection point is within bounds of right seg. */
+	if (y < right_seg->y0) {
+		x = right_seg->x[0];
+		y = right_seg->y0;
+	} else if (y > right_seg->y1) {
+		x = right_seg->x[1];
+		y = right_seg->y1;
+	} else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
+		x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
+	else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
+		x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
+
+	if (y == left_seg->y0) {
+		if (y != right_seg->y0) {
+			art_svp_intersect_push_pt(ctx, right_seg, x, y);
+			if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+				art_svp_intersect_add_point(ctx, x, y, right_seg->right,
+				                            break_flags);
+		} else {
+			/* Intersection takes place at current scan line; process
+			   immediately rather than queueing intersection point into
+			   priq. */
+			ArtActiveSeg *winner, *loser;
+
+			/* Choose "most vertical" segement */
+			if (left_seg->a > right_seg->a) {
+				winner = left_seg;
+				loser = right_seg;
+			} else {
+				winner = right_seg;
+				loser = left_seg;
+			}
+
+			loser->x[0] = winner->x[0];
+			loser->horiz_x = loser->x[0];
+			loser->horiz_delta_wind += loser->delta_wind;
+			winner->horiz_delta_wind -= loser->delta_wind;
+
+			art_svp_intersect_swap_active(ctx, left_seg, right_seg);
+			return ART_TRUE;
+		}
+	} else if (y == right_seg->y0) {
+		art_svp_intersect_push_pt(ctx, left_seg, x, y);
+		if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+			art_svp_intersect_add_point(ctx, x, y, left_seg->left,
+			                            break_flags);
+	} else {
+		/* Insert the intersection point into both segments. */
+		art_svp_intersect_push_pt(ctx, left_seg, x, y);
+		art_svp_intersect_push_pt(ctx, right_seg, x, y);
+		if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+			art_svp_intersect_add_point(ctx, x, y, left_seg->left, break_flags);
+		if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+			art_svp_intersect_add_point(ctx, x, y, right_seg->right, break_flags);
+	}
+	return ART_FALSE;
+}
+
+/**
+ * art_svp_intersect_active_delete: Delete segment from active list.
+ * @ctx: Intersection context.
+ * @seg: Segment to delete.
+ *
+ * Deletes @seg from the active list.
+ **/
+static /* todo inline */ void
+art_svp_intersect_active_delete(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+	ArtActiveSeg *left = seg->left, *right = seg->right;
+
+	if (left != NULL)
+		left->right = right;
+	else
+		ctx->active_head = right;
+	if (right != NULL)
+		right->left = left;
+}
+
+/**
+ * art_svp_intersect_active_free: Free an active segment.
+ * @seg: Segment to delete.
+ *
+ * Frees @seg.
+ **/
+static /* todo inline */ void
+art_svp_intersect_active_free(ArtActiveSeg *seg) {
+	free(seg->stack);
+	free(seg);
+}
+
+/**
+ * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
+ *
+ * Tests @seg against its left and right neighbors for intersections.
+ * Precondition: the line in @seg is not purely horizontal.
+ **/
+static void
+art_svp_intersect_insert_cross(ArtIntersectCtx *ctx,
+                               ArtActiveSeg *seg) {
+	ArtActiveSeg *left = seg, *right = seg;
+
+	for (;;) {
+		if (left != NULL) {
+			ArtActiveSeg *leftc;
+
+			for (leftc = left->left; leftc != NULL; leftc = leftc->left)
+				if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
+					break;
+			if (leftc != NULL &&
+			        art_svp_intersect_test_cross(ctx, leftc, left,
+			                                     ART_BREAK_LEFT)) {
+				if (left == right || right == NULL)
+					right = left->right;
+			} else {
+				left = NULL;
+			}
+		} else if (right != NULL && right->right != NULL) {
+			ArtActiveSeg *rightc;
+
+			for (rightc = right->right; rightc != NULL; rightc = rightc->right)
+				if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
+					break;
+			if (rightc != NULL &&
+			        art_svp_intersect_test_cross(ctx, right, rightc,
+			                                     ART_BREAK_RIGHT)) {
+				if (left == right || left == NULL)
+					left = right->left;
+			} else {
+				right = NULL;
+			}
+		} else
+			break;
+	}
+}
+
+/**
+ * art_svp_intersect_horiz: Add horizontal line segment.
+ * @ctx: Intersector context.
+ * @seg: Segment on which to add horizontal line.
+ * @x0: Old x position.
+ * @x1: New x position.
+ *
+ * Adds a horizontal line from @x0 to @x1, and updates the current
+ * location of @seg to @x1.
+ **/
+static void
+art_svp_intersect_horiz(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+                        double x0, double x1) {
+	ArtActiveSeg *hs;
+
+	if (x0 == x1)
+		return;
+
+	hs = art_new(ArtActiveSeg, 1);
+
+	hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
+	if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
+		ArtSvpWriter *swr = ctx->out;
+
+		swr->add_point(swr, seg->seg_id, x0, ctx->y);
+	}
+	hs->seg_id = seg->seg_id;
+	hs->horiz_x = x0;
+	hs->horiz_delta_wind = seg->delta_wind;
+	hs->stack = NULL;
+
+	/* Ideally, the (a, b, c) values will never be read. However, there
+	   are probably some tests remaining that don't check for _DEL
+	   before evaluating the line equation. For those, these
+	   initializations will at least prevent a UMR of the values, which
+	   can crash on some platforms. */
+	hs->a = 0.0;
+	hs->b = 0.0;
+	hs->c = 0.0;
+
+	seg->horiz_delta_wind -= seg->delta_wind;
+
+	art_svp_intersect_add_horiz(ctx, hs);
+
+	if (x0 > x1) {
+		ArtActiveSeg *left;
+		art_boolean first = ART_TRUE;
+
+		for (left = seg->left; left != NULL; left = seg->left) {
+			int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
+
+			if (left->x[left_bneg] <= x1)
+				break;
+			if (left->x[left_bneg ^ 1] <= x1 &&
+			        x1 *left->a + ctx->y *left->b + left->c >= 0)
+				break;
+			if (left->y0 != ctx->y && left->y1 != ctx->y) {
+				art_svp_intersect_break(ctx, left, x1, ctx->y, ART_BREAK_LEFT);
+			}
+			art_svp_intersect_swap_active(ctx, left, seg);
+			if (first && left->right != NULL) {
+				art_svp_intersect_test_cross(ctx, left, left->right,
+				                             ART_BREAK_RIGHT);
+				first = ART_FALSE;
+			}
+		}
+	} else {
+		ArtActiveSeg *right;
+		art_boolean first = ART_TRUE;
+
+		for (right = seg->right; right != NULL; right = seg->right) {
+			int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
+
+			if (right->x[right_bneg ^ 1] >= x1)
+				break;
+			if (right->x[right_bneg] >= x1 &&
+			        x1 *right->a + ctx->y *right->b + right->c <= 0)
+				break;
+			if (right->y0 != ctx->y && right->y1 != ctx->y) {
+				art_svp_intersect_break(ctx, right, x1, ctx->y,
+				                        ART_BREAK_LEFT);
+			}
+			art_svp_intersect_swap_active(ctx, seg, right);
+			if (first && right->left != NULL) {
+				art_svp_intersect_test_cross(ctx, right->left, right,
+				                             ART_BREAK_RIGHT);
+				first = ART_FALSE;
+			}
+		}
+	}
+
+	seg->x[0] = x1;
+	seg->x[1] = x1;
+	seg->horiz_x = x1;
+	seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+}
+
+/**
+ * art_svp_intersect_insert_line: Insert a line into the active list.
+ * @ctx: Intersector context.
+ * @seg: Segment containing line to insert.
+ *
+ * Inserts the line into the intersector context, taking care of any
+ * intersections, and adding the appropriate horizontal points to the
+ * active list.
+ **/
+static void
+art_svp_intersect_insert_line(ArtIntersectCtx *ctx, ArtActiveSeg *seg) {
+	if (seg->y1 == seg->y0) {
+		art_svp_intersect_horiz(ctx, seg, seg->x[0], seg->x[1]);
+	} else {
+		art_svp_intersect_insert_cross(ctx, seg);
+		art_svp_intersect_add_horiz(ctx, seg);
+	}
+}
+
+static void
+art_svp_intersect_process_intersection(ArtIntersectCtx *ctx,
+                                       ArtActiveSeg *seg) {
+	int n_stack = --seg->n_stack;
+	seg->x[1] = seg->stack[n_stack - 1].x;
+	seg->y1 = seg->stack[n_stack - 1].y;
+	seg->x[0] = seg->stack[n_stack].x;
+	seg->y0 = seg->stack[n_stack].y;
+	seg->horiz_x = seg->x[0];
+	art_svp_intersect_insert_line(ctx, seg);
+}
+
+static void
+art_svp_intersect_advance_cursor(ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+                                 ArtPriPoint *pri_pt) {
+	const ArtSVPSeg *in_seg = seg->in_seg;
+	int in_curs = seg->in_curs;
+	ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
+
+	if (swr != NULL)
+		swr->add_point(swr, seg->seg_id, seg->x[1], seg->y1);
+	if (in_curs + 1 == in_seg->n_points) {
+		ArtActiveSeg *left = seg->left, *right = seg->right;
+
+#if 0
+		if (swr != NULL)
+			swr->close_segment(swr, seg->seg_id);
+		seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+#endif
+		seg->flags |= ART_ACTIVE_FLAGS_DEL;
+		art_svp_intersect_add_horiz(ctx, seg);
+		art_svp_intersect_active_delete(ctx, seg);
+		if (left != NULL && right != NULL)
+			art_svp_intersect_test_cross(ctx, left, right,
+			                             (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
+		free(pri_pt);
+	} else {
+		seg->horiz_x = seg->x[1];
+
+		art_svp_intersect_setup_seg(seg, pri_pt);
+		art_pri_insert(ctx->pq, pri_pt);
+		art_svp_intersect_insert_line(ctx, seg);
+	}
+}
+
+static void
+art_svp_intersect_add_seg(ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg) {
+	ArtActiveSeg *seg = art_new(ArtActiveSeg, 1);
+	ArtActiveSeg *test;
+	double x0, y0;
+	ArtActiveSeg *beg_range;
+	ArtActiveSeg *last = NULL;
+	ArtActiveSeg *left, *right;
+	ArtPriPoint *pri_pt = art_new(ArtPriPoint, 1);
+
+	seg->flags = 0;
+	seg->in_seg = in_seg;
+	seg->in_curs = 0;
+
+	seg->n_stack_max = 4;
+	seg->stack = art_new(ArtPoint, seg->n_stack_max);
+
+	seg->horiz_delta_wind = 0;
+
+	seg->wind_left = 0;
+
+	pri_pt->user_data = seg;
+	art_svp_intersect_setup_seg(seg, pri_pt);
+	art_pri_insert(ctx->pq, pri_pt);
+
+	/* Find insertion place for new segment */
+	/* This is currently a left-to-right scan, but should be replaced
+	   with a binary search as soon as it's validated. */
+
+	x0 = in_seg->points[0].x;
+	y0 = in_seg->points[0].y;
+	beg_range = NULL;
+	for (test = ctx->active_head; test != NULL; test = test->right) {
+		double d;
+		int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
+
+		if (x0 < test->x[test_bneg]) {
+			if (x0 < test->x[test_bneg ^ 1])
+				break;
+			d = x0 * test->a + y0 * test->b + test->c;
+			if (d < 0)
+				break;
+		}
+		last = test;
+	}
+
+	left = art_svp_intersect_add_point(ctx, x0, y0, last, (ArtBreakFlags)(ART_BREAK_LEFT | ART_BREAK_RIGHT));
+	seg->left = left;
+	if (left == NULL) {
+		right = ctx->active_head;
+		ctx->active_head = seg;
+	} else {
+		right = left->right;
+		left->right = seg;
+	}
+	seg->right = right;
+	if (right != NULL)
+		right->left = seg;
+
+	seg->delta_wind = in_seg->dir ? 1 : -1;
+	seg->horiz_x = x0;
+
+	art_svp_intersect_insert_line(ctx, seg);
+}
+
+/**
+ * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
+ * @ctx: Intersection context.
+ *
+ * The main function of the horizontal commit is to output new
+ * points to the output writer.
+ *
+ * This "commit" pass is also where winding numbers are assigned,
+ * because doing it here provides much greater tolerance for inputs
+ * which are not in strict SVP order.
+ *
+ * Each cluster in the horiz_list contains both segments that are in
+ * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
+ * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
+ * need to deal with both.
+ **/
+static void
+art_svp_intersect_horiz_commit(ArtIntersectCtx *ctx) {
+	ArtActiveSeg *seg;
+	int winding_number = 0; /* initialization just to avoid warning */
+	int horiz_wind = 0;
+	double last_x = 0; /* initialization just to avoid warning */
+
+	/* Output points to svp writer. */
+	for (seg = ctx->horiz_first; seg != NULL;) {
+		/* Find a cluster with common horiz_x, */
+		ArtActiveSeg *curs;
+		double x = seg->horiz_x;
+
+		/* Generate any horizontal segments. */
+		if (horiz_wind != 0) {
+			ArtSvpWriter *swr = ctx->out;
+			int seg_id;
+
+			seg_id = swr->add_segment(swr, winding_number, horiz_wind,
+			                          last_x, ctx->y);
+			swr->add_point(swr, seg_id, x, ctx->y);
+			swr->close_segment(swr, seg_id);
+		}
+
+		/* Find first active segment in cluster. */
+
+		for (curs = seg; curs != NULL && curs->horiz_x == x;
+		        curs = curs->horiz_right)
+			if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
+				break;
+
+		if (curs != NULL && curs->horiz_x == x) {
+			/* There exists at least one active segment in this cluster. */
+
+			/* Find beginning of cluster. */
+			for (; curs->left != NULL; curs = curs->left)
+				if (curs->left->horiz_x != x)
+					break;
+
+			if (curs->left != NULL)
+				winding_number = curs->left->wind_left + curs->left->delta_wind;
+			else
+				winding_number = 0;
+
+			do {
+				if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
+				        curs->wind_left != winding_number) {
+					ArtSvpWriter *swr = ctx->out;
+
+					if (curs->flags & ART_ACTIVE_FLAGS_OUT) {
+						swr->add_point(swr, curs->seg_id,
+						               curs->horiz_x, ctx->y);
+						swr->close_segment(swr, curs->seg_id);
+					}
+
+					curs->seg_id = swr->add_segment(swr, winding_number,
+					                                curs->delta_wind,
+					                                x, ctx->y);
+					curs->flags |= ART_ACTIVE_FLAGS_OUT;
+				}
+				curs->wind_left = winding_number;
+				winding_number += curs->delta_wind;
+				curs = curs->right;
+			} while (curs != NULL && curs->horiz_x == x);
+		}
+
+		/* Skip past cluster. */
+		do {
+			ArtActiveSeg *next = seg->horiz_right;
+
+			seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
+			horiz_wind += seg->horiz_delta_wind;
+			seg->horiz_delta_wind = 0;
+			if (seg->flags & ART_ACTIVE_FLAGS_DEL) {
+				if (seg->flags & ART_ACTIVE_FLAGS_OUT) {
+					ArtSvpWriter *swr = ctx->out;
+					swr->close_segment(swr, seg->seg_id);
+				}
+				art_svp_intersect_active_free(seg);
+			}
+			seg = next;
+		} while (seg != NULL && seg->horiz_x == x);
+
+		last_x = x;
+	}
+	ctx->horiz_first = NULL;
+	ctx->horiz_last = NULL;
+}
+
+void
+art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out) {
+	ArtIntersectCtx *ctx;
+	ArtPriQ *pq;
+	ArtPriPoint *first_point;
+
+	if (in->n_segs == 0)
+		return;
+
+	ctx = art_new(ArtIntersectCtx, 1);
+	ctx->in = in;
+	ctx->out = out;
+	pq = art_pri_new();
+	ctx->pq = pq;
+
+	ctx->active_head = NULL;
+
+	ctx->horiz_first = NULL;
+	ctx->horiz_last = NULL;
+
+	ctx->in_curs = 0;
+	first_point = art_new(ArtPriPoint, 1);
+	first_point->x = in->segs[0].points[0].x;
+	first_point->y = in->segs[0].points[0].y;
+	first_point->user_data = NULL;
+	ctx->y = first_point->y;
+	art_pri_insert(pq, first_point);
+
+	while (!art_pri_empty(pq)) {
+		ArtPriPoint *pri_point = art_pri_choose(pq);
+		ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
+
+		if (ctx->y != pri_point->y) {
+			art_svp_intersect_horiz_commit(ctx);
+			ctx->y = pri_point->y;
+		}
+
+		if (seg == NULL) {
+			/* Insert new segment from input */
+			const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
+			art_svp_intersect_add_seg(ctx, in_seg);
+			if (ctx->in_curs < in->n_segs) {
+				const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
+				pri_point->x = next_seg->points[0].x;
+				pri_point->y = next_seg->points[0].y;
+				/* user_data is already NULL */
+				art_pri_insert(pq, pri_point);
+			} else
+				free(pri_point);
+		} else {
+			int n_stack = seg->n_stack;
+
+			if (n_stack > 1) {
+				art_svp_intersect_process_intersection(ctx, seg);
+				free(pri_point);
+			} else {
+				art_svp_intersect_advance_cursor(ctx, seg, pri_point);
+			}
+		}
+	}
+
+	art_svp_intersect_horiz_commit(ctx);
+
+	art_pri_free(pq);
+	free(ctx);
+}


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.h
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.h	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.h	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,58 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2001 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_INTERSECT_H__
+#define __ART_SVP_INTERSECT_H__
+
+/* The funky new SVP intersector. */
+
+#include "art.h"
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+	ART_WIND_RULE_NONZERO,
+	ART_WIND_RULE_INTERSECT,
+	ART_WIND_RULE_ODDEVEN,
+	ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+typedef struct _ArtSvpWriter ArtSvpWriter;
+
+struct _ArtSvpWriter {
+	int (*add_segment)(ArtSvpWriter *self, int wind_left, int delta_wind,
+	                   double x, double y);
+	void (*add_point)(ArtSvpWriter *self, int seg_id, double x, double y);
+	void (*close_segment)(ArtSvpWriter *self, int seg_id);
+};
+
+ArtSvpWriter *
+art_svp_writer_rewind_new(ArtWindRule rule);
+
+ArtSVP *
+art_svp_writer_rewind_reap(ArtSvpWriter *self);
+
+int
+art_svp_seg_compare(const void *s1, const void *s2);
+
+void
+art_svp_intersector(const ArtSVP *in, ArtSvpWriter *out);
+
+#endif /* __ART_SVP_INTERSECT_H__ */


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_intersect.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.cpp
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.cpp	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,428 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+#include "art.h"
+#include "art_svp_render_aa.h"
+
+#include <math.h>
+#include <string.h> /* for memmove */
+
+#include <stdio.h>
+
+typedef double artfloat;
+
+struct _ArtSVPRenderAAIter {
+	const ArtSVP *svp;
+	int x0, x1;
+	int y;
+	int seg_ix;
+
+	int *active_segs;
+	int n_active_segs;
+	int *cursor;
+	artfloat *seg_x;
+	artfloat *seg_dx;
+
+	ArtSVPRenderAAStep *steps;
+};
+
+static void
+art_svp_render_insert_active(int i, int *active_segs, int n_active_segs,
+                             artfloat *seg_x, artfloat *seg_dx) {
+	int j;
+	artfloat x;
+	int tmp1, tmp2;
+
+	/* this is a cheap hack to get ^'s sorted correctly */
+	x = seg_x[i] + 0.001 * seg_dx[i];
+	for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++);
+
+	tmp1 = i;
+	while (j < n_active_segs) {
+		tmp2 = active_segs[j];
+		active_segs[j] = tmp1;
+		tmp1 = tmp2;
+		j++;
+	}
+	active_segs[j] = tmp1;
+}
+
+static void
+art_svp_render_delete_active(int *active_segs, int j, int n_active_segs) {
+	int k;
+
+	for (k = j; k < n_active_segs; k++)
+		active_segs[k] = active_segs[k + 1];
+}
+
+#define EPSILON 1e-6
+
+/* Render the sorted vector path in the given rectangle, antialiased.
+
+   This interface uses a callback for the actual pixel rendering. The
+   callback is called y1 - y0 times (once for each scan line). The y
+   coordinate is given as an argument for convenience (it could be
+   stored in the callback's private data and incremented on each
+   call).
+
+   The rendered polygon is represented in a semi-runlength format: a
+   start value and a sequence of "steps". Each step has an x
+   coordinate and a value delta. The resulting value at position x is
+   equal to the sum of the start value and all step delta values for
+   which the step x coordinate is less than or equal to x. An
+   efficient algorithm will traverse the steps left to right, keeping
+   a running sum.
+
+   All x coordinates in the steps are guaranteed to be x0 <= x < x1.
+   (This guarantee is a change from the gfonted vpaar renderer, and is
+   designed to simplify the callback).
+
+   There is now a further guarantee that no two steps will have the
+   same x value. This may allow for further speedup and simplification
+   of renderers.
+
+   The value 0x8000 represents 0% coverage by the polygon, while
+   0xff8000 represents 100% coverage. This format is designed so that
+   >> 16 results in a standard 0x00..0xff value range, with nice
+   rounding.
+
+   Status of this routine:
+
+   Basic correctness: OK
+
+   Numerical stability: pretty good, although probably not
+   bulletproof.
+
+   Speed: Needs more aggressive culling of bounding boxes.  Can
+   probably speed up the [x0,x1) clipping of step values.  Can do more
+   of the step calculation in fixed point.
+
+   Precision: No known problems, although it should be tested
+   thoroughly, especially for symmetry.
+
+*/
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter(const ArtSVP *svp,
+                       int x0, int y0, int x1, int y1) {
+	ArtSVPRenderAAIter *iter = art_new(ArtSVPRenderAAIter, 1);
+
+	iter->svp = svp;
+	iter->y = y0;
+	iter->x0 = x0;
+	iter->x1 = x1;
+	iter->seg_ix = 0;
+
+	iter->active_segs = art_new(int, svp->n_segs);
+	iter->cursor = art_new(int, svp->n_segs);
+	iter->seg_x = art_new(artfloat, svp->n_segs);
+	iter->seg_dx = art_new(artfloat, svp->n_segs);
+	iter->steps = art_new(ArtSVPRenderAAStep, x1 - x0);
+	iter->n_active_segs = 0;
+
+	return iter;
+}
+
+#define ADD_STEP(xpos, xdelta)                          \
+	/* stereotype code fragment for adding a step */      \
+	if (n_steps == 0 || steps[n_steps - 1].x < xpos)      \
+	{                                                   \
+		sx = n_steps;                                     \
+		steps[sx].x = xpos;                               \
+		steps[sx].delta = xdelta;                         \
+		n_steps++;                                        \
+	}                                                   \
+	else                                                  \
+	{                                                   \
+		for (sx = n_steps; sx > 0; sx--)                  \
+		{                                               \
+			if (steps[sx - 1].x == xpos)                  \
+			{                                           \
+				steps[sx - 1].delta += xdelta;            \
+				sx = n_steps;                             \
+				break;                                    \
+			}                                           \
+			else if (steps[sx - 1].x < xpos)              \
+			{                                           \
+				break;                                    \
+			}                                           \
+		}                                               \
+		if (sx < n_steps)                                 \
+		{                                               \
+			memmove (&steps[sx + 1], &steps[sx],          \
+			         (n_steps - sx) * sizeof(steps[0]));  \
+			steps[sx].x = xpos;                           \
+			steps[sx].delta = xdelta;                     \
+			n_steps++;                                    \
+		}                                               \
+	}
+
+void
+art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
+                            ArtSVPRenderAAStep **p_steps, int *p_n_steps) {
+	const ArtSVP *svp = iter->svp;
+	int *active_segs = iter->active_segs;
+	int n_active_segs = iter->n_active_segs;
+	int *cursor = iter->cursor;
+	artfloat *seg_x = iter->seg_x;
+	artfloat *seg_dx = iter->seg_dx;
+	int i = iter->seg_ix;
+	int j;
+	int x0 = iter->x0;
+	int x1 = iter->x1;
+	int y = iter->y;
+	int seg_index;
+
+	int x;
+	ArtSVPRenderAAStep *steps = iter->steps;
+	int n_steps;
+	artfloat y_top, y_bot;
+	artfloat x_top, x_bot;
+	artfloat x_min, x_max;
+	int ix_min, ix_max;
+	artfloat delta; /* delta should be int too? */
+	int last, this_;
+	int xdelta;
+	artfloat rslope, drslope;
+	int start;
+	const ArtSVPSeg *seg;
+	int curs;
+	artfloat dy;
+
+	int sx;
+
+	/* insert new active segments */
+	for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++) {
+		if (svp->segs[i].bbox.y1 > y &&
+		        svp->segs[i].bbox.x0 < x1) {
+			seg = &svp->segs[i];
+			/* move cursor to topmost vector which overlaps [y,y+1) */
+			for (curs = 0; seg->points[curs + 1].y < y; curs++);
+			cursor[i] = curs;
+			dy = seg->points[curs + 1].y - seg->points[curs].y;
+			if (fabs(dy) >= EPSILON)
+				seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
+				            dy;
+			else
+				seg_dx[i] = 1e12;
+			seg_x[i] = seg->points[curs].x +
+			           (y - seg->points[curs].y) * seg_dx[i];
+			art_svp_render_insert_active(i, active_segs, n_active_segs++,
+			                             seg_x, seg_dx);
+		}
+	}
+
+	n_steps = 0;
+
+	/* render the runlengths, advancing and deleting as we go */
+	start = 0x8000;
+
+	for (j = 0; j < n_active_segs; j++) {
+		seg_index = active_segs[j];
+		seg = &svp->segs[seg_index];
+		curs = cursor[seg_index];
+		while (curs != seg->n_points - 1 &&
+		        seg->points[curs].y < y + 1) {
+			y_top = y;
+			if (y_top < seg->points[curs].y)
+				y_top = seg->points[curs].y;
+			y_bot = y + 1;
+			if (y_bot > seg->points[curs + 1].y)
+				y_bot = seg->points[curs + 1].y;
+			if (y_top != y_bot) {
+				delta = (seg->dir ? 16711680.0 : -16711680.0) *
+				        (y_bot - y_top);
+				x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
+				x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
+				if (x_top < x_bot) {
+					x_min = x_top;
+					x_max = x_bot;
+				} else {
+					x_min = x_bot;
+					x_max = x_top;
+				}
+				ix_min = floor(x_min);
+				ix_max = floor(x_max);
+				if (ix_min >= x1) {
+					/* skip; it starts to the right of the render region */
+				} else if (ix_max < x0)
+					/* it ends to the left of the render region */
+					start += delta;
+				else if (ix_min == ix_max) {
+					/* case 1, antialias a single pixel */
+					xdelta = (ix_min + 1 - (x_min + x_max) * 0.5) * delta;
+
+					ADD_STEP(ix_min, xdelta)
+
+					if (ix_min + 1 < x1) {
+						xdelta = delta - xdelta;
+
+						ADD_STEP(ix_min + 1, xdelta)
+					}
+				} else {
+					/* case 2, antialias a run */
+					rslope = 1.0 / fabs(seg_dx[seg_index]);
+					drslope = delta * rslope;
+					last =
+					    drslope * 0.5 *
+					    (ix_min + 1 - x_min) * (ix_min + 1 - x_min);
+					xdelta = last;
+					if (ix_min >= x0) {
+						ADD_STEP(ix_min, xdelta)
+
+						x = ix_min + 1;
+					} else {
+						start += last;
+						x = x0;
+					}
+					if (ix_max > x1)
+						ix_max = x1;
+					for (; x < ix_max; x++) {
+						this_ = (seg->dir ? 16711680.0 : -16711680.0) * rslope *
+						        (x + 0.5 - x_min);
+						xdelta = this_ - last;
+						last = this_;
+
+						ADD_STEP(x, xdelta)
+					}
+					if (x < x1) {
+						this_ =
+						    delta * (1 - 0.5 *
+						             (x_max - ix_max) * (x_max - ix_max) *
+						             rslope);
+						xdelta = this_ - last;
+						last = this_;
+
+						ADD_STEP(x, xdelta)
+
+						if (x + 1 < x1) {
+							xdelta = delta - last;
+
+							ADD_STEP(x + 1, xdelta)
+						}
+					}
+				}
+			}
+			curs++;
+			if (curs != seg->n_points - 1 &&
+			        seg->points[curs].y < y + 1) {
+				dy = seg->points[curs + 1].y - seg->points[curs].y;
+				if (fabs(dy) >= EPSILON)
+					seg_dx[seg_index] = (seg->points[curs + 1].x -
+					                     seg->points[curs].x) / dy;
+				else
+					seg_dx[seg_index] = 1e12;
+				seg_x[seg_index] = seg->points[curs].x +
+				                   (y - seg->points[curs].y) * seg_dx[seg_index];
+			}
+			/* break here, instead of duplicating predicate in while? */
+		}
+		if (seg->points[curs].y >= y + 1) {
+			curs--;
+			cursor[seg_index] = curs;
+			seg_x[seg_index] += seg_dx[seg_index];
+		} else {
+			art_svp_render_delete_active(active_segs, j--,
+			                             --n_active_segs);
+		}
+	}
+
+	*p_start = start;
+	*p_steps = steps;
+	*p_n_steps = n_steps;
+
+	iter->seg_ix = i;
+	iter->n_active_segs = n_active_segs;
+	iter->y++;
+}
+
+void
+art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter) {
+	free(iter->steps);
+
+	free(iter->seg_dx);
+	free(iter->seg_x);
+	free(iter->cursor);
+	free(iter->active_segs);
+	free(iter);
+}
+
+/**
+ * art_svp_render_aa: Render SVP antialiased.
+ * @svp: The #ArtSVP to render.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @callback: The callback which actually paints the pixels.
+ * @callback_data: Private data for @callback.
+ *
+ * Renders the sorted vector path in the given rectangle, antialiased.
+ *
+ * This interface uses a callback for the actual pixel rendering. The
+ * callback is called @y1 - @y0 times (once for each scan line). The y
+ * coordinate is given as an argument for convenience (it could be
+ * stored in the callback's private data and incremented on each
+ * call).
+ *
+ * The rendered polygon is represented in a semi-runlength format: a
+ * start value and a sequence of "steps". Each step has an x
+ * coordinate and a value delta. The resulting value at position x is
+ * equal to the sum of the start value and all step delta values for
+ * which the step x coordinate is less than or equal to x. An
+ * efficient algorithm will traverse the steps left to right, keeping
+ * a running sum.
+ *
+ * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1.
+ * (This guarantee is a change from the gfonted vpaar renderer from
+ * which this routine is derived, and is designed to simplify the
+ * callback).
+ *
+ * The value 0x8000 represents 0% coverage by the polygon, while
+ * 0xff8000 represents 100% coverage. This format is designed so that
+ * >> 16 results in a standard 0x00..0xff value range, with nice
+ * rounding.
+ *
+ **/
+void
+art_svp_render_aa(const ArtSVP *svp,
+                  int x0, int y0, int x1, int y1,
+                  void (*callback)(void *callback_data,
+                                   int y,
+                                   int start,
+                                   ArtSVPRenderAAStep *steps, int n_steps),
+                  void *callback_data) {
+	ArtSVPRenderAAIter *iter;
+	int y;
+	int start;
+	ArtSVPRenderAAStep *steps;
+	int n_steps;
+
+	iter = art_svp_render_aa_iter(svp, x0, y0, x1, y1);
+
+
+	for (y = y0; y < y1; y++) {
+		art_svp_render_aa_iter_step(iter, &start, &steps, &n_steps);
+		(*callback)(callback_data, y, start, steps, n_steps);
+	}
+
+	art_svp_render_aa_iter_done(iter);
+}


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.h
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.h	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.h	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,55 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_RENDER_AA_H__
+#define __ART_SVP_RENDER_AA_H__
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+#include "art.h"
+
+typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep;
+typedef struct _ArtSVPRenderAAIter ArtSVPRenderAAIter;
+
+struct _ArtSVPRenderAAStep {
+	int x;
+	int delta; /* stored with 16 fractional bits */
+};
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter(const ArtSVP *svp,
+                       int x0, int y0, int x1, int y1);
+
+void
+art_svp_render_aa_iter_step(ArtSVPRenderAAIter *iter, int *p_start,
+                            ArtSVPRenderAAStep **p_steps, int *p_n_steps);
+
+void
+art_svp_render_aa_iter_done(ArtSVPRenderAAIter *iter);
+
+void
+art_svp_render_aa(const ArtSVP *svp,
+                  int x0, int y0, int x1, int y1,
+                  void (*callback)(void *callback_data,
+                                   int y,
+                                   int start,
+                                   ArtSVPRenderAAStep *steps, int n_steps),
+                  void *callback_data);
+
+#endif /* __ART_SVP_RENDER_AA_H__ */


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_render_aa.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.cpp
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.cpp	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,192 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Sort vector paths into sorted vector paths */
+
+#include "art.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+/* reverse a list of points in place */
+static void
+reverse_points(ArtPoint *points, int n_points) {
+	int i;
+	ArtPoint tmp_p;
+
+	for (i = 0; i < (n_points >> 1); i++) {
+		tmp_p = points[i];
+		points[i] = points[n_points - (i + 1)];
+		points[n_points - (i + 1)] = tmp_p;
+	}
+}
+
+/**
+ * art_svp_from_vpath: Convert a vpath to a sorted vector path.
+ * @vpath: #ArtVPath to convert.
+ *
+ * Converts a vector path into sorted vector path form. The svp form is
+ * more efficient for rendering and other vector operations.
+ *
+ * Basically, the implementation is to traverse the vector path,
+ * generating a new segment for each "run" of points in the vector
+ * path with monotonically increasing Y values. All the resulting
+ * values are then sorted.
+ *
+ * Note: I'm not sure that the sorting rule is correct with respect
+ * to numerical stability issues.
+ *
+ * Return value: Resulting sorted vector path.
+ **/
+ArtSVP *
+art_svp_from_vpath(ArtVpath *vpath) {
+	int n_segs, n_segs_max;
+	ArtSVP *svp;
+	int dir;
+	int new_dir;
+	int i;
+	ArtPoint *points;
+	int n_points, n_points_max;
+	double x, y;
+	double x_min, x_max;
+
+	n_segs = 0;
+	n_segs_max = 16;
+	svp = (ArtSVP *)malloc(sizeof(ArtSVP) +
+	                          (n_segs_max - 1) * sizeof(ArtSVPSeg));
+
+	dir = 0;
+	n_points = 0;
+	n_points_max = 0;
+	points = NULL;
+	i = 0;
+
+	x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
+        but it makes gcc -Wall -ansi -pedantic happier */
+	x_min = x_max = 0; /* same */
+
+	while (vpath[i].code != ART_END) {
+		if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN) {
+			if (points != NULL && n_points >= 2) {
+				if (n_segs == n_segs_max) {
+					n_segs_max <<= 1;
+					svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+					                            (n_segs_max - 1) *
+					                            sizeof(ArtSVPSeg));
+				}
+				svp->segs[n_segs].n_points = n_points;
+				svp->segs[n_segs].dir = (dir > 0);
+				if (dir < 0)
+					reverse_points(points, n_points);
+				svp->segs[n_segs].points = points;
+				svp->segs[n_segs].bbox.x0 = x_min;
+				svp->segs[n_segs].bbox.x1 = x_max;
+				svp->segs[n_segs].bbox.y0 = points[0].y;
+				svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+				n_segs++;
+				points = NULL;
+			}
+
+			if (points == NULL) {
+				n_points_max = 4;
+				points = art_new(ArtPoint, n_points_max);
+			}
+
+			n_points = 1;
+			points[0].x = x = vpath[i].x;
+			points[0].y = y = vpath[i].y;
+			x_min = x;
+			x_max = x;
+			dir = 0;
+		} else { /* must be LINETO */
+			new_dir = (vpath[i].y > y ||
+			           (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
+			if (dir && dir != new_dir) {
+				/* new segment */
+				x = points[n_points - 1].x;
+				y = points[n_points - 1].y;
+				if (n_segs == n_segs_max) {
+					n_segs_max <<= 1;
+					svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+					                            (n_segs_max - 1) *
+					                            sizeof(ArtSVPSeg));
+				}
+				svp->segs[n_segs].n_points = n_points;
+				svp->segs[n_segs].dir = (dir > 0);
+				if (dir < 0)
+					reverse_points(points, n_points);
+				svp->segs[n_segs].points = points;
+				svp->segs[n_segs].bbox.x0 = x_min;
+				svp->segs[n_segs].bbox.x1 = x_max;
+				svp->segs[n_segs].bbox.y0 = points[0].y;
+				svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+				n_segs++;
+
+				n_points = 1;
+				n_points_max = 4;
+				points = art_new(ArtPoint, n_points_max);
+				points[0].x = x;
+				points[0].y = y;
+				x_min = x;
+				x_max = x;
+			}
+
+			if (points != NULL) {
+				if (n_points == n_points_max)
+					art_expand(points, ArtPoint, n_points_max);
+				points[n_points].x = x = vpath[i].x;
+				points[n_points].y = y = vpath[i].y;
+				if (x < x_min) x_min = x;
+				else if (x > x_max) x_max = x;
+				n_points++;
+			}
+			dir = new_dir;
+		}
+		i++;
+	}
+
+	if (points != NULL) {
+		if (n_points >= 2) {
+			if (n_segs == n_segs_max) {
+				n_segs_max <<= 1;
+				svp = (ArtSVP *)realloc(svp, sizeof(ArtSVP) +
+				                            (n_segs_max - 1) *
+				                            sizeof(ArtSVPSeg));
+			}
+			svp->segs[n_segs].n_points = n_points;
+			svp->segs[n_segs].dir = (dir > 0);
+			if (dir < 0)
+				reverse_points(points, n_points);
+			svp->segs[n_segs].points = points;
+			svp->segs[n_segs].bbox.x0 = x_min;
+			svp->segs[n_segs].bbox.x1 = x_max;
+			svp->segs[n_segs].bbox.y0 = points[0].y;
+			svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+			n_segs++;
+		} else
+			free(points);
+	}
+
+	svp->n_segs = n_segs;
+
+	qsort(&svp->segs, n_segs, sizeof(ArtSVPSeg), art_svp_seg_compare);
+
+	return svp;
+}
+


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.h
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.h	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.h	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,30 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_VPATH_H__
+#define __ART_SVP_VPATH_H__
+
+#include "art.h"
+
+/* Sort vector paths into sorted vector paths. */
+
+ArtSVP *
+art_svp_from_vpath(ArtVpath *vpath);
+
+#endif /* __ART_SVP_VPATH_H__ */


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,643 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "art.h"
+#include "art_svp_vpath_stroke.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "art_svp_intersect.h"
+#include "art_svp_vpath.h"
+
+#define EPSILON 1e-6
+#define EPSILON_2 1e-12
+
+#define yes_OPTIMIZE_INNER
+
+/* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1,
+   yc + y1), centered at (xc, yc), and with given radius. Both x0^2 +
+   y0^2 and x1^2 + y1^2 should be equal to radius^2.
+
+   A positive value of radius means curve to the left, negative means
+   curve to the right.
+*/
+static void
+art_svp_vpath_stroke_arc(ArtVpath **p_vpath, int *pn, int *pn_max,
+                         double xc, double yc,
+                         double x0, double y0,
+                         double x1, double y1,
+                         double radius,
+                         double flatness) {
+	double theta;
+	double th_0, th_1;
+	int n_pts;
+	int i;
+	double aradius;
+
+	aradius = fabs(radius);
+	theta = 2 * M_SQRT2 * sqrt(flatness / aradius);
+	th_0 = atan2(y0, x0);
+	th_1 = atan2(y1, x1);
+	if (radius > 0) {
+		/* curve to the left */
+		if (th_0 < th_1) th_0 += M_PI * 2;
+		n_pts = ceil((th_0 - th_1) / theta);
+	} else {
+		/* curve to the right */
+		if (th_1 < th_0) th_1 += M_PI * 2;
+		n_pts = ceil((th_1 - th_0) / theta);
+	}
+#ifdef VERBOSE
+	printf("start %f %f; th_0 = %f, th_1 = %f, r = %f, theta = %f\n", x0, y0, th_0, th_1, radius, theta);
+#endif
+	art_vpath_add_point(p_vpath, pn, pn_max,
+	                    ART_LINETO, xc + x0, yc + y0);
+	for (i = 1; i < n_pts; i++) {
+		theta = th_0 + (th_1 - th_0) * i / n_pts;
+		art_vpath_add_point(p_vpath, pn, pn_max,
+		                    ART_LINETO, xc + cos(theta) * aradius,
+		                    yc + sin(theta) * aradius);
+#ifdef VERBOSE
+		printf("mid %f %f\n", cos(theta) * radius, sin(theta) * radius);
+#endif
+	}
+	art_vpath_add_point(p_vpath, pn, pn_max,
+	                    ART_LINETO, xc + x1, yc + y1);
+#ifdef VERBOSE
+	printf("end %f %f\n", x1, y1);
+#endif
+}
+
+/* Assume that forw and rev are at point i0. Bring them to i1,
+   joining with the vector i1 - i2.
+
+   This used to be true, but isn't now that the stroke_raw code is
+   filtering out (near)zero length vectors: {It so happens that all
+   invocations of this function maintain the precondition i1 = i0 + 1,
+   so we could decrease the number of arguments by one. We haven't
+   done that here, though.}
+
+   forw is to the line's right and rev is to its left.
+
+   Precondition: no zero-length vectors, otherwise a divide by
+   zero will happen.  */
+static void
+render_seg(ArtVpath **p_forw, int *pn_forw, int *pn_forw_max,
+           ArtVpath **p_rev, int *pn_rev, int *pn_rev_max,
+           ArtVpath *vpath, int i0, int i1, int i2,
+           ArtPathStrokeJoinType join,
+           double line_width, double miter_limit, double flatness) {
+	double dx0, dy0;
+	double dx1, dy1;
+	double dlx0, dly0;
+	double dlx1, dly1;
+	double dmx, dmy;
+	double dmr2;
+	double scale;
+	double cross;
+
+#ifdef VERBOSE
+	printf("join style = %d\n", join);
+#endif
+
+	/* The vectors of the lines from i0 to i1 and i1 to i2. */
+	dx0 = vpath[i1].x - vpath[i0].x;
+	dy0 = vpath[i1].y - vpath[i0].y;
+
+	dx1 = vpath[i2].x - vpath[i1].x;
+	dy1 = vpath[i2].y - vpath[i1].y;
+
+	/* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+	   90 degrees, and scaled to the length of line_width. */
+	scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
+	dlx0 = dy0 * scale;
+	dly0 = -dx0 * scale;
+
+	/* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise
+	   90 degrees, and scaled to the length of line_width. */
+	scale = line_width / sqrt(dx1 * dx1 + dy1 * dy1);
+	dlx1 = dy1 * scale;
+	dly1 = -dx1 * scale;
+
+#ifdef VERBOSE
+	printf("%% render_seg: (%g, %g) - (%g, %g) - (%g, %g)\n",
+	       vpath[i0].x, vpath[i0].y,
+	       vpath[i1].x, vpath[i1].y,
+	       vpath[i2].x, vpath[i2].y);
+
+	printf("%% render_seg: d[xy]0 = (%g, %g), dl[xy]0 = (%g, %g)\n",
+	       dx0, dy0, dlx0, dly0);
+
+	printf("%% render_seg: d[xy]1 = (%g, %g), dl[xy]1 = (%g, %g)\n",
+	       dx1, dy1, dlx1, dly1);
+#endif
+
+	/* now, forw's last point is expected to be colinear along d[xy]0
+	   to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */
+
+	/* positive for positive area (i.e. left turn) */
+	cross = dx1 * dy0 - dx0 * dy1;
+
+	dmx = (dlx0 + dlx1) * 0.5;
+	dmy = (dly0 + dly1) * 0.5;
+	dmr2 = dmx * dmx + dmy * dmy;
+
+	if (join == ART_PATH_STROKE_JOIN_MITER &&
+	        dmr2 * miter_limit * miter_limit < line_width * line_width)
+		join = ART_PATH_STROKE_JOIN_BEVEL;
+
+	/* the case when dmr2 is zero or very small bothers me
+	   (i.e. near a 180 degree angle)
+	   ALEX: So, we avoid the optimization when dmr2 is very small. This should
+	   be safe since dmx/y is only used in optimization and in MITER case, and MITER
+	   should be converted to BEVEL when dmr2 is very small. */
+	if (dmr2 > EPSILON_2) {
+		scale = line_width * line_width / dmr2;
+		dmx *= scale;
+		dmy *= scale;
+	}
+
+	if (cross *cross < EPSILON_2 && dx0 *dx1 + dy0 *dy1 >= 0) {
+		/* going straight */
+#ifdef VERBOSE
+		printf("%% render_seg: straight\n");
+#endif
+		art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+		                    ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+		art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+		                    ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+	} else if (cross > 0) {
+		/* left turn, forw is outside and rev is inside */
+
+#ifdef VERBOSE
+		printf("%% render_seg: left\n");
+#endif
+		if (
+#ifdef NO_OPTIMIZE_INNER
+		    0 &&
+#endif
+		    (dmr2 > EPSILON_2) &&
+		    /* check that i1 + dm[xy] is inside i0-i1 rectangle */
+		    (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 &&
+		    /* and that i1 + dm[xy] is inside i1-i2 rectangle */
+		    ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+		    &&
+		    /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */
+		    (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 &&
+		    /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */
+		    ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0)
+#endif
+		) {
+			/* can safely add single intersection point */
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+		} else {
+			/* need to loop-de-loop the inside */
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x, vpath[i1].y);
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+		}
+
+		if (join == ART_PATH_STROKE_JOIN_BEVEL) {
+			/* bevel */
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+		} else if (join == ART_PATH_STROKE_JOIN_MITER) {
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+		} else if (join == ART_PATH_STROKE_JOIN_ROUND)
+			art_svp_vpath_stroke_arc(p_forw, pn_forw, pn_forw_max,
+			                         vpath[i1].x, vpath[i1].y,
+			                         -dlx0, -dly0,
+			                         -dlx1, -dly1,
+			                         line_width,
+			                         flatness);
+	} else {
+		/* right turn, rev is outside and forw is inside */
+#ifdef VERBOSE
+		printf("%% render_seg: right\n");
+#endif
+
+		if (
+#ifdef NO_OPTIMIZE_INNER
+		    0 &&
+#endif
+		    (dmr2 > EPSILON_2) &&
+		    /* check that i1 - dm[xy] is inside i0-i1 rectangle */
+		    (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 &&
+		    /* and that i1 - dm[xy] is inside i1-i2 rectangle */
+		    ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+		    &&
+		    /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */
+		    (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 &&
+		    /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */
+		    ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0)
+#endif
+		) {
+			/* can safely add single intersection point */
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+		} else {
+			/* need to loop-de-loop the inside */
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x, vpath[i1].y);
+			art_vpath_add_point(p_forw, pn_forw, pn_forw_max,
+			                    ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+		}
+
+		if (join == ART_PATH_STROKE_JOIN_BEVEL) {
+			/* bevel */
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+		} else if (join == ART_PATH_STROKE_JOIN_MITER) {
+			art_vpath_add_point(p_rev, pn_rev, pn_rev_max,
+			                    ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+		} else if (join == ART_PATH_STROKE_JOIN_ROUND)
+			art_svp_vpath_stroke_arc(p_rev, pn_rev, pn_rev_max,
+			                         vpath[i1].x, vpath[i1].y,
+			                         dlx0, dly0,
+			                         dlx1, dly1,
+			                         -line_width,
+			                         flatness);
+
+	}
+}
+
+/* caps i1, under the assumption of a vector from i0 */
+static void
+render_cap(ArtVpath **p_result, int *pn_result, int *pn_result_max,
+           ArtVpath *vpath, int i0, int i1,
+           ArtPathStrokeCapType cap, double line_width, double flatness) {
+	double dx0, dy0;
+	double dlx0, dly0;
+	double scale;
+	int n_pts;
+	int i;
+
+	dx0 = vpath[i1].x - vpath[i0].x;
+	dy0 = vpath[i1].y - vpath[i0].y;
+
+	/* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+	   90 degrees, and scaled to the length of line_width. */
+	scale = line_width / sqrt(dx0 * dx0 + dy0 * dy0);
+	dlx0 = dy0 * scale;
+	dly0 = -dx0 * scale;
+
+#ifdef VERBOSE
+	printf("cap style = %d\n", cap);
+#endif
+
+	switch (cap) {
+	case ART_PATH_STROKE_CAP_BUTT:
+		art_vpath_add_point(p_result, pn_result, pn_result_max,
+		                    ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+		art_vpath_add_point(p_result, pn_result, pn_result_max,
+		                    ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+		break;
+	case ART_PATH_STROKE_CAP_ROUND:
+		n_pts = ceil(M_PI / (2.0 * M_SQRT2 * sqrt(flatness / line_width)));
+		art_vpath_add_point(p_result, pn_result, pn_result_max,
+		                    ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+		for (i = 1; i < n_pts; i++) {
+			double theta, c_th, s_th;
+
+			theta = M_PI * i / n_pts;
+			c_th = cos(theta);
+			s_th = sin(theta);
+			art_vpath_add_point(p_result, pn_result, pn_result_max,
+			                    ART_LINETO,
+			                    vpath[i1].x - dlx0 * c_th - dly0 * s_th,
+			                    vpath[i1].y - dly0 * c_th + dlx0 * s_th);
+		}
+		art_vpath_add_point(p_result, pn_result, pn_result_max,
+		                    ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+		break;
+	case ART_PATH_STROKE_CAP_SQUARE:
+		art_vpath_add_point(p_result, pn_result, pn_result_max,
+		                    ART_LINETO,
+		                    vpath[i1].x - dlx0 - dly0,
+		                    vpath[i1].y - dly0 + dlx0);
+		art_vpath_add_point(p_result, pn_result, pn_result_max,
+		                    ART_LINETO,
+		                    vpath[i1].x + dlx0 - dly0,
+		                    vpath[i1].y + dly0 + dlx0);
+		break;
+	}
+}
+
+/**
+ * art_svp_from_vpath_raw: Stroke a vector path, raw version
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Exactly the same as art_svp_vpath_stroke(), except that the resulting
+ * stroke outline may self-intersect and have regions of winding number
+ * greater than 1.
+ *
+ * Return value: Resulting raw stroked outline in svp format.
+ **/
+ArtVpath *
+art_svp_vpath_stroke_raw(ArtVpath *vpath,
+                         ArtPathStrokeJoinType join,
+                         ArtPathStrokeCapType cap,
+                         double line_width,
+                         double miter_limit,
+                         double flatness) {
+	int begin_idx, end_idx;
+	int i;
+	ArtVpath *forw, *rev;
+	int n_forw, n_rev;
+	int n_forw_max, n_rev_max;
+	ArtVpath *result;
+	int n_result, n_result_max;
+	double half_lw = 0.5 * line_width;
+	int closed;
+	int last, this_, next, second;
+	double dx, dy;
+
+	n_forw_max = 16;
+	forw = art_new(ArtVpath, n_forw_max);
+
+	n_rev_max = 16;
+	rev = art_new(ArtVpath, n_rev_max);
+
+	n_result = 0;
+	n_result_max = 16;
+	result = art_new(ArtVpath, n_result_max);
+
+	for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx) {
+		n_forw = 0;
+		n_rev = 0;
+
+		closed = (vpath[begin_idx].code == ART_MOVETO);
+
+		/* we don't know what the first point joins with until we get to the
+		     last point and see if it's closed. So we start with the second
+		     line in the path.
+
+		     Note: this is not strictly true (we now know it's closed from
+		     the opening pathcode), but why fix code that isn't broken?
+		*/
+
+		this_ = begin_idx;
+		/* skip over identical points at the beginning of the subpath */
+		for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
+			dx = vpath[i].x - vpath[this_].x;
+			dy = vpath[i].y - vpath[this_].y;
+			if (dx * dx + dy * dy > EPSILON_2)
+				break;
+		}
+		next = i;
+		second = next;
+
+		/* invariant: this doesn't coincide with next */
+		while (vpath[next].code == ART_LINETO) {
+			last = this_;
+			this_ = next;
+			/* skip over identical points after the beginning of the subpath */
+			for (i = this_ + 1; vpath[i].code == ART_LINETO; i++) {
+				dx = vpath[i].x - vpath[this_].x;
+				dy = vpath[i].y - vpath[this_].y;
+				if (dx * dx + dy * dy > EPSILON_2)
+					break;
+			}
+			next = i;
+			if (vpath[next].code != ART_LINETO) {
+				/* reached end of path */
+				/* make "closed" detection conform to PostScript
+				      semantics (i.e. explicit closepath code rather than
+				      just the fact that end of the path is the beginning) */
+				if (closed &&
+				        vpath[this_].x == vpath[begin_idx].x &&
+				        vpath[this_].y == vpath[begin_idx].y) {
+					int j;
+
+					/* path is closed, render join to beginning */
+					render_seg(&forw, &n_forw, &n_forw_max,
+					           &rev, &n_rev, &n_rev_max,
+					           vpath, last, this_, second,
+					           join, half_lw, miter_limit, flatness);
+
+#ifdef VERBOSE
+					printf("%% forw %d, rev %d\n", n_forw, n_rev);
+#endif
+					/* do forward path */
+					art_vpath_add_point(&result, &n_result, &n_result_max,
+					                    ART_MOVETO, forw[n_forw - 1].x,
+					                    forw[n_forw - 1].y);
+					for (j = 0; j < n_forw; j++)
+						art_vpath_add_point(&result, &n_result, &n_result_max,
+						                    ART_LINETO, forw[j].x,
+						                    forw[j].y);
+
+					/* do reverse path, reversed */
+					art_vpath_add_point(&result, &n_result, &n_result_max,
+					                    ART_MOVETO, rev[0].x,
+					                    rev[0].y);
+					for (j = n_rev - 1; j >= 0; j--)
+						art_vpath_add_point(&result, &n_result, &n_result_max,
+						                    ART_LINETO, rev[j].x,
+						                    rev[j].y);
+				} else {
+					/* path is open */
+					int j;
+
+					/* add to forw rather than result to ensure that
+					   forw has at least one point. */
+					render_cap(&forw, &n_forw, &n_forw_max,
+					           vpath, last, this_,
+					           cap, half_lw, flatness);
+					art_vpath_add_point(&result, &n_result, &n_result_max,
+					                    ART_MOVETO, forw[0].x,
+					                    forw[0].y);
+					for (j = 1; j < n_forw; j++)
+						art_vpath_add_point(&result, &n_result, &n_result_max,
+						                    ART_LINETO, forw[j].x,
+						                    forw[j].y);
+					for (j = n_rev - 1; j >= 0; j--)
+						art_vpath_add_point(&result, &n_result, &n_result_max,
+						                    ART_LINETO, rev[j].x,
+						                    rev[j].y);
+					render_cap(&result, &n_result, &n_result_max,
+					           vpath, second, begin_idx,
+					           cap, half_lw, flatness);
+					art_vpath_add_point(&result, &n_result, &n_result_max,
+					                    ART_LINETO, forw[0].x,
+					                    forw[0].y);
+				}
+			} else
+				render_seg(&forw, &n_forw, &n_forw_max,
+				           &rev, &n_rev, &n_rev_max,
+				           vpath, last, this_, next,
+				           join, half_lw, miter_limit, flatness);
+		}
+		end_idx = next;
+	}
+
+	free(forw);
+	free(rev);
+#ifdef VERBOSE
+	printf("%% n_result = %d\n", n_result);
+#endif
+	art_vpath_add_point(&result, &n_result, &n_result_max, ART_END, 0, 0);
+	return result;
+}
+
+#define noVERBOSE
+
+#ifdef VERBOSE
+
+#define XOFF 50
+#define YOFF 700
+
+static void
+print_ps_vpath(ArtVpath *vpath) {
+	int i;
+
+	for (i = 0; vpath[i].code != ART_END; i++) {
+		switch (vpath[i].code) {
+		case ART_MOVETO:
+			printf("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+			break;
+		case ART_LINETO:
+			printf("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+			break;
+		default:
+			break;
+		}
+	}
+	printf("stroke showpage\n");
+}
+
+static void
+print_ps_svp(ArtSVP *vpath) {
+	int i, j;
+
+	printf("%% begin\n");
+	for (i = 0; i < vpath->n_segs; i++) {
+		printf("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
+		for (j = 0; j < vpath->segs[i].n_points; j++) {
+			printf("%g %g %s\n",
+			       XOFF + vpath->segs[i].points[j].x,
+			       YOFF - vpath->segs[i].points[j].y,
+			       j ? "lineto" : "moveto");
+		}
+		printf("stroke\n");
+	}
+
+	printf("showpage\n");
+}
+#endif
+
+/* Render a vector path into a stroked outline.
+
+   Status of this routine:
+
+   Basic correctness: Only miter and bevel line joins are implemented,
+   and only butt line caps. Otherwise, seems to be fine.
+
+   Numerical stability: We cheat (adding random perturbation). Thus,
+   it seems very likely that no numerical stability problems will be
+   seen in practice.
+
+   Speed: Should be pretty good.
+
+   Precision: The perturbation fuzzes the coordinates slightly,
+   but not enough to be visible.  */
+/**
+ * art_svp_vpath_stroke: Stroke a vector path.
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Computes an svp representing the stroked outline of @vpath. The
+ * width of the stroked line is @line_width.
+ *
+ * Lines are joined according to the @join rule. Possible values are
+ * ART_PATH_STROKE_JOIN_MITER (for mitered joins),
+ * ART_PATH_STROKE_JOIN_ROUND (for round joins), and
+ * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join
+ * is converted to a bevelled join if the miter would extend to a
+ * distance of more than @miter_limit * @line_width from the actual
+ * join point.
+ *
+ * If there are open subpaths, the ends of these subpaths are capped
+ * according to the @cap rule. Possible values are
+ * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end
+ * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at
+ * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap,
+ * extending half @line_width past the end point).
+ *
+ * The @flatness parameter controls the accuracy of the rendering. It
+ * is most important for determining the number of points to use to
+ * approximate circular arcs for round lines and joins. In general, the
+ * resulting vector path will be within @flatness pixels of the "ideal"
+ * path containing actual circular arcs. I reserve the right to use
+ * the @flatness parameter to convert bevelled joins to miters for very
+ * small turn angles, as this would reduce the number of points in the
+ * resulting outline path.
+ *
+ * The resulting path is "clean" with respect to self-intersections, i.e.
+ * the winding number is 0 or 1 at each point.
+ *
+ * Return value: Resulting stroked outline in svp format.
+ **/
+ArtSVP *
+art_svp_vpath_stroke(ArtVpath *vpath,
+                     ArtPathStrokeJoinType join,
+                     ArtPathStrokeCapType cap,
+                     double line_width,
+                     double miter_limit,
+                     double flatness) {
+	ArtVpath *vpath_stroke;
+	ArtSVP *svp, *svp2;
+	ArtSvpWriter *swr;
+
+	vpath_stroke = art_svp_vpath_stroke_raw(vpath, join, cap,
+	                                        line_width, miter_limit, flatness);
+	svp = art_svp_from_vpath(vpath_stroke);
+	free(vpath_stroke);
+
+	swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO);
+	art_svp_intersector(svp, swr);
+
+	svp2 = art_svp_writer_rewind_reap(swr);
+	art_svp_free(svp);
+	return svp2;
+}


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.h
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.h	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.h	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,56 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_VPATH_STROKE_H__
+#define __ART_SVP_VPATH_STROKE_H__
+
+/* Sort vector paths into sorted vector paths. */
+
+#include "art.h"
+
+typedef enum {
+	ART_PATH_STROKE_JOIN_MITER,
+	ART_PATH_STROKE_JOIN_ROUND,
+	ART_PATH_STROKE_JOIN_BEVEL
+} ArtPathStrokeJoinType;
+
+typedef enum {
+	ART_PATH_STROKE_CAP_BUTT,
+	ART_PATH_STROKE_CAP_ROUND,
+	ART_PATH_STROKE_CAP_SQUARE
+} ArtPathStrokeCapType;
+
+ArtSVP *
+art_svp_vpath_stroke(ArtVpath *vpath,
+                     ArtPathStrokeJoinType join,
+                     ArtPathStrokeCapType cap,
+                     double line_width,
+                     double miter_limit,
+                     double flatness);
+
+/* This version may have winding numbers exceeding 1. */
+ArtVpath *
+art_svp_vpath_stroke_raw(ArtVpath *vpath,
+                         ArtPathStrokeJoinType join,
+                         ArtPathStrokeCapType cap,
+                         double line_width,
+                         double miter_limit,
+                         double flatness);
+
+#endif /* __ART_SVP_VPATH_STROKE_H__ */


Property changes on: scummvm/trunk/engines/sword25/gfx/image/art_svp_vpath_stroke.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/sword25/gfx/image/art_vpath_bpath.cpp
===================================================================
--- scummvm/trunk/engines/sword25/gfx/image/art_vpath_bpath.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sword25/gfx/image/art_vpath_bpath.cpp	2010-10-12 23:40:27 UTC (rev 53328)
@@ -0,0 +1,261 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for bezier paths */
+
+#include "art.h"
+
+#include <math.h>
+
+#define RENDER_LEVEL 4
+#define RENDER_SIZE (1 << (RENDER_LEVEL))
+
+/**
+ * art_vpath_render_bez: Render a bezier segment into the vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in *@p_vpath.
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @x0: X coordinate of starting bezier point.
+ * @y0: Y coordinate of starting bezier point.
+ * @x1: X coordinate of first bezier control point.
+ * @y1: Y coordinate of first bezier control point.

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list