diff options
| author | Eugene Sandulenko | 2010-09-07 12:11:00 +0000 | 
|---|---|---|
| committer | Eugene Sandulenko | 2010-10-12 23:40:27 +0000 | 
| commit | 94f1a8be0393d2f7cfccaa07dc168bea435e61dd (patch) | |
| tree | ad0f22883e7f6bd85ede1827c499e0c481f3e555 | |
| parent | 49decb9dc52eae5a43369d274ae2bee6ec43c1fe (diff) | |
| download | scummvm-rg350-94f1a8be0393d2f7cfccaa07dc168bea435e61dd.tar.gz scummvm-rg350-94f1a8be0393d2f7cfccaa07dc168bea435e61dd.tar.bz2 scummvm-rg350-94f1a8be0393d2f7cfccaa07dc168bea435e61dd.zip | |
SWORD25: Added minimal subset of libart inplace
svn-id: r53328
| -rwxr-xr-x | engines/sword25/gfx/image/art.cpp | 134 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art.h | 163 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_intersect.cpp | 1329 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_intersect.h | 58 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_render_aa.cpp | 428 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_render_aa.h | 55 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_vpath.cpp | 192 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_vpath.h | 30 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_vpath_stroke.cpp | 643 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_svp_vpath_stroke.h | 56 | ||||
| -rwxr-xr-x | engines/sword25/gfx/image/art_vpath_bpath.cpp | 261 | ||||
| -rw-r--r-- | engines/sword25/gfx/image/b25sloader.cpp | 2 | ||||
| -rw-r--r-- | engines/sword25/gfx/image/imageloader.cpp | 8 | ||||
| -rw-r--r-- | engines/sword25/gfx/image/pngloader.cpp | 8 | ||||
| -rw-r--r-- | engines/sword25/gfx/image/vectorimage.cpp | 28 | ||||
| -rw-r--r-- | engines/sword25/gfx/image/vectorimage.h | 13 | ||||
| -rw-r--r-- | engines/sword25/gfx/image/vectorimagerenderer.cpp | 45 | ||||
| -rw-r--r-- | engines/sword25/module.mk | 9 | 
18 files changed, 3408 insertions, 54 deletions
| diff --git a/engines/sword25/gfx/image/art.cpp b/engines/sword25/gfx/image/art.cpp new file mode 100755 index 0000000000..9471153a78 --- /dev/null +++ b/engines/sword25/gfx/image/art.cpp @@ -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; +} diff --git a/engines/sword25/gfx/image/art.h b/engines/sword25/gfx/image/art.h new file mode 100755 index 0000000000..1c0e6a8584 --- /dev/null +++ b/engines/sword25/gfx/image/art.h @@ -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__ */ diff --git a/engines/sword25/gfx/image/art_svp_intersect.cpp b/engines/sword25/gfx/image/art_svp_intersect.cpp new file mode 100755 index 0000000000..231d88d9bb --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_intersect.cpp @@ -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); +} diff --git a/engines/sword25/gfx/image/art_svp_intersect.h b/engines/sword25/gfx/image/art_svp_intersect.h new file mode 100755 index 0000000000..5e70ac6d79 --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_intersect.h @@ -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__ */ diff --git a/engines/sword25/gfx/image/art_svp_render_aa.cpp b/engines/sword25/gfx/image/art_svp_render_aa.cpp new file mode 100755 index 0000000000..d0342c28d7 --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_render_aa.cpp @@ -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); +} diff --git a/engines/sword25/gfx/image/art_svp_render_aa.h b/engines/sword25/gfx/image/art_svp_render_aa.h new file mode 100755 index 0000000000..ac4f538805 --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_render_aa.h @@ -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__ */ diff --git a/engines/sword25/gfx/image/art_svp_vpath.cpp b/engines/sword25/gfx/image/art_svp_vpath.cpp new file mode 100755 index 0000000000..e689e21e84 --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_vpath.cpp @@ -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; +} + diff --git a/engines/sword25/gfx/image/art_svp_vpath.h b/engines/sword25/gfx/image/art_svp_vpath.h new file mode 100755 index 0000000000..d0f5f51b28 --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_vpath.h @@ -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__ */ diff --git a/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp b/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp new file mode 100755 index 0000000000..66cf3b783c --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_vpath_stroke.cpp @@ -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; +} diff --git a/engines/sword25/gfx/image/art_svp_vpath_stroke.h b/engines/sword25/gfx/image/art_svp_vpath_stroke.h new file mode 100755 index 0000000000..d16e324a3a --- /dev/null +++ b/engines/sword25/gfx/image/art_svp_vpath_stroke.h @@ -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__ */ diff --git a/engines/sword25/gfx/image/art_vpath_bpath.cpp b/engines/sword25/gfx/image/art_vpath_bpath.cpp new file mode 100755 index 0000000000..82f1668ff7 --- /dev/null +++ b/engines/sword25/gfx/image/art_vpath_bpath.cpp @@ -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. + * @x2: X coordinate of second bezier control point. + * @y2: Y coordinate of second bezier control point. + * @x3: X coordinate of ending bezier point. + * @y3: Y coordinate of ending bezier point. + * @flatness: Flatness control. + * + * Renders a bezier segment into the vector path, reallocating and + * updating *@p_vpath and *@pn_vpath_max as necessary. *@pn_vpath is + * incremented by the number of vector points added. + * + * This step includes (@x0, @y0) but not (@x3, @y3). + * + * The @flatness argument guides the amount of subdivision. The Adobe + * PostScript reference manual defines flatness as the maximum + * deviation between the any point on the vpath approximation and the + * corresponding point on the "true" curve, and we follow this + * definition here. A value of 0.25 should ensure high quality for aa + * rendering. +**/ +static void +art_vpath_render_bez(ArtVpath **p_vpath, int *pn, int *pn_max, +                     double x0, double y0, +                     double x1, double y1, +                     double x2, double y2, +                     double x3, double y3, +                     double flatness) { +	double x3_0, y3_0; +	double z3_0_dot; +	double z1_dot, z2_dot; +	double z1_perp, z2_perp; +	double max_perp_sq; + +	double x_m, y_m; +	double xa1, ya1; +	double xa2, ya2; +	double xb1, yb1; +	double xb2, yb2; + +	/* It's possible to optimize this routine a fair amount. + +	   First, once the _dot conditions are met, they will also be met in +	   all further subdivisions. So we might recurse to a different +	   routine that only checks the _perp conditions. + +	   Second, the distance _should_ decrease according to fairly +	   predictable rules (a factor of 4 with each subdivision). So it might +	   be possible to note that the distance is within a factor of 4 of +	   acceptable, and subdivide once. But proving this might be hard. + +	   Third, at the last subdivision, x_m and y_m can be computed more +	   expeditiously (as in the routine above). + +	   Finally, if we were able to subdivide by, say 2 or 3, this would +	   allow considerably finer-grain control, i.e. fewer points for the +	   same flatness tolerance. This would speed things up downstream. + +	   In any case, this routine is unlikely to be the bottleneck. It's +	   just that I have this undying quest for more speed... + +	*/ + +	x3_0 = x3 - x0; +	y3_0 = y3 - y0; + +	/* z3_0_dot is dist z0-z3 squared */ +	z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0; + +	if (z3_0_dot < 0.001) { +		/* if start and end point are almost identical, the flatness tests +		 * don't work properly, so fall back on testing whether both of +		 * the other two control points are the same as the start point, +		 * too. +		 */ +		if (hypot(x1 - x0, y1 - y0) < 0.001 +		        && hypot(x2 - x0, y2 - y0) < 0.001) +			goto nosubdivide; +		else +			goto subdivide; +	} + +	/* we can avoid subdivision if: + +	   z1 has distance no more than flatness from the z0-z3 line + +	   z1 is no more z0'ward than flatness past z0-z3 + +	   z1 is more z0'ward than z3'ward on the line traversing z0-z3 + +	   and correspondingly for z2 */ + +	/* perp is distance from line, multiplied by dist z0-z3 */ +	max_perp_sq = flatness * flatness * z3_0_dot; + +	z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0; +	if (z1_perp * z1_perp > max_perp_sq) +		goto subdivide; + +	z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0; +	if (z2_perp * z2_perp > max_perp_sq) +		goto subdivide; + +	z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0; +	if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq) +		goto subdivide; + +	z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0; +	if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq) +		goto subdivide; + +	if (z1_dot + z1_dot > z3_0_dot) +		goto subdivide; + +	if (z2_dot + z2_dot > z3_0_dot) +		goto subdivide; + + +nosubdivide: +	/* don't subdivide */ +	art_vpath_add_point(p_vpath, pn, pn_max, +	                    ART_LINETO, x3, y3); +	return; + +subdivide: + +	xa1 = (x0 + x1) * 0.5; +	ya1 = (y0 + y1) * 0.5; +	xa2 = (x0 + 2 * x1 + x2) * 0.25; +	ya2 = (y0 + 2 * y1 + y2) * 0.25; +	xb1 = (x1 + 2 * x2 + x3) * 0.25; +	yb1 = (y1 + 2 * y2 + y3) * 0.25; +	xb2 = (x2 + x3) * 0.5; +	yb2 = (y2 + y3) * 0.5; +	x_m = (xa2 + xb1) * 0.5; +	y_m = (ya2 + yb1) * 0.5; +#ifdef VERBOSE +	printf("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2, +	       xb1, yb1, xb2, yb2); +#endif +	art_vpath_render_bez(p_vpath, pn, pn_max, +	                     x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness); +	art_vpath_render_bez(p_vpath, pn, pn_max, +	                     x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness); +} + +/** + * art_bez_path_to_vec: Create vpath from bezier path. + * @bez: Bezier path. + * @flatness: Flatness control. + * + * Creates a vector path closely approximating the bezier path defined by + * @bez. The @flatness argument controls the amount of subdivision. In + * general, the resulting vpath deviates by at most @flatness pixels + * from the "ideal" path described by @bez. + * + * Return value: Newly allocated vpath. + **/ +ArtVpath * +art_bez_path_to_vec(const ArtBpath *bez, double flatness) { +	ArtVpath *vec; +	int vec_n, vec_n_max; +	int bez_index; +	double x, y; + +	vec_n = 0; +	vec_n_max = RENDER_SIZE; +	vec = art_new(ArtVpath, vec_n_max); + +	/* Initialization is unnecessary because of the precondition that the +	   bezier path does not begin with LINETO or CURVETO, but is here +	   to make the code warning-free. */ +	x = 0; +	y = 0; + +	bez_index = 0; +	do { +#ifdef VERBOSE +		printf("%s %g %g\n", +		       bez[bez_index].code == ART_CURVETO ? "curveto" : +		       bez[bez_index].code == ART_LINETO ? "lineto" : +		       bez[bez_index].code == ART_MOVETO ? "moveto" : +		       bez[bez_index].code == ART_MOVETO_OPEN ? "moveto-open" : +		       "end", bez[bez_index].x3, bez[bez_index].y3); +#endif +		/* make sure space for at least one more code */ +		if (vec_n >= vec_n_max) +			art_expand(vec, ArtVpath, vec_n_max); +		switch (bez[bez_index].code) { +		case ART_MOVETO_OPEN: +		case ART_MOVETO: +		case ART_LINETO: +			x = bez[bez_index].x3; +			y = bez[bez_index].y3; +			vec[vec_n].code = bez[bez_index].code; +			vec[vec_n].x = x; +			vec[vec_n].y = y; +			vec_n++; +			break; +		case ART_END: +			vec[vec_n].code = bez[bez_index].code; +			vec[vec_n].x = 0; +			vec[vec_n].y = 0; +			vec_n++; +			break; +		case ART_CURVETO: +#ifdef VERBOSE +			printf("%g,%g %g,%g %g,%g %g,%g\n", x, y, +			       bez[bez_index].x1, bez[bez_index].y1, +			       bez[bez_index].x2, bez[bez_index].y2, +			       bez[bez_index].x3, bez[bez_index].y3); +#endif +			art_vpath_render_bez(&vec, &vec_n, &vec_n_max, +			                     x, y, +			                     bez[bez_index].x1, bez[bez_index].y1, +			                     bez[bez_index].x2, bez[bez_index].y2, +			                     bez[bez_index].x3, bez[bez_index].y3, +			                     flatness); +			x = bez[bez_index].x3; +			y = bez[bez_index].y3; +			break; +		} +	} while (bez[bez_index++].code != ART_END); +	return vec; +} + diff --git a/engines/sword25/gfx/image/b25sloader.cpp b/engines/sword25/gfx/image/b25sloader.cpp index 6d548a0d32..3be6374e67 100644 --- a/engines/sword25/gfx/image/b25sloader.cpp +++ b/engines/sword25/gfx/image/b25sloader.cpp @@ -88,7 +88,7 @@ bool B25SLoader::IsCorrectImageFormat(const byte *FileDataPtr, uint FileSize) {  // -----------------------------------------------------------------------------  bool B25SLoader::DecodeImage(const byte *FileDataPtr, uint FileSize, GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr, -                                int &Width, int &Height, int &Pitch) { +                             int &Width, int &Height, int &Pitch) {  	// PNG innerhalb des Spielstandes finden und den Methodenaufruf zu BS_PNGLoader weiterreichen.  	uint PNGOffset = FindEmbeddedPNG(FileDataPtr, FileSize);  	if (PNGOffset > 0) { diff --git a/engines/sword25/gfx/image/imageloader.cpp b/engines/sword25/gfx/image/imageloader.cpp index 9ecd358071..55ce833cc9 100644 --- a/engines/sword25/gfx/image/imageloader.cpp +++ b/engines/sword25/gfx/image/imageloader.cpp @@ -47,10 +47,10 @@ bool ImageLoader::_ImageLoaderListInitialized = false;  // ------------  bool ImageLoader::LoadImage(const byte *pFileData, uint FileSize, -                               GraphicEngine::COLOR_FORMATS ColorFormat, -                               byte *&pUncompressedData, -                               int &Width, int &Height, -                               int &Pitch) { +                            GraphicEngine::COLOR_FORMATS ColorFormat, +                            byte *&pUncompressedData, +                            int &Width, int &Height, +                            int &Pitch) {  	// Falls die Liste der BS_ImageLoader noch nicht initialisiert wurde, wird dies getan.  	if (!_ImageLoaderListInitialized)  		_InitializeLoaderList(); diff --git a/engines/sword25/gfx/image/pngloader.cpp b/engines/sword25/gfx/image/pngloader.cpp index 989eada8e9..c59d68724d 100644 --- a/engines/sword25/gfx/image/pngloader.cpp +++ b/engines/sword25/gfx/image/pngloader.cpp @@ -63,7 +63,7 @@ static void png_user_read_data(png_structp png_ptr, png_bytep data, png_size_t l  // -----------------------------------------------------------------------------  bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize,  GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr, -                                 int &Width, int &Height, int &Pitch) { +                              int &Width, int &Height, int &Pitch) {  	png_structp png_ptr = NULL;  	png_infop   info_ptr = NULL;  	png_bytep   RawDataBuffer = NULL; @@ -125,7 +125,7 @@ bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize,  GraphicEn  	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))  		png_set_expand(png_ptr);  	if (ColorType == PNG_COLOR_TYPE_GRAY || -		ColorType == PNG_COLOR_TYPE_GRAY_ALPHA) +	        ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)  		png_set_gray_to_rgb(png_ptr);  	png_set_bgr(png_ptr); @@ -191,7 +191,7 @@ bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize,  GraphicEn  			break;  		}  	} -	 +  	// Die zusätzlichen Daten am Ende des Bildes lesen  	png_read_end(png_ptr, NULL); @@ -209,7 +209,7 @@ bool PNGLoader::DoDecodeImage(const byte *FileDataPtr, uint FileSize,  GraphicEn  // -----------------------------------------------------------------------------  bool PNGLoader::DecodeImage(const byte *FileDataPtr, uint FileSize,  GraphicEngine::COLOR_FORMATS ColorFormat, byte *&UncompressedDataPtr, -                               int &Width, int &Height, int &Pitch) { +                            int &Width, int &Height, int &Pitch) {  	return DoDecodeImage(FileDataPtr, FileSize, ColorFormat, UncompressedDataPtr, Width, Height, Pitch);  } diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp index 13ef9bf55d..e926e13745 100644 --- a/engines/sword25/gfx/image/vectorimage.cpp +++ b/engines/sword25/gfx/image/vectorimage.cpp @@ -41,7 +41,7 @@  #include "graphics/colormasks.h" -#include <libart_lgpl/art_vpath_bpath.h> +#include "art_vpath_bpath.h"  #include "sword25/gfx/opengl/glimage.h" @@ -209,7 +209,7 @@ Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement)  				if (vec[i].y > y1) y1 = vec[i].y;  			}  		} -		art_free(vec); +		free(vec);  	}  	return Common::Rect(static_cast<int>(x0), static_cast<int>(y0), static_cast<int>(x1) + 1, static_cast<int>(y1) + 1); @@ -235,8 +235,8 @@ VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, co  	signature[1] = bs.getByte();  	signature[2] = bs.getByte();  	if (signature[0] != 'F' || -		signature[1] != 'W' || -		signature[2] != 'S') { +	        signature[1] != 'W' || +	        signature[2] != 'S') {  		BS_LOG_ERRORLN("File is not a valid SWF-file");  		return;  	} @@ -259,8 +259,10 @@ VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, co  	Common::Rect movieRect = flashRectToBSRect(bs);  	// Framerate und Frameanzahl auslesen -	/* uint32 frameRate = */bs.getUInt16(); -	/* uint32 frameCount = */bs.getUInt16(); +	/* uint32 frameRate = */ +	bs.getUInt16(); +	/* uint32 frameCount = */ +	bs.getUInt16();  	// Tags parsen  	// Da wir uns nur für das erste DefineShape-Tag interessieren @@ -303,7 +305,7 @@ VectorImage::~VectorImage() {  	for (int j = _elements.size() - 1; j >= 0; j--)  		for (int i = _elements[j].getPathCount() - 1; i >= 0; i--)  			if (_elements[j].getPathInfo(i).getVec()) -				art_free(_elements[j].getPathInfo(i).getVec()); +				free(_elements[j].getPathInfo(i).getVec());  	if (_pixelData)  		free(_pixelData); @@ -380,7 +382,7 @@ bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {  			// End der Shape-Definition erreicht?  			if (!stateNewStyles && !stateLineStyle && !stateFillStyle0 && !stateFillStyle1 && !stateMoveTo) {  				endOfShapeDiscovered = true; -			// Parameter dekodieren +				// Parameter dekodieren  			} else {  				if (stateMoveTo) {  					uint32 moveToBits = bs.getBits(5); @@ -496,7 +498,7 @@ bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {  	if (bezNodes)  		bez = storeBez(bez, lineStyle, fillStyle0, fillStyle1, &bezNodes, &bezAllocated); -	art_free(bez); +	free(bez);  	// Bounding-Boxes der einzelnen Elemente berechnen  	Common::Array<VectorImageElement>::iterator it = _elements.begin(); @@ -599,10 +601,10 @@ bool VectorImage::setContent(const byte *pixeldata, uint size, uint offset, uint  }  bool VectorImage::blit(int posX, int posY, -                          int flipping, -                          Common::Rect *pPartRect, -                          uint color, -                          int width, int height) { +                       int flipping, +                       Common::Rect *pPartRect, +                       uint color, +                       int width, int height) {  	static VectorImage *oldThis = 0;  	static int              oldWidth = -2;  	static int              oldHeight = -2; diff --git a/engines/sword25/gfx/image/vectorimage.h b/engines/sword25/gfx/image/vectorimage.h index b3cc34e25c..3477463b43 100644 --- a/engines/sword25/gfx/image/vectorimage.h +++ b/engines/sword25/gfx/image/vectorimage.h @@ -43,9 +43,7 @@  #include "sword25/gfx/image/image.h"  #include "common/rect.h" -#include <libart_lgpl/art_vpath.h> -#include <libart_lgpl/art_bpath.h> -#include <libart_lgpl/art_misc.h> +#include "art.h"  namespace Sword25 { @@ -60,8 +58,8 @@ class VectorImage;  class VectorPathInfo {  public: - VectorPathInfo(ArtBpath *vec, int len, uint lineStyle, uint fillStyle0, uint fillStyle1) : -	_vec(vec), _lineStyle(lineStyle), _fillStyle0(fillStyle0), _fillStyle1(fillStyle1), _len(len) {} +	VectorPathInfo(ArtBpath *vec, int len, uint lineStyle, uint fillStyle0, uint fillStyle1) : +		_vec(vec), _lineStyle(lineStyle), _fillStyle0(fillStyle0), _fillStyle1(fillStyle1), _len(len) {}  	VectorPathInfo() {  		_lineStyle = _fillStyle0 = _fillStyle1 = _len = 0; @@ -137,7 +135,10 @@ public:  private:  	struct LineStyleType {  		LineStyleType(double width_, uint32 color_) : width(width_), color(color_) {} -		LineStyleType() { width = 0; color = 0; } +		LineStyleType() { +			width = 0; +			color = 0; +		}  		double width;  		uint32 color;  	}; diff --git a/engines/sword25/gfx/image/vectorimagerenderer.cpp b/engines/sword25/gfx/image/vectorimagerenderer.cpp index 715d777c83..bbdee2def9 100644 --- a/engines/sword25/gfx/image/vectorimagerenderer.cpp +++ b/engines/sword25/gfx/image/vectorimagerenderer.cpp @@ -32,13 +32,11 @@   *   */ -#include <libart_lgpl/art_vpath_bpath.h> -#include <libart_lgpl/art_vpath_bpath.h> -#include <libart_lgpl/art_svp_vpath.h> -#include <libart_lgpl/art_svp_vpath_stroke.h> -#include <libart_lgpl/art_svp_render_aa.h> -#include <libart_lgpl/art_rgb_svp.h> -#include <libart_lgpl/art_rgb.h> +#include "art_vpath_bpath.h" +#include "art_vpath_bpath.h" +#include "art_svp_vpath.h" +#include "art_svp_vpath_stroke.h" +#include "art_svp_render_aa.h"  #include "sword25/gfx/image/vectorimage.h"  #include "graphics/colormasks.h" @@ -86,7 +84,7 @@ struct _ArtRgbSVPAlphaData {  };  static void art_rgb_svp_alpha_callback1(void *callback_data, int y, -                            int start, ArtSVPRenderAAStep *steps, int n_steps) { +                                        int start, ArtSVPRenderAAStep *steps, int n_steps) {  	ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;  	art_u8 *linebuf;  	int run_x0, run_x1; @@ -140,8 +138,8 @@ static void art_rgb_svp_alpha_callback1(void *callback_data, int y,  }  static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y, -                                   int start, -                                   ArtSVPRenderAAStep *steps, int n_steps) { +        int start, +        ArtSVPRenderAAStep *steps, int n_steps) {  	ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;  	art_u8 *linebuf;  	int run_x0, run_x1; @@ -211,10 +209,9 @@ static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,  }  void art_rgb_svp_alpha1(const ArtSVP *svp, -                   int x0, int y0, int x1, int y1, -                   uint32 color, -                   art_u8 *buf, int rowstride, -                   ArtAlphaGamma *alphagamma) { +                        int x0, int y0, int x1, int y1, +                        uint32 color, +                        art_u8 *buf, int rowstride) {  	ArtRgbSVPAlphaData data;  	byte r, g, b, alpha;  	int i; @@ -299,7 +296,7 @@ ArtVpath *art_vpath_reverse(ArtVpath *a) {  			state = 1;  		}  		if (a[len - i - 1].code == ART_MOVETO || -			a[len - i - 1].code == ART_MOVETO_OPEN) { +		        a[len - i - 1].code == ART_MOVETO_OPEN) {  			state = 0;  		}  		dest[i] = it; @@ -313,7 +310,7 @@ ArtVpath *art_vpath_reverse_free(ArtVpath *a) {  	ArtVpath *dest;  	dest = art_vpath_reverse(a); -	art_free(a); +	free(a);  	return dest;  } @@ -346,8 +343,8 @@ void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int heig  		vec2 = art_vpath_reverse_free(vec2);  		vec = art_vpath_cat(vec1, vec2); -		art_free(vec1); -		art_free(vec2); +		free(vec1); +		free(vec2);  	} else {  		vec = vec1;  	} @@ -365,7 +362,7 @@ void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int heig  			vect[k].y = vec[k].y * scaleY;  		}  		vect[k].code = ART_END; -		art_free(vec); +		free(vec);  		vec = vect;  	} @@ -377,10 +374,10 @@ void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int heig  		art_svp_make_convex(svp);  	} -	art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4, NULL); +	art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4); -	art_free(svp); -	art_free(vec); +	free(svp); +	free(vec);  }  void VectorImage::render(int width, int height) { @@ -436,8 +433,8 @@ void VectorImage::render(int width, int height) {  			drawBez(fill1, fill0, _pixelData, width, height, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s)); -			art_free(fill0); -			art_free(fill1); +			free(fill0); +			free(fill1);  		}  		//// Draw strokes diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 4eb113a0e9..ba7a763088 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -32,6 +32,12 @@ MODULE_OBJS := \  	gfx/image/pngloader.o \  	gfx/image/vectorimage.o \  	gfx/image/vectorimagerenderer.o \ +	gfx/image/art.o \ +	gfx/image/art_svp_intersect.o \ +	gfx/image/art_svp_render_aa.o \ +	gfx/image/art_svp_vpath.o \ +	gfx/image/art_svp_vpath_stroke.o \ +	gfx/image/art_vpath_bpath.o \  	gfx/opengl/glimage.o \  	gfx/opengl/openglgfx.o \  	gfx/opengl/swimage.o \ @@ -109,8 +115,7 @@ MODULE_OBJS := \  	$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)  	$(QUIET_CXX)gcc  $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $*.o -LIBS += -lpng -ltheoradec -lart_lgpl_2 -CXXFLAGS += -I/usr/include/libart-2.0 +LIBS += -lpng -ltheoradec  # This module can be built as a plugin  ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) | 
