aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2010-09-18 10:03:54 +0000
committerEugene Sandulenko2010-10-12 23:58:51 +0000
commit1e07d9561fc43e1b1c0763d54c3eea4154fff070 (patch)
treec101b3f437b2afca186627455a9278670f241906
parentb2003364ff109e1f0170ea87fe379496631f6040 (diff)
downloadscummvm-rg350-1e07d9561fc43e1b1c0763d54c3eea4154fff070.tar.gz
scummvm-rg350-1e07d9561fc43e1b1c0763d54c3eea4154fff070.tar.bz2
scummvm-rg350-1e07d9561fc43e1b1c0763d54c3eea4154fff070.zip
SWORD25: Add standalone SWF renderer
svn-id: r53372
-rw-r--r--engines/sword25/tools/swfdisplay/Makefile33
-rw-r--r--engines/sword25/tools/swfdisplay/algorithm.h255
-rw-r--r--engines/sword25/tools/swfdisplay/array.h318
-rw-r--r--engines/sword25/tools/swfdisplay/art-non-display1.cpp488
-rw-r--r--engines/sword25/tools/swfdisplay/art.cpp134
-rw-r--r--engines/sword25/tools/swfdisplay/art.h182
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_intersect.cpp1329
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_intersect.h58
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_render_aa.cpp428
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_render_aa.h55
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_vpath.cpp192
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_vpath.h30
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_vpath_stroke.cpp643
-rw-r--r--engines/sword25/tools/swfdisplay/art_svp_vpath_stroke.h56
-rw-r--r--engines/sword25/tools/swfdisplay/art_vpath_bpath.cpp261
-rw-r--r--engines/sword25/tools/swfdisplay/bezierrender.cpp584
-rw-r--r--engines/sword25/tools/swfdisplay/func.h542
-rw-r--r--engines/sword25/tools/swfdisplay/rect.h223
-rw-r--r--engines/sword25/tools/swfdisplay/swfrender.cpp172
-rw-r--r--engines/sword25/tools/swfdisplay/vectorimage.cpp658
-rw-r--r--engines/sword25/tools/swfdisplay/vectorimage.h233
-rw-r--r--engines/sword25/tools/swfdisplay/vectorimagerenderer.cpp480
22 files changed, 7354 insertions, 0 deletions
diff --git a/engines/sword25/tools/swfdisplay/Makefile b/engines/sword25/tools/swfdisplay/Makefile
new file mode 100644
index 0000000000..7e2005134e
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/Makefile
@@ -0,0 +1,33 @@
+CC = g++
+DEBFLAGS = -g -Wall -O0
+LDFLAGS = -lgdk_pixbuf-2.0 -lpng
+CXXFLAGS = -I/usr/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include/ -I/usr/include/gdk-pixbuf-2.0/
+BCFLAGS = -I/usr/include/libart-2.0 -I/usr/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include/ -I/usr/include/gdk-pixbuf-2.0/
+BIN_TARGET = swfrender
+OBJ_TARGETS = \
+ swfrender.o \
+ art.o \
+ art_svp_intersect.o \
+ art_svp_render_aa.o \
+ art_svp_vpath.o \
+ art_svp_vpath_stroke.o \
+ art_vpath_bpath.o \
+ vectorimage.o \
+ vectorimagerenderer.o
+
+all: ${BIN_TARGET}
+
+${BIN_TARGET}: ${OBJ_TARGETS}
+ ${CC} ${DEBFLAGS} $^ -o $@ ${LDFLAGS}
+
+%.o: %.cpp Makefile
+ ${CC} ${CXXFLAGS} ${DEBFLAGS} -c $< -o $@
+
+bezierrender: bezierrender.o
+ ${CC} ${DEBFLAGS} $^ -o $@ ${LDFLAGS} -lart_lgpl_2
+
+bezierrender.o: bezierrender.cpp
+ ${CC} ${BCFLAGS} ${DEBFLAGS} -c $< -o $@
+
+clean: ${BIN_TARGET} Makefile
+ rm -f *~ *.o ${BIN_TARGET} 2>/dev/null
diff --git a/engines/sword25/tools/swfdisplay/algorithm.h b/engines/sword25/tools/swfdisplay/algorithm.h
new file mode 100644
index 0000000000..8d6743a911
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/algorithm.h
@@ -0,0 +1,255 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/common/algorithm.h $
+ * $Id: algorithm.h 51524 2010-07-30 23:42:50Z lordhoto $
+ */
+
+#ifndef COMMON_ALGORITHM_H
+#define COMMON_ALGORITHM_H
+
+#include "func.h"
+
+namespace Common {
+
+/**
+ * Copies data from the range [first, last) to [dst, dst + (last - first)).
+ * It requires the range [dst, dst + (last - first)) to be valid.
+ * It also requires dst not to be in the range [first, last).
+ */
+template<class In, class Out>
+Out copy(In first, In last, Out dst) {
+ while (first != last)
+ *dst++ = *first++;
+ return dst;
+}
+
+/**
+ * Copies data from the range [first, last) to [dst - (last - first), dst).
+ * It requires the range [dst - (last - first), dst) to be valid.
+ * It also requires dst not to be in the range [first, last).
+ *
+ * Unlike copy copy_backward copies the data from the end to the beginning.
+ */
+template<class In, class Out>
+Out copy_backward(In first, In last, Out dst) {
+ while (first != last)
+ *--dst = *--last;
+ return dst;
+}
+
+/**
+ * Copies data from the range [first, last) to [dst, dst + (last - first)).
+ * It requires the range [dst, dst + (last - first)) to be valid.
+ * It also requires dst not to be in the range [first, last).
+ *
+ * Unlike copy or copy_backward it does not copy all data. It only copies
+ * a data element when operator() of the op parameter returns true for the
+ * passed data element.
+ */
+template<class In, class Out, class Op>
+Out copy_if(In first, In last, Out dst, Op op) {
+ while (first != last) {
+ if (op(*first))
+ *dst++ = *first;
+ ++first;
+ }
+ return dst;
+}
+
+// Our 'specialized' 'set_to' template for char, signed char and unsigned char arrays.
+// Since C++ doesn't support partial specialized template functions (currently) we
+// are going this way...
+// With this we assure the usage of memset for those, which should be
+// faster than a simple loop like for the generic 'set_to'.
+template<class Value>
+signed char *set_to(signed char *first, signed char *last, Value val) {
+ memset(first, (val & 0xFF), last - first);
+ return last;
+}
+
+template<class Value>
+unsigned char *set_to(unsigned char *first, unsigned char *last, Value val) {
+ memset(first, (val & 0xFF), last - first);
+ return last;
+}
+
+template<class Value>
+char *set_to(char *first, char *last, Value val) {
+ memset(first, (val & 0xFF), last - first);
+ return last;
+}
+
+/**
+ * Sets all elements in the range [first, last) to val.
+ */
+template<class In, class Value>
+In set_to(In first, In last, Value val) {
+ while (first != last)
+ *first++ = val;
+ return first;
+}
+
+/**
+ * Finds the first data value in the range [first, last) matching v.
+ * For data comperance it uses operator == of the data elements.
+ */
+template<class In, class T>
+In find(In first, In last, const T &v) {
+ while (first != last) {
+ if (*first == v)
+ return first;
+ ++first;
+ }
+ return last;
+}
+
+/**
+ * Finds the first data value in the range [first, last) for which
+ * the specified predicate p returns true.
+ */
+template<class In, class Pred>
+In find_if(In first, In last, Pred p) {
+ while (first != last) {
+ if (p(*first))
+ return first;
+ ++first;
+ }
+ return last;
+}
+
+/**
+ * Applies the function f on all elements of the range [first, last).
+ * The processing order is from beginning to end.
+ */
+template<class In, class Op>
+Op for_each(In first, In last, Op f) {
+ while (first != last) f(*first++);
+ return f;
+}
+
+template<typename T>
+unsigned int distance(T *first, T *last) {
+ return last - first;
+}
+
+template<typename T>
+unsigned int distance(T first, T last) {
+ unsigned int n = 0;
+ while (first != last) {
+ ++n;
+ ++first;
+ }
+ return n;
+}
+
+template<typename T>
+T *sortChoosePivot(T *first, T *last) {
+ return first + distance(first, last) / 2;
+}
+
+template<typename T>
+T sortChoosePivot(T first, T last) {
+ unsigned int n = distance(first, last);
+ n /= 2;
+ while (n--)
+ ++first;
+ return first;
+}
+
+template<typename T, class StrictWeakOrdering>
+T sortPartition(T first, T last, T pivot, StrictWeakOrdering &comp) {
+ --last;
+ SWAP(*pivot, *last);
+
+ T sorted;
+ for (sorted = first; first != last; ++first) {
+ if (!comp(*last, *first)) {
+ if (first != sorted)
+ SWAP(*first, *sorted);
+ ++sorted;
+ }
+ }
+
+ SWAP(*last, *sorted);
+ return sorted;
+}
+
+/**
+ * Simple sort function, modeled after std::sort.
+ * It compares data with the given comparator object comp.
+ *
+ * Like std::sort this is not guaranteed to be stable.
+ *
+ * Two small quotes from wikipedia about stability:
+ *
+ * Stable sorting algorithms maintain the relative order of records with
+ * equal keys.
+ *
+ * Unstable sorting algorithms may change the relative order of records with
+ * equal keys, but stable sorting algorithms never do so.
+ *
+ * For more information on that topic check out:
+ * http://en.wikipedia.org/wiki/Sorting_algorithm#Stability
+ *
+ * NOTE: Actually as the time of writing our implementation is unstable.
+ */
+template<typename T, class StrictWeakOrdering>
+void sort(T first, T last, StrictWeakOrdering comp) {
+ if (first == last)
+ return;
+
+ T pivot = sortChoosePivot(first, last);
+ pivot = sortPartition(first, last, pivot, comp);
+ sort<T, StrictWeakOrdering>(first, pivot, comp);
+ sort<T, StrictWeakOrdering>(++pivot, last, comp);
+}
+
+/**
+ * Simple sort function, modeled after std::sort.
+ */
+template<typename T>
+void sort(T *first, T *last) {
+ sort(first, last, Common::Less<T>());
+}
+
+template<class T>
+void sort(T first, T last) {
+ sort(first, last, Common::Less<typename T::ValueType>());
+}
+
+/**
+ * Euclid's algorithm to compute the greatest common divisor.
+ */
+template<class T>
+T gcd(T a, T b) {
+ if (a <= 0) a = -a;
+ if (b <= 0) b = -b;
+ while (a > 0) {
+ T tmp = a;
+ a = b % a;
+ b = tmp;
+ }
+ return b;
+}
+
+} // End of namespace Common
+#endif
+
diff --git a/engines/sword25/tools/swfdisplay/array.h b/engines/sword25/tools/swfdisplay/array.h
new file mode 100644
index 0000000000..f09db9c63c
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/array.h
@@ -0,0 +1,318 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/common/array.h $
+ * $Id: array.h 45247 2009-10-19 17:46:50Z fingolfin $
+ */
+
+#ifndef COMMON_ARRAY_H
+#define COMMON_ARRAY_H
+
+#include "algorithm.h"
+
+namespace Common {
+
+/**
+ * This class implements a dynamically sized container, which
+ * can be accessed similar to a regular C++ array. Accessing
+ * elements is performed in constant time (like with plain arrays).
+ * In addition, one can append, insert and remove entries (this
+ * is the 'dynamic' part). Doing that in general takes time
+ * proportional to the number of elements in the array.
+ *
+ * The container class closest to this in the C++ standard library is
+ * std::vector. However, there are some differences. The most important one is
+ * that std::vector has a far more sophisticated (and complicated) memory
+ * management scheme. There, only elements that 'live' are actually constructed
+ * (i.e., have their constructor called), and objects that are removed are
+ * immediately destructed (have their destructor called).
+ * With Common::Array, this is not the case; instead, it simply uses new[] and
+ * delete[] to allocate whole blocks of objects, possibly more than are
+ * currently 'alive'. This simplifies memory management, but may have
+ * undesirable side effects when one wants to use an Array of complex
+ * data types.
+ *
+ * @todo Improve the storage management of this class.
+ * In particular, don't use new[] and delete[], but rather
+ * construct/destruct objects manually. This way, we can
+ * ensure that storage which is not currently used does not
+ * correspond to a live active object.
+ * (This is only of interest for array of non-POD objects).
+ */
+template<class T>
+class Array {
+protected:
+ uint _capacity;
+ uint _size;
+ T *_storage;
+
+public:
+ typedef T *iterator;
+ typedef const T *const_iterator;
+
+ typedef T value_type;
+
+public:
+ Array() : _capacity(0), _size(0), _storage(0) {}
+
+ Array(const Array<T> &array) : _capacity(array._size), _size(array._size), _storage(0) {
+ if (array._storage) {
+ _storage = new T[_capacity];
+ assert(_storage);
+ copy(array._storage, array._storage + _size, _storage);
+ }
+ }
+
+ /**
+ * Construct an array by copying data from a regular array.
+ */
+ template<class T2>
+ Array(const T2 *data, int n) {
+ _capacity = _size = n;
+ _storage = new T[_capacity];
+ assert(_storage);
+ copy(data, data + _size, _storage);
+ }
+
+ ~Array() {
+ delete[] _storage;
+ _storage = 0;
+ _capacity = _size = 0;
+ }
+
+ /** Appends element to the end of the array. */
+ void push_back(const T &element) {
+ if (_size + 1 <= _capacity)
+ _storage[_size++] = element;
+ else
+ insert_aux(end(), &element, &element + 1);
+ }
+
+ void push_back(const Array<T> &array) {
+ if (_size + array.size() <= _capacity) {
+ copy(array.begin(), array.end(), end());
+ _size += array.size();
+ } else
+ insert_aux(end(), array.begin(), array.end());
+ }
+
+ /** Removes the last element of the array. */
+ void pop_back() {
+ assert(_size > 0);
+ _size--;
+ }
+
+ /** Returns a reference to the first element of the array. */
+ T &front() {
+ assert(_size > 0);
+ return _storage[0];
+ }
+
+ /** Returns a reference to the first element of the array. */
+ const T &front() const {
+ assert(_size > 0);
+ return _storage[0];
+ }
+
+ /** Returns a reference to the last element of the array. */
+ T &back() {
+ assert(_size > 0);
+ return _storage[_size-1];
+ }
+
+ /** Returns a reference to the last element of the array. */
+ const T &back() const {
+ assert(_size > 0);
+ return _storage[_size-1];
+ }
+
+
+ void insert_at(int idx, const T &element) {
+ assert(idx >= 0 && (uint)idx <= _size);
+ insert_aux(_storage + idx, &element, &element + 1);
+ }
+
+ T remove_at(int idx) {
+ assert(idx >= 0 && (uint)idx < _size);
+ T tmp = _storage[idx];
+ copy(_storage + idx + 1, _storage + _size, _storage + idx);
+ _size--;
+ return tmp;
+ }
+
+ // TODO: insert, remove, ...
+
+ T& operator[](int idx) {
+ assert(idx >= 0 && (uint)idx < _size);
+ return _storage[idx];
+ }
+
+ const T& operator[](int idx) const {
+ assert(idx >= 0 && (uint)idx < _size);
+ return _storage[idx];
+ }
+
+ Array<T>& operator=(const Array<T> &array) {
+ if (this == &array)
+ return *this;
+
+ delete[] _storage;
+ _size = array._size;
+ _capacity = _size + 32;
+ _storage = new T[_capacity];
+ assert(_storage);
+ copy(array._storage, array._storage + _size, _storage);
+
+ return *this;
+ }
+
+ uint size() const {
+ return _size;
+ }
+
+ void clear() {
+ delete[] _storage;
+ _storage = 0;
+ _size = 0;
+ _capacity = 0;
+ }
+
+ bool empty() const {
+ return (_size == 0);
+ }
+
+ bool operator==(const Array<T> &other) const {
+ if (this == &other)
+ return true;
+ if (_size != other._size)
+ return false;
+ for (uint i = 0; i < _size; ++i) {
+ if (_storage[i] != other._storage[i])
+ return false;
+ }
+ return true;
+ }
+ bool operator!=(const Array<T> &other) const {
+ return !(*this == other);
+ }
+
+
+ iterator begin() {
+ return _storage;
+ }
+
+ iterator end() {
+ return _storage + _size;
+ }
+
+ const_iterator begin() const {
+ return _storage;
+ }
+
+ const_iterator end() const {
+ return _storage + _size;
+ }
+
+ void reserve(uint newCapacity) {
+ if (newCapacity <= _capacity)
+ return;
+
+ T *old_storage = _storage;
+ _capacity = newCapacity;
+ _storage = new T[newCapacity];
+ assert(_storage);
+
+ if (old_storage) {
+ // Copy old data
+ copy(old_storage, old_storage + _size, _storage);
+ delete[] old_storage;
+ }
+ }
+
+ void resize(uint newSize) {
+ reserve(newSize);
+ for (uint i = _size; i < newSize; ++i)
+ _storage[i] = T();
+ _size = newSize;
+ }
+
+protected:
+ static uint roundUpCapacity(uint capacity) {
+ // Round up capacity to the next power of 2;
+ // we use a minimal capacity of 8.
+ uint capa = 8;
+ while (capa < capacity)
+ capa <<= 1;
+ return capa;
+ }
+
+ /**
+ * Insert a range of elements coming from this or another array.
+ * Unlike std::vector::insert, this method does not accept
+ * arbitrary iterators, mainly because our iterator system is
+ * seriously limited and does not distinguish between input iterators,
+ * output iterators, forward iterators or random access iterators.
+ *
+ * So, we simply restrict to Array iterators. Extending this to arbitrary
+ * random access iterators would be trivial.
+ *
+ * Moreover, this method does not handle all cases of inserting a subrange
+ * of an array into itself; this is why it is private for now.
+ */
+ iterator insert_aux(iterator pos, const_iterator first, const_iterator last) {
+ assert(_storage <= pos && pos <= _storage + _size);
+ assert(first <= last);
+ const uint n = last - first;
+ if (n) {
+ const uint idx = pos - _storage;
+ T *newStorage = _storage;
+ if (_size + n > _capacity) {
+ // If there is not enough space, allocate more and
+ // copy old elements over.
+ uint newCapacity = roundUpCapacity(_size + n);
+ newStorage = new T[newCapacity];
+ assert(newStorage);
+ copy(_storage, _storage + idx, newStorage);
+ pos = newStorage + idx;
+ }
+
+ // Make room for the new elements by shifting back
+ // existing ones.
+ copy_backward(_storage + idx, _storage + _size, newStorage + _size + n);
+
+ // Insert the new elements.
+ copy(first, last, pos);
+
+ // Finally, update the internal state
+ if (newStorage != _storage) {
+ delete[] _storage;
+ _capacity = roundUpCapacity(_size + n);
+ _storage = newStorage;
+ }
+ _size += n;
+ }
+ return pos;
+ }
+
+};
+
+} // End of namespace Common
+
+#endif
diff --git a/engines/sword25/tools/swfdisplay/art-non-display1.cpp b/engines/sword25/tools/swfdisplay/art-non-display1.cpp
new file mode 100644
index 0000000000..b3d4457e71
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/art-non-display1.cpp
@@ -0,0 +1,488 @@
+#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>
+
+#define SAVING 1
+
+#define WIDTH 3000
+#define HEIGHT 3000
+#define BYTES_PER_PIXEL 3 /* 24 packed rgb bits */
+#define ROWSTRIDE (WIDTH*BYTES_PER_PIXEL)
+
+
+static unsigned char *render_path(void);
+
+#if SAVING
+static void save_buffer(unsigned char *buffer, const char *filename);
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <png.h>
+#include <stdio.h>
+
+static gboolean
+pixbuf_save_to_file(const GdkPixbuf *pixbuf, const char *file_name) {
+ FILE *handle;
+ unsigned char *buffer;
+ gboolean has_alpha;
+ int width, height, depth, rowstride;
+ guchar *pixels;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_text text[2];
+ int i;
+
+ g_return_val_if_fail(pixbuf != NULL, FALSE);
+ g_return_val_if_fail(file_name != NULL, FALSE);
+ g_return_val_if_fail(file_name[0] != '\0', FALSE);
+
+ handle = fopen(file_name, "wb");
+ if (handle == NULL) {
+ return FALSE;
+ }
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png_ptr == NULL) {
+ fclose(handle);
+ return FALSE;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ fclose(handle);
+ return FALSE;
+ }
+
+ if (setjmp(png_ptr->jmpbuf)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(handle);
+ return FALSE;
+ }
+
+ png_init_io(png_ptr, handle);
+
+ has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
+ width = gdk_pixbuf_get_width(pixbuf);
+ height = gdk_pixbuf_get_height(pixbuf);
+ depth = gdk_pixbuf_get_bits_per_sample(pixbuf);
+ pixels = gdk_pixbuf_get_pixels(pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride(pixbuf);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height,
+ depth, PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ /* Some text to go with the png image */
+ text[0].key = "Title";
+ text[0].text = (char *) file_name;
+ text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+ text[1].key = "Software";
+ text[1].text = "Nautilus Thumbnail";
+ text[1].compression = PNG_TEXT_COMPRESSION_NONE;
+ png_set_text(png_ptr, info_ptr, text, 2);
+
+ /* Write header data */
+ png_write_info(png_ptr, info_ptr);
+
+ /* if there is no alpha in the data, allocate buffer to expand into */
+ if (has_alpha) {
+ buffer = NULL;
+ } else {
+ buffer = g_malloc(4 * width);
+ }
+
+ /* pump the raster data into libpng, one scan line at a time */
+ for (i = 0; i < height; i++) {
+ if (has_alpha) {
+ png_bytep row_pointer = pixels;
+ png_write_row(png_ptr, row_pointer);
+ } else {
+ /* expand RGB to RGBA using an opaque alpha value */
+ int x;
+ unsigned char *buffer_ptr = buffer;
+ unsigned char *source_ptr = pixels;
+ for (x = 0; x < width; x++) {
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = 255;
+ }
+ png_write_row(png_ptr, (png_bytep) buffer);
+ }
+ pixels += rowstride;
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ g_free(buffer);
+
+ fclose(handle);
+ return TRUE;
+}
+
+static void
+save_buffer(unsigned char *buffer, const char *filename) {
+ GdkPixbuf *pixbuf;
+
+ g_type_init();
+
+ pixbuf = gdk_pixbuf_new_from_data(buffer,
+ GDK_COLORSPACE_RGB,
+#if BYTES_PER_PIXEL == 4
+ TRUE,
+#else
+ FALSE,
+#endif
+ 8,
+ WIDTH, HEIGHT,
+ ROWSTRIDE,
+ NULL, NULL);
+
+ pixbuf_save_to_file(pixbuf, filename);
+
+ gdk_pixbuf_unref(pixbuf);
+}
+
+#endif
+
+void
+art_rgb_fill_run1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, gint n) {
+ int i;
+
+ if (r == g && g == b && r == 255) {
+ memset(buf, g, n + n + n + n);
+ } else {
+ art_u32 *alt = (art_u32 *)buf;
+ //art_u32 color = (r << 24) | (g << 16) | (b << 8) | 0xff;
+ art_u32 color = (r << 0) | (g << 8) | (b << 16) | (0xff << 24);
+ for (i = 0; i < n; i++)
+ *alt++ = color;
+ }
+}
+
+void
+art_rgb_run_alpha1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n) {
+ int i;
+ int v;
+
+ for (i = 0; i < n; i++) {
+ v = *buf;
+ *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((alpha - v) * alpha + 0x80) >> 8);
+ }
+}
+
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void
+art_rgb_svp_alpha_callback1(void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ } else {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void
+art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ }
+ } else {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+void
+art_rgb_svp_alpha1(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma) {
+ ArtRgbSVPAlphaData data;
+ int r, g, b, alpha;
+ int i;
+ int a, da;
+
+ alpha = rgba >> 24;
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = rgba & 0xff;
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++) {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
+ else
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
+}
+
+static int art_vpath_len (ArtVpath * a) {
+ int i;
+
+ for (i = 0; a[i].code != ART_END; i++);
+ return i;
+}
+
+ArtVpath *art_vpath_reverse(ArtVpath * a) {
+ ArtVpath *dest;
+ ArtVpath it;
+ int len;
+ int state = 0;
+ int i;
+
+ len = art_vpath_len(a);
+ dest = g_malloc((len + 1) * sizeof(ArtVpath));
+
+ for (i = 0; i < len; i++) {
+ it = a[len - i - 1];
+ if (state) {
+ it.code = ART_LINETO;
+ } else {
+ it.code = ART_MOVETO_OPEN;
+ state = 1;
+ }
+ if (a[len - i - 1].code == ART_MOVETO ||
+ a[len - i - 1].code == ART_MOVETO_OPEN) {
+ state = 0;
+ }
+ dest[i] = it;
+ }
+ dest[len] = a[len];
+
+ return dest;
+}
+
+ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
+ ArtVpath *dest;
+
+ dest = art_vpath_reverse(a);
+ art_free(a);
+
+ return dest;
+}
+
+void art_svp_make_convex (ArtSVP *svp) {
+ int i;
+
+ if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
+ for (i = 0; i < svp->n_segs; i++) {
+ svp->segs[i].dir = !svp->segs[i].dir;
+ }
+ }
+}
+
+
+void drawBez(ArtBpath *bez, art_u8 *buffer, double scaleX, double scaleY, double penWidth, unsigned int color) {
+ ArtVpath *vec = NULL;
+ ArtSVP *svp = NULL;
+
+ vec = art_bez_path_to_vec(bez, 0.5);
+
+ if (scaleX != 1.0 || scaleY != 1.0) {
+ ArtVpath *vec1;
+ double matrix[6];
+
+ matrix[0] = scaleX;
+ matrix[1] = 0;
+ matrix[2] = 0;
+ matrix[3] = scaleY;
+ matrix[4] = matrix[5] = 0;
+
+ vec1 = art_vpath_affine_transform(vec, matrix);
+ art_free(vec);
+
+ vec = vec1;
+ }
+
+ if (penWidth != -1) {
+ svp = art_svp_vpath_stroke(vec, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
+ } else {
+ svp = art_svp_from_vpath(vec);
+ }
+
+#if BYTES_PER_PIXEL == 4
+ art_rgb_svp_alpha1(svp, 0, 0, WIDTH, HEIGHT, color, buffer, ROWSTRIDE, NULL);
+#else
+ color <<= 8;
+ color |= 0xff;
+ art_rgb_svp_alpha(svp, 0, 0, WIDTH, HEIGHT, color, buffer, ROWSTRIDE, NULL);
+#endif
+ art_free(svp);
+
+ art_free(vec);
+}
+
+static unsigned char *render_path() {
+ art_u8 *buffer = NULL;
+
+ ArtBpath *bez = NULL;
+
+ buffer = art_new(art_u8, WIDTH * HEIGHT * BYTES_PER_PIXEL);
+#if BYTES_PER_PIXEL == 4
+ memset(buffer, 0, WIDTH*HEIGHT*BYTES_PER_PIXEL);
+#else
+ memset(buffer, 0xff, WIDTH*HEIGHT*BYTES_PER_PIXEL);
+#endif
+ bez = art_new(ArtBpath, 100);
+
+#include "image.c"
+
+ art_free(bez);
+
+ return (unsigned char *) buffer;
+}
+
+int main(int argc, char *argv[]) {
+ unsigned char *buffer;
+
+ buffer = render_path();
+
+#if SAVING
+ save_buffer(buffer, "foo.png");
+#endif
+
+ return 0;
+}
diff --git a/engines/sword25/tools/swfdisplay/art.cpp b/engines/sword25/tools/swfdisplay/art.cpp
new file mode 100644
index 0000000000..e88d6e55c4
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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 = (const ArtSVPSeg *)s1;
+ const ArtSVPSeg *seg2 = (const 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/tools/swfdisplay/art.h b/engines/sword25/tools/swfdisplay/art.h
new file mode 100644
index 0000000000..aa66036287
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/art.h
@@ -0,0 +1,182 @@
+/* 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__
+
+#define DEBUG 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+typedef unsigned char byte;
+typedef unsigned short uint16;
+typedef unsigned int uint32;
+typedef unsigned int uint;
+typedef short int16;
+typedef int int32;
+
+typedef byte art_u8;
+typedef uint16 art_u16;
+typedef uint32 art_u32;
+
+#define FLIP_NONE 0
+
+#define BS_ASSERT(x) assert(x)
+#define error(x) assert(0)
+#define BS_LOG_ERRORLN(x) assert(0)
+
+#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))
+
+/* 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/tools/swfdisplay/art_svp_intersect.cpp b/engines/sword25/tools/swfdisplay/art_svp_intersect.cpp
new file mode 100644
index 0000000000..56164996b6
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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 */
+ 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. */
+ 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 */
+ 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 */
+ 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) {
+ 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 */
+ 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/tools/swfdisplay/art_svp_intersect.h b/engines/sword25/tools/swfdisplay/art_svp_intersect.h
new file mode 100644
index 0000000000..5e70ac6d79
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/art_svp_render_aa.cpp b/engines/sword25/tools/swfdisplay/art_svp_render_aa.cpp
new file mode 100644
index 0000000000..616977fb5e
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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 = (int)floor(x_min);
+ ix_max = (int)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 += (int)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/tools/swfdisplay/art_svp_render_aa.h b/engines/sword25/tools/swfdisplay/art_svp_render_aa.h
new file mode 100644
index 0000000000..ac4f538805
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/art_svp_vpath.cpp b/engines/sword25/tools/swfdisplay/art_svp_vpath.cpp
new file mode 100644
index 0000000000..e689e21e84
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/art_svp_vpath.h b/engines/sword25/tools/swfdisplay/art_svp_vpath.h
new file mode 100644
index 0000000000..d0f5f51b28
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/art_svp_vpath_stroke.cpp b/engines/sword25/tools/swfdisplay/art_svp_vpath_stroke.cpp
new file mode 100644
index 0000000000..66cf3b783c
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/art_svp_vpath_stroke.h b/engines/sword25/tools/swfdisplay/art_svp_vpath_stroke.h
new file mode 100644
index 0000000000..d16e324a3a
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/art_vpath_bpath.cpp b/engines/sword25/tools/swfdisplay/art_vpath_bpath.cpp
new file mode 100644
index 0000000000..82f1668ff7
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/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/tools/swfdisplay/bezierrender.cpp b/engines/sword25/tools/swfdisplay/bezierrender.cpp
new file mode 100644
index 0000000000..ca0051fbe3
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/bezierrender.cpp
@@ -0,0 +1,584 @@
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_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>
+
+#define SAVING 1
+
+#define WIDTH 1500
+#define HEIGHT 1500
+#define BYTES_PER_PIXEL 4 /* 24 packed rgb bits */
+#define ROWSTRIDE (WIDTH*BYTES_PER_PIXEL)
+
+
+static unsigned char *render_path (void);
+
+#if SAVING
+static void save_buffer (unsigned char *buffer, const char *filename);
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <png.h>
+#include <stdio.h>
+
+/**
+ * pixbuf_save_to_file:
+ * @pixbuf: pixel buffer to save.
+ * @filename: file name to save the pixel buffer into.
+ *
+ * Saves the pixel buffer in the given filename under
+ * the .png format. Returns TRUE is succesful and FALSE
+ * otherwise.
+ *
+ * Copyright is to Iain Holmes and Eazel, inc for this function.
+ * It was stolen from nautilus-gdk-pixbuf-extensions.c but
+ * was originally coming from gnome-iconedit by Iain Holmes
+ * It was hacked by Ramiro Estrugo for Eazel, inc.
+ */
+static gboolean
+pixbuf_save_to_file (const GdkPixbuf *pixbuf, const char *file_name)
+{
+ FILE *handle;
+ unsigned char *buffer;
+ gboolean has_alpha;
+ int width, height, depth, rowstride;
+ guchar *pixels;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_text text[2];
+ int i;
+
+ g_return_val_if_fail (pixbuf != NULL, FALSE);
+ g_return_val_if_fail (file_name != NULL, FALSE);
+ g_return_val_if_fail (file_name[0] != '\0', FALSE);
+
+ handle = fopen (file_name, "wb");
+ if (handle == NULL) {
+ return FALSE;
+ }
+
+ png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png_ptr == NULL) {
+ fclose (handle);
+ return FALSE;
+ }
+
+ info_ptr = png_create_info_struct (png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
+ fclose (handle);
+ return FALSE;
+ }
+
+ if (setjmp (png_ptr->jmpbuf)) {
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+ fclose (handle);
+ return FALSE;
+ }
+
+ png_init_io (png_ptr, handle);
+
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ depth = gdk_pixbuf_get_bits_per_sample (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+
+ png_set_IHDR (png_ptr, info_ptr, width, height,
+ depth, PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ /* Some text to go with the png image */
+ text[0].key = "Title";
+ text[0].text = (char *) file_name;
+ text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+ text[1].key = "Software";
+ text[1].text = "Nautilus Thumbnail";
+ text[1].compression = PNG_TEXT_COMPRESSION_NONE;
+ png_set_text (png_ptr, info_ptr, text, 2);
+
+ /* Write header data */
+ png_write_info (png_ptr, info_ptr);
+
+ /* if there is no alpha in the data, allocate buffer to expand into */
+ if (has_alpha) {
+ buffer = NULL;
+ } else {
+ buffer = (unsigned char *)g_malloc(4 * width);
+ }
+
+ /* pump the raster data into libpng, one scan line at a time */
+ for (i = 0; i < height; i++) {
+ if (has_alpha) {
+ png_bytep row_pointer = pixels;
+ png_write_row (png_ptr, row_pointer);
+ } else {
+ /* expand RGB to RGBA using an opaque alpha value */
+ int x;
+ unsigned char *buffer_ptr = buffer;
+ unsigned char *source_ptr = pixels;
+ for (x = 0; x < width; x++) {
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = 255;
+ }
+ png_write_row (png_ptr, (png_bytep) buffer);
+ }
+ pixels += rowstride;
+ }
+
+ png_write_end (png_ptr, info_ptr);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+
+ g_free (buffer);
+
+ fclose (handle);
+ return TRUE;
+}
+
+static void
+save_buffer (unsigned char *buffer, const char *filename)
+{
+ GdkPixbuf *pixbuf;
+
+ g_type_init();
+
+ pixbuf = gdk_pixbuf_new_from_data (buffer,
+ GDK_COLORSPACE_RGB,
+ TRUE, 8,
+ WIDTH, HEIGHT,
+ ROWSTRIDE,
+ NULL, NULL);
+
+ pixbuf_save_to_file (pixbuf, filename);
+
+ gdk_pixbuf_unref (pixbuf);
+}
+
+#endif
+
+void
+art_rgb_fill_run1 (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, gint n)
+{
+ int i;
+
+ if (r == g && g == b && r == 255)
+ {
+ memset (buf, g, n + n + n + n);
+ }
+ else
+ {
+ art_u32 *alt = (art_u32 *)buf;
+ art_u32 color = (r << 24) | (g << 16) | (b << 8) | 0xff;
+ for (i = 0; i < n; i++)
+ *alt++ = color;
+ }
+}
+
+/**
+ * art_rgb_run_alpha: Render semitransparent color over RGB buffer.
+ * @buf: Buffer for rendering.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @alpha: Alpha, range 0..256.
+ * @n: Number of RGB triples to render.
+ *
+ * Renders a sequential run of solid (@r, @g, @b) color over @buf with
+ * opacity @alpha.
+ **/
+void
+art_rgb_run_alpha1 (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+ int i;
+ int v;
+
+ for (i = 0; i < n; i++)
+ {
+ v = *buf;
+ *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+ v = *buf + alpha;
+ if (v > 255)
+ v = 255;
+ *buf++ = v;
+ }
+}
+
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void
+art_rgb_svp_alpha_callback1 (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1 (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1 (linebuf + (run_x0 - x0) * 4,
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1 (linebuf + (run_x1 - x0) * 4,
+ r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ else
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1 (linebuf,
+ r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void
+art_rgb_svp_alpha_opaque_callback1 (void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run1 (linebuf,
+ r, g, b,
+ run_x1 - x0);
+ else
+ art_rgb_run_alpha1 (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+ }
+
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run1 (linebuf + (run_x0 - x0) * 4,
+ r, g, b,
+ run_x1 - run_x0);
+ else
+ art_rgb_run_alpha1 (linebuf + (run_x0 - x0) * 4,
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run1 (linebuf + (run_x1 - x0) * 4,
+ r, g, b,
+ x1 - run_x1);
+ else
+ art_rgb_run_alpha1 (linebuf + (run_x1 - x0) * 4,
+ r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ }
+ else
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgb_fill_run1 (linebuf,
+ r, g, b,
+ x1 - x0);
+ else
+ art_rgb_run_alpha1 (linebuf,
+ r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+/**
+ * art_rgb_svp_alpha: Alpha-composite sorted vector path over RGB buffer.
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @rgba: Color in 0xRRGGBBAA format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @rgba argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @rgba composited over them (ie,
+ * are replaced by the red, green, blue components of @rgba if the alpha
+ * component is 0xff). Pixels of intermediate coverage are interpolated
+ * according to the rule in @alphagamma, or default to linear if
+ * @alphagamma is NULL.
+ **/
+void
+art_rgb_svp_alpha1 (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma)
+{
+ ArtRgbSVPAlphaData data;
+ int r, g, b, alpha;
+ int i;
+ int a, da;
+
+ r = rgba >> 24;
+ g = (rgba >> 16) & 0xff;
+ b = (rgba >> 8) & 0xff;
+ alpha = rgba & 0xff;
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++)
+ {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1,
+ &data);
+ else
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
+}
+
+
+
+static unsigned char *
+render_path ()
+{
+ art_u8 *buffer = NULL;
+ art_u32 color1 = (0xFF << 24) | (0x00 <<16) | (0x00<<8) | (0xFF) ; /* RRGGBBAA */
+ art_u32 color2 = (0x00 << 24) | (0x00 <<16) | (0xFF<<8) | (0xFF) ; /* RRGGBBAA */
+
+ ArtBpath *bez1 = NULL;
+ ArtVpath *vec = NULL;
+ ArtSVP *svp1 = NULL;
+ ArtSVP *svp2 = NULL;
+
+ bez1 = art_new (ArtBpath, 30);
+ bez1[0].code = ART_MOVETO;
+ bez1[0].x3 = 352.000000; bez1[0].y3 = 59.000000;
+ bez1[1].code = ART_CURVETO;
+ bez1[1].x1 = 396.000000; bez1[1].y1 = 41.000000;
+ bez1[1].x2 = 434.333333; bez1[1].y2 = 57.666667;
+ bez1[1].x3 = 467.000000; bez1[1].y3 = 109.000000;
+ bez1[2].code = ART_CURVETO;
+ bez1[2].x1 = 481.666667; bez1[2].y1 = 137.666667;
+ bez1[2].x2 = 487.666667; bez1[2].y2 = 172.000000;
+ bez1[2].x3 = 485.000000; bez1[2].y3 = 212.000000;
+ bez1[3].code = ART_LINETO;
+ bez1[3].x3 = 485.000000; bez1[3].y3 = 216.000000;
+ bez1[4].code = ART_LINETO;
+ bez1[4].x3 = 483.000000; bez1[4].y3 = 231.000000;
+ bez1[5].code = ART_LINETO;
+ bez1[5].x3 = 468.000000; bez1[5].y3 = 291.000000;
+ bez1[6].code = ART_LINETO;
+ bez1[6].x3 = 466.000000; bez1[6].y3 = 299.000000;
+ bez1[7].code = ART_LINETO;
+ bez1[7].x3 = 446.000000; bez1[7].y3 = 349.000000;
+ bez1[8].code = ART_LINETO;
+ bez1[8].x3 = 445.000000; bez1[8].y3 = 351.000000;
+ bez1[9].code = ART_CURVETO;
+ bez1[9].x1 = 442.333333; bez1[9].y1 = 376.333333;
+ bez1[9].x2 = 450.000000; bez1[9].y2 = 394.000000;
+ bez1[9].x3 = 468.000000; bez1[9].y3 = 404.000000;
+ bez1[10].code = ART_CURVETO;
+ bez1[10].x1 = 373.333333; bez1[10].y1 = 428.666667;
+ bez1[10].x2 = 294.666667; bez1[10].y2 = 426.666667;
+ bez1[10].x3 = 232.000000; bez1[10].y3 = 398.000000;
+ bez1[11].code = ART_CURVETO;
+ bez1[11].x1 = 249.333333; bez1[11].y1 = 380.666667;
+ bez1[11].x2 = 257.666667; bez1[11].y2 = 365.000000;
+ bez1[11].x3 = 257.000000; bez1[11].y3 = 351.000000;
+ bez1[12].code = ART_LINETO;
+ bez1[12].x3 = 255.000000; bez1[12].y3 = 345.000000;
+ bez1[13].code = ART_CURVETO;
+ bez1[13].x1 = 244.333333; bez1[13].y1 = 326.333333;
+ bez1[13].x2 = 236.000000; bez1[13].y2 = 307.666667;
+ bez1[13].x3 = 230.000000; bez1[13].y3 = 289.000000;
+ bez1[14].code = ART_CURVETO;
+ bez1[14].x1 = 223.333333; bez1[14].y1 = 270.333333;
+ bez1[14].x2 = 219.000000; bez1[14].y2 = 252.000000;
+ bez1[14].x3 = 217.000000; bez1[14].y3 = 234.000000;
+ bez1[15].code = ART_LINETO;
+ bez1[15].x3 = 216.000000; bez1[15].y3 = 233.000000;
+ bez1[16].code = ART_LINETO;
+ bez1[16].x3 = 215.000000; bez1[16].y3 = 229.000000;
+ bez1[17].code = ART_CURVETO;
+ bez1[17].x1 = 201.666667; bez1[17].y1 = 187.666667;
+ bez1[17].x2 = 210.666667; bez1[17].y2 = 144.000000;
+ bez1[17].x3 = 242.000000; bez1[17].y3 = 98.000000;
+ bez1[18].code = ART_CURVETO;
+ bez1[18].x1 = 274.000000; bez1[18].y1 = 56.000000;
+ bez1[18].x2 = 310.333333; bez1[18].y2 = 43.333333;
+ bez1[18].x3 = 351.000000; bez1[18].y3 = 60.000000;
+ bez1[19].code = ART_LINETO;
+ bez1[19].x3 = 352.000000; bez1[19].y3 = 59.000000;
+ bez1[20].code = ART_END;
+
+ vec = art_bez_path_to_vec (bez1, 0.5);
+ svp1 = art_svp_from_vpath (vec);
+
+ int penWidth = 5;
+ svp2 = art_svp_vpath_stroke(vec, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
+
+
+ buffer = art_new (art_u8, WIDTH*HEIGHT*BYTES_PER_PIXEL);
+ memset(buffer, 0, WIDTH*HEIGHT*BYTES_PER_PIXEL);
+ art_rgb_run_alpha (buffer, 0, 0, 0, 0, WIDTH*HEIGHT);
+ art_rgb_svp_alpha1 (svp1, 0, 0, WIDTH, HEIGHT, color1, buffer, ROWSTRIDE, NULL);
+ art_rgb_svp_alpha1 (svp2, 0, 0, WIDTH, HEIGHT, color2, buffer, ROWSTRIDE, NULL);
+
+ art_free(svp2);
+ art_free(svp1);
+ art_free(vec);
+ art_free(bez1);
+
+ return (unsigned char *) buffer;
+}
+
+int main (int argc, char *argv[])
+{
+ unsigned char *buffer;
+
+ buffer = render_path ();
+
+#if SAVING
+ save_buffer (buffer, "foo.png");
+#endif
+
+ return 0;
+}
+
+
+
+
+
+
+
diff --git a/engines/sword25/tools/swfdisplay/func.h b/engines/sword25/tools/swfdisplay/func.h
new file mode 100644
index 0000000000..251c1b4e7c
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/func.h
@@ -0,0 +1,542 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/common/func.h $
+ * $Id: func.h 46129 2009-11-24 22:10:34Z fingolfin $
+ */
+
+#ifndef COMMON_FUNC_H
+#define COMMON_FUNC_H
+
+namespace Common {
+
+/**
+ * Generic unary function.
+ */
+template<class Arg, class Result>
+struct UnaryFunction {
+ typedef Arg ArgumenType;
+ typedef Result ResultType;
+};
+
+/**
+ * Generic binary function.
+ */
+template<class Arg1, class Arg2, class Result>
+struct BinaryFunction {
+ typedef Arg1 FirstArgumentType;
+ typedef Arg2 SecondArgumentType;
+ typedef Result ResultType;
+};
+
+/**
+ * Predicate to check for equallity of two data elements.
+ */
+template<class T>
+struct EqualTo : public BinaryFunction<T, T, bool> {
+ bool operator()(const T &x, const T &y) const { return x == y; }
+};
+
+/**
+ * Predicate to check for x being less than y.
+ */
+template<class T>
+struct Less : public BinaryFunction<T, T, bool> {
+ bool operator()(const T &x, const T &y) const { return x < y; }
+};
+
+/**
+ * Predicate to check for x being greater than y.
+ */
+template<class T>
+struct Greater : public BinaryFunction<T, T, bool> {
+ bool operator()(const T &x, const T &y) const { return x > y; }
+};
+
+template<class Op>
+class Binder1st : public UnaryFunction<typename Op::SecondArgumentType, typename Op::ResultType> {
+private:
+ Op _op;
+ typename Op::FirstArgumentType _arg1;
+public:
+ Binder1st(const Op &op, typename Op::FirstArgumentType arg1) : _op(op), _arg1(arg1) {}
+
+ typename Op::ResultType operator()(typename Op::SecondArgumentType v) const {
+ return _op(_arg1, v);
+ }
+};
+
+/**
+ * Transforms a binary function object into an unary function object.
+ * To achieve that the first parameter is bound to the passed value t.
+ */
+template<class Op>
+inline Binder1st<Op> bind1st(const Op &op, typename Op::FirstArgumentType t) {
+ return Binder1st<Op>(op, t);
+}
+
+template<class Op>
+class Binder2nd : public UnaryFunction<typename Op::FirstArgumentType, typename Op::ResultType> {
+private:
+ Op _op;
+ typename Op::SecondArgumentType _arg2;
+public:
+ Binder2nd(const Op &op, typename Op::SecondArgumentType arg2) : _op(op), _arg2(arg2) {}
+
+ typename Op::ResultType operator()(typename Op::FirstArgumentType v) const {
+ return _op(v, _arg2);
+ }
+};
+
+/**
+ * Transforms a binary function object into an unary function object.
+ * To achieve that the first parameter is bound to the passed value t.
+ */
+template<class Op>
+inline Binder2nd<Op> bind2nd(const Op &op, typename Op::SecondArgumentType t) {
+ return Binder2nd<Op>(op, t);
+}
+
+template<class Arg, class Result>
+class PointerToUnaryFunc : public UnaryFunction<Arg, Result> {
+private:
+ Result (*_func)(Arg);
+public:
+ typedef Result (*FuncType)(Arg);
+
+ PointerToUnaryFunc(const FuncType &func) : _func(func) {}
+ Result operator()(Arg v) const {
+ return _func(v);
+ }
+};
+
+template<class Arg1, class Arg2, class Result>
+class PointerToBinaryFunc : public BinaryFunction<Arg1, Arg2, Result> {
+private:
+ Result (*_func)(Arg1, Arg2);
+public:
+ typedef Result (*FuncType)(Arg1, Arg2);
+
+ PointerToBinaryFunc(const FuncType &func) : _func(func) {}
+ Result operator()(Arg1 v1, Arg2 v2) const {
+ return _func(v1, v2);
+ }
+};
+
+/**
+ * Creates an unary function object from a function pointer.
+ */
+template<class Arg, class Result>
+inline PointerToUnaryFunc<Arg, Result> ptr_fun(Result (*func)(Arg)) {
+ return PointerToUnaryFunc<Arg, Result>(func);
+}
+
+/**
+ * Creates an binary function object from a function pointer.
+ */
+template<class Arg1, class Arg2, class Result>
+inline PointerToBinaryFunc<Arg1, Arg2, Result> ptr_fun(Result (*func)(Arg1, Arg2)) {
+ return PointerToBinaryFunc<Arg1, Arg2, Result>(func);
+}
+
+template<class Result, class T>
+class MemFunc0 : public UnaryFunction<T *, Result> {
+private:
+ Result (T::*_func)();
+public:
+ typedef Result (T::*FuncType)();
+
+ MemFunc0(const FuncType &func) : _func(func) {}
+ Result operator()(T *v) const {
+ return (v->*_func)();
+ }
+};
+
+template<class Result, class T>
+class ConstMemFunc0 : public UnaryFunction<T *, Result> {
+private:
+ Result (T::*_func)() const;
+public:
+ typedef Result (T::*FuncType)() const;
+
+ ConstMemFunc0(const FuncType &func) : _func(func) {}
+ Result operator()(const T *v) const {
+ return (v->*_func)();
+ }
+};
+
+template<class Result, class Arg, class T>
+class MemFunc1 : public BinaryFunction<T *, Arg, Result> {
+private:
+ Result (T::*_func)(Arg);
+public:
+ typedef Result (T::*FuncType)(Arg);
+
+ MemFunc1(const FuncType &func) : _func(func) {}
+ Result operator()(T *v1, Arg v2) const {
+ return (v1->*_func)(v2);
+ }
+};
+
+template<class Result, class Arg, class T>
+class ConstMemFunc1 : public BinaryFunction<T *, Arg, Result> {
+private:
+ Result (T::*_func)(Arg) const;
+public:
+ typedef Result (T::*FuncType)(Arg) const;
+
+ ConstMemFunc1(const FuncType &func) : _func(func) {}
+ Result operator()(const T *v1, Arg v2) const {
+ return (v1->*_func)(v2);
+ }
+};
+
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ */
+template<class Result, class T>
+inline MemFunc0<Result, T> mem_fun(Result (T::*f)()) {
+ return MemFunc0<Result, T>(f);
+}
+
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ */
+template<class Result, class T>
+inline ConstMemFunc0<Result, T> mem_fun(Result (T::*f)() const) {
+ return ConstMemFunc0<Result, T>(f);
+}
+
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ * The second one is the parameter passed to the member function.
+ */
+template<class Result, class Arg, class T>
+inline MemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg)) {
+ return MemFunc1<Result, Arg, T>(f);
+}
+
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the 'this' pointer to
+ * be used for the function call.
+ * The second one is the parameter passed to the member function.
+ */
+template<class Result, class Arg, class T>
+inline ConstMemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg) const) {
+ return ConstMemFunc1<Result, Arg, T>(f);
+}
+
+template<class Result, class T>
+class MemFuncRef0 : public UnaryFunction<T &, Result> {
+private:
+ Result (T::*_func)();
+public:
+ typedef Result (T::*FuncType)();
+
+ MemFuncRef0(const FuncType &func) : _func(func) {}
+ Result operator()(T &v) const {
+ return (v.*_func)();
+ }
+};
+
+template<class Result, class T>
+class ConstMemFuncRef0 : public UnaryFunction<T &, Result> {
+private:
+ Result (T::*_func)() const;
+public:
+ typedef Result (T::*FuncType)() const;
+
+ ConstMemFuncRef0(const FuncType &func) : _func(func) {}
+ Result operator()(const T &v) const {
+ return (v.*_func)();
+ }
+};
+
+template<class Result, class Arg, class T>
+class MemFuncRef1 : public BinaryFunction<T &, Arg, Result> {
+private:
+ Result (T::*_func)(Arg);
+public:
+ typedef Result (T::*FuncType)(Arg);
+
+ MemFuncRef1(const FuncType &func) : _func(func) {}
+ Result operator()(T &v1, Arg v2) const {
+ return (v1.*_func)(v2);
+ }
+};
+
+template<class Result, class Arg, class T>
+class ConstMemFuncRef1 : public BinaryFunction<T &, Arg, Result> {
+private:
+ Result (T::*_func)(Arg) const;
+public:
+ typedef Result (T::*FuncType)(Arg) const;
+
+ ConstMemFuncRef1(const FuncType &func) : _func(func) {}
+ Result operator()(const T &v1, Arg v2) const {
+ return (v1.*_func)(v2);
+ }
+};
+
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ */
+template<class Result, class T>
+inline MemFuncRef0<Result, T> mem_fun_ref(Result (T::*f)()) {
+ return MemFuncRef0<Result, T>(f);
+}
+
+/**
+ * Creates a unary function object from a class member function pointer.
+ * The parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ */
+template<class Result, class T>
+inline ConstMemFuncRef0<Result, T> mem_fun_Ref(Result (T::*f)() const) {
+ return ConstMemFuncRef0<Result, T>(f);
+}
+
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ * The second one is the parameter passed to the member function.
+ */
+template<class Result, class Arg, class T>
+inline MemFuncRef1<Result, Arg, T> mem_fun_ref(Result (T::*f)(Arg)) {
+ return MemFuncRef1<Result, Arg, T>(f);
+}
+
+/**
+ * Creates a binary function object from a class member function pointer.
+ * The first parameter passed to the function object is the object instance to
+ * be used for the function call. Note unlike mem_fun, it takes a reference
+ * as parameter.
+ * The second one is the parameter passed to the member function.
+ */
+template<class Result, class Arg, class T>
+inline ConstMemFuncRef1<Result, Arg, T> mem_fun_ref(Result (T::*f)(Arg) const) {
+ return ConstMemFuncRef1<Result, Arg, T>(f);
+}
+
+// functor code
+
+/**
+ * Generic functor object for function objects without parameters.
+ *
+ * @see Functor1
+ */
+template<class Res>
+struct Functor0 {
+ virtual ~Functor0() {}
+
+ virtual bool isValid() const = 0;
+ virtual Res operator()() const = 0;
+};
+
+/**
+ * Functor object for a class member function without parameter.
+ *
+ * Example creation:
+ *
+ * Foo bar;
+ * Functor0Mem<void, Foo> myFunctor(&bar, &Foo::myFunc);
+ *
+ * Example usage:
+ *
+ * myFunctor();
+ */
+template<class Res, class T>
+class Functor0Mem : public Functor0<Res> {
+public:
+ typedef Res (T::*FuncType)();
+
+ Functor0Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()() const {
+ return (_t->*_func)();
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+/**
+ * Generic functor object for unary function objects.
+ *
+ * A typical usage for an unary function object is for executing opcodes
+ * in a script interpreter. To achieve that one can create an Common::Array
+ * object with 'Functor1<Arg, Res> *' as type. Now after the right engine version
+ * has been determined and the opcode table to use is found one could easily
+ * add the opcode implementations like this:
+ *
+ * Common::Array<Functor1<ScriptState, void> *> opcodeTable;
+ * opcodeTable[0] = new Functor1Mem<ScriptState, void, MyEngine_v1>(&myEngine, &MyEngine_v1::o1_foo);
+ * opcodeTable[1] = new Functor1Mem<ScriptState, void, MyEngine_v2>(&myEngine, &MyEngine_v2::o2_foo);
+ * // unimplemented/unused opcode
+ * opcodeTable[2] = 0;
+ * etc.
+ *
+ * This makes it easy to add member functions of different classes as
+ * opcode functions to the function table. Since with the generic
+ * Functor1<ScriptState, void> object the only requirement for an
+ * function to be used is 'ScriptState' as argument and 'void' as return
+ * value.
+ *
+ * Now for calling the opcodes one has simple to do:
+ * if (opcodeTable[opcodeNum] && opcodeTable[opcodeNum]->isValid())
+ * (*opcodeTable[opcodeNum])(scriptState);
+ * else
+ * warning("Unimplemented opcode %d", opcodeNum);
+ *
+ * If you want to see an real world example check the kyra engine.
+ * Files: engines/kyra/script.cpp and .h and engines/kyra/script_*.cpp
+ * are interesting for that matter.
+ */
+template<class Arg, class Res>
+struct Functor1 : public Common::UnaryFunction<Arg, Res> {
+ virtual ~Functor1() {}
+
+ virtual bool isValid() const = 0;
+ virtual Res operator()(Arg) const = 0;
+};
+
+/**
+ * Functor object for an unary class member function.
+ * Usage is like with Functor0Mem. The resulting functor object
+ * will take one parameter though.
+ *
+ * @see Functor0Mem
+ */
+template<class Arg, class Res, class T>
+class Functor1Mem : public Functor1<Arg, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg);
+
+ Functor1Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg v1) const {
+ return (_t->*_func)(v1);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+/**
+ * Generic functor object for binary function objects.
+ *
+ * @see Functor1
+ */
+template<class Arg1, class Arg2, class Res>
+struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
+ virtual ~Functor2() {}
+
+ virtual bool isValid() const = 0;
+ virtual Res operator()(Arg1, Arg2) const = 0;
+};
+
+/**
+ * Functor object for a binary function.
+ *
+ * @see Functor2Mem
+ */
+template<class Arg1, class Arg2, class Res>
+class Functor2Fun : public Functor2<Arg1, Arg2, Res> {
+public:
+ typedef Res (*FuncType)(Arg1, Arg2);
+
+ Functor2Fun(const FuncType func) : _func(func) {}
+
+ bool isValid() const { return _func != 0; }
+ Res operator()(Arg1 v1, Arg2 v2) const {
+ return (*_func)(v1, v2);
+ }
+private:
+ const FuncType _func;
+};
+
+/**
+ * Functor object for a binary class member function.
+ * Usage is like with Functor0Mem. The resulting functor object
+ * will take two parameter though.
+ *
+ * @see Functor0Mem
+ */
+template<class Arg1, class Arg2, class Res, class T>
+class Functor2Mem : public Functor2<Arg1, Arg2, Res> {
+public:
+ typedef Res (T::*FuncType)(Arg1, Arg2);
+
+ Functor2Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
+
+ bool isValid() const { return _func != 0 && _t != 0; }
+ Res operator()(Arg1 v1, Arg2 v2) const {
+ return (_t->*_func)(v1, v2);
+ }
+private:
+ mutable T *_t;
+ const FuncType _func;
+};
+
+/**
+ * Base template for hash functor objects, used by HashMap.
+ * This needs to be specialized for every type that you need to hash.
+ */
+template<typename T> struct Hash;
+
+
+#define GENERATE_TRIVIAL_HASH_FUNCTOR(T) \
+ template<> struct Hash<T> : public UnaryFunction<T, uint> { \
+ uint operator()(T val) const { return (uint)val; } \
+ }
+
+GENERATE_TRIVIAL_HASH_FUNCTOR(bool);
+GENERATE_TRIVIAL_HASH_FUNCTOR(char);
+GENERATE_TRIVIAL_HASH_FUNCTOR(signed char);
+GENERATE_TRIVIAL_HASH_FUNCTOR(unsigned char);
+GENERATE_TRIVIAL_HASH_FUNCTOR(short);
+GENERATE_TRIVIAL_HASH_FUNCTOR(int);
+GENERATE_TRIVIAL_HASH_FUNCTOR(long);
+GENERATE_TRIVIAL_HASH_FUNCTOR(unsigned short);
+GENERATE_TRIVIAL_HASH_FUNCTOR(unsigned int);
+GENERATE_TRIVIAL_HASH_FUNCTOR(unsigned long);
+
+#undef GENERATE_TRIVIAL_HASH_FUNCTOR
+
+} // End of namespace Common
+
+#endif
+
diff --git a/engines/sword25/tools/swfdisplay/rect.h b/engines/sword25/tools/swfdisplay/rect.h
new file mode 100644
index 0000000000..365dff09be
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/rect.h
@@ -0,0 +1,223 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/common/rect.h $
+ * $Id: rect.h 52249 2010-08-21 08:17:06Z dreammaster $
+ *
+ */
+
+#ifndef COMMON_RECT_H
+#define COMMON_RECT_H
+
+namespace Common {
+
+/**
+ * Simple class for handling both 2D position and size.
+ */
+struct Point {
+ int16 x; ///< The horizontal part of the point
+ int16 y; ///< The vertical part of the point
+
+ Point() : x(0), y(0) {}
+ Point(int16 x1, int16 y1) : x(x1), y(y1) {}
+ bool operator==(const Point &p) const { return x == p.x && y == p.y; }
+ bool operator!=(const Point &p) const { return x != p.x || y != p.y; }
+
+ /**
+ * Return the square of the distance between this point and the point p.
+ *
+ * @param p the other point
+ * @return the distance between this and p
+ */
+};
+
+/**
+ * Simple class for handling a rectangular zone.
+ *
+ * Note: This implementation is built around the assumption that (top,left) is
+ * part of the rectangle, but (bottom,right) is not. This is reflected in
+ * various methods, including contains(), intersects() and others.
+ *
+ * Another very wide spread approach to rectangle classes treats (bottom,right)
+ * also as a part of the rectangle.
+ *
+ * Conceptually, both are sound, but the approach we use saves many intermediate
+ * computations (like computing the height in our case is done by doing this:
+ * height = bottom - top;
+ * while in the alternate system, it would be
+ * height = bottom - top + 1;
+ *
+ * When writing code using our Rect class, always keep this principle in mind!
+*/
+struct Rect {
+ int16 top, left; ///< The point at the top left of the rectangle (part of the rect).
+ int16 bottom, right; ///< The point at the bottom right of the rectangle (not part of the rect).
+
+ Rect() : top(0), left(0), bottom(0), right(0) {}
+ Rect(int16 w, int16 h) : top(0), left(0), bottom(h), right(w) {}
+ Rect(int16 x1, int16 y1, int16 x2, int16 y2) : top(y1), left(x1), bottom(y2), right(x2) {
+ assert(isValidRect());
+ }
+ bool operator==(const Rect &rhs) const { return equals(rhs); }
+ bool operator!=(const Rect &rhs) const { return !equals(rhs); }
+
+ int16 width() const { return right - left; }
+ int16 height() const { return bottom - top; }
+
+ void setWidth(int16 aWidth) {
+ right = left + aWidth;
+ }
+
+ void setHeight(int16 aHeight) {
+ bottom = top + aHeight;
+ }
+
+ /**
+ * Check if given position is inside this rectangle.
+ *
+ * @param x the horizontal position to check
+ * @param y the vertical position to check
+ *
+ * @return true if the given position is inside this rectangle, false otherwise
+ */
+ bool contains(int16 x, int16 y) const {
+ return (left <= x) && (x < right) && (top <= y) && (y < bottom);
+ }
+
+ /**
+ * Check if given point is inside this rectangle.
+ *
+ * @param p the point to check
+ *
+ * @return true if the given point is inside this rectangle, false otherwise
+ */
+ bool contains(const Point &p) const {
+ return contains(p.x, p.y);
+ }
+
+ /**
+ * Check if the given rect is contained inside this rectangle.
+ *
+ * @param r The rectangle to check
+ *
+ * @return true if the given rect is inside, false otherwise
+ */
+ bool contains(const Rect &r) const {
+ return (left <= r.left) && (r.right <= right) && (top <= r.top) && (r.bottom <= bottom);
+ }
+
+ /**
+ * Check if the given rect is equal to this one.
+ *
+ * @param r The rectangle to check
+ *
+ * @return true if the given rect is equal, false otherwise
+ */
+ bool equals(const Rect &r) const {
+ return (left == r.left) && (right == r.right) && (top == r.top) && (bottom == r.bottom);
+ }
+
+ /**
+ * Check if given rectangle intersects with this rectangle
+ *
+ * @param r the rectangle to check
+ *
+ * @return true if the given rectangle is inside the rectangle, false otherwise
+ */
+ bool intersects(const Rect &r) const {
+ return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom);
+ }
+
+ /**
+ * Extend this rectangle so that it contains r
+ *
+ * @param r the rectangle to extend by
+ */
+
+ /**
+ * Extend this rectangle in all four directions by the given number of pixels
+ *
+ * @param offset the size to grow by
+ */
+ void grow(int16 offset) {
+ top -= offset;
+ left -= offset;
+ bottom += offset;
+ right += offset;
+ }
+
+ void clip(const Rect &r) {
+ assert(isValidRect());
+ assert(r.isValidRect());
+
+ if (top < r.top) top = r.top;
+ else if (top > r.bottom) top = r.bottom;
+
+ if (left < r.left) left = r.left;
+ else if (left > r.right) left = r.right;
+
+ if (bottom > r.bottom) bottom = r.bottom;
+ else if (bottom < r.top) bottom = r.top;
+
+ if (right > r.right) right = r.right;
+ else if (right < r.left) right = r.left;
+ }
+
+ void clip(int16 maxw, int16 maxh) {
+ clip(Rect(0, 0, maxw, maxh));
+ }
+
+ bool isEmpty() const {
+ return (left >= right || top >= bottom);
+ }
+
+ bool isValidRect() const {
+ return (left <= right && top <= bottom);
+ }
+
+ void moveTo(int16 x, int16 y) {
+ bottom += y - top;
+ right += x - left;
+ top = y;
+ left = x;
+ }
+
+ void translate(int16 dx, int16 dy) {
+ left += dx; right += dx;
+ top += dy; bottom += dy;
+ }
+
+ void moveTo(const Point &p) {
+ moveTo(p.x, p.y);
+ }
+
+ /**
+ * Create a rectangle around the given center.
+ */
+ static Rect center(int16 cx, int16 cy, int16 w, int16 h) {
+ w /= 2;
+ h /= 2;
+ return Rect(cx - w, cy - h, cx + w, cy + h);
+ }
+};
+
+} // End of namespace Common
+
+#endif
diff --git a/engines/sword25/tools/swfdisplay/swfrender.cpp b/engines/sword25/tools/swfdisplay/swfrender.cpp
new file mode 100644
index 0000000000..af536978c0
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/swfrender.cpp
@@ -0,0 +1,172 @@
+#include "art.h"
+#include "vectorimage.h"
+
+#define SAVING 1
+
+#if SAVING
+static void save_buffer(unsigned char *buffer, uint width, uint height, const char *filename);
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <png.h>
+#include <stdio.h>
+
+static gboolean
+pixbuf_save_to_file(const GdkPixbuf *pixbuf, const char *file_name) {
+ FILE *handle;
+ unsigned char *buffer;
+ gboolean has_alpha;
+ int width, height, depth, rowstride;
+ guchar *pixels;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_text text[2];
+ int i;
+
+ g_return_val_if_fail(pixbuf != NULL, FALSE);
+ g_return_val_if_fail(file_name != NULL, FALSE);
+ g_return_val_if_fail(file_name[0] != '\0', FALSE);
+
+ handle = fopen(file_name, "wb");
+ if (handle == NULL) {
+ return FALSE;
+ }
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png_ptr == NULL) {
+ fclose(handle);
+ return FALSE;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ fclose(handle);
+ return FALSE;
+ }
+
+ if (setjmp(png_ptr->jmpbuf)) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(handle);
+ return FALSE;
+ }
+
+ png_init_io(png_ptr, handle);
+
+ has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
+ width = gdk_pixbuf_get_width(pixbuf);
+ height = gdk_pixbuf_get_height(pixbuf);
+ depth = gdk_pixbuf_get_bits_per_sample(pixbuf);
+ pixels = gdk_pixbuf_get_pixels(pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride(pixbuf);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height,
+ depth, PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ /* Some text to go with the png image */
+ text[0].key = "Title";
+ text[0].text = (char *) file_name;
+ text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+ text[1].key = "Software";
+ text[1].text = "Nautilus Thumbnail";
+ text[1].compression = PNG_TEXT_COMPRESSION_NONE;
+ png_set_text(png_ptr, info_ptr, text, 2);
+
+ /* Write header data */
+ png_write_info(png_ptr, info_ptr);
+
+ /* if there is no alpha in the data, allocate buffer to expand into */
+ if (has_alpha) {
+ buffer = NULL;
+ } else {
+ buffer = (unsigned char *)g_malloc(4 * width);
+ }
+
+ /* pump the raster data into libpng, one scan line at a time */
+ for (i = 0; i < height; i++) {
+ if (has_alpha) {
+ png_bytep row_pointer = pixels;
+ png_write_row(png_ptr, row_pointer);
+ } else {
+ /* expand RGB to RGBA using an opaque alpha value */
+ int x;
+ unsigned char *buffer_ptr = buffer;
+ unsigned char *source_ptr = pixels;
+ for (x = 0; x < width; x++) {
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = *source_ptr++;
+ *buffer_ptr++ = 255;
+ }
+ png_write_row(png_ptr, (png_bytep) buffer);
+ }
+ pixels += rowstride;
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ g_free(buffer);
+
+ fclose(handle);
+ return TRUE;
+}
+
+static void
+save_buffer(unsigned char *buffer, uint width, uint height, const char *filename) {
+ GdkPixbuf *pixbuf;
+
+ g_type_init();
+
+ pixbuf = gdk_pixbuf_new_from_data(buffer,
+ GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ width, height,
+ width * 4,
+ NULL, NULL);
+
+ pixbuf_save_to_file(pixbuf, filename);
+
+ gdk_pixbuf_unref(pixbuf);
+}
+
+#endif
+
+
+int main(int argc, char *argv[]) {
+ if (argc < 2) {
+ printf("Usage: %s <file>\n", argv[0]);
+ exit(0);
+ }
+
+ int fnamelen = strlen(argv[1]);
+ char *fname = (char *)malloc(fnamelen + 1);
+ strcpy(fname, argv[1]);
+ fname[fnamelen-3] = 'p';
+ fname[fnamelen-2] = 'n';
+ fname[fnamelen-1] = 'g';
+
+ FILE *in = fopen(argv[1], "r");
+ fseek(in, 0, SEEK_END);
+ uint32 len = ftell(in);
+
+ byte *buf = (byte *)malloc(len);
+ fseek(in, 0, SEEK_SET);
+
+ fread(buf, len, 1, in);
+ fclose(in);
+
+ bool success;
+ Sword25::VectorImage img(buf, len, success);
+
+ img.render(img.getWidth(), img.getHeight());
+
+#if SAVING
+ save_buffer(img.getPixelData(), img.getWidth(), img.getHeight(), fname);
+#endif
+
+ return 0;
+}
diff --git a/engines/sword25/tools/swfdisplay/vectorimage.cpp b/engines/sword25/tools/swfdisplay/vectorimage.cpp
new file mode 100644
index 0000000000..7b266d8780
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/vectorimage.cpp
@@ -0,0 +1,658 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "art.h"
+#include "vectorimage.h"
+
+namespace Sword25 {
+
+#define BS_LOG_PREFIX "VECTORIMAGE"
+
+#define BEZSMOOTHNESS 0.5
+
+// -----------------------------------------------------------------------------
+// SWF Datentypen
+// -----------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------
+// Bitstream Hilfsklasse
+// -----------------------------------------------------------------------------
+// Das Parsen von SWF-Dateien erfordert sowohl bitweises Auslesen als auch an
+// Bytegrenzen ausgerichtetes Lesen.
+// Diese Klasse ist speziell dafür ausgestattet.
+// -----------------------------------------------------------------------------
+
+class VectorImage::SWFBitStream {
+public:
+ SWFBitStream(const byte *pData, uint dataSize) :
+ m_Pos(pData), m_End(pData + dataSize), m_WordMask(0)
+ {}
+
+ inline uint32 getBits(uint bitCount) {
+ if (bitCount == 0 || bitCount > 32) {
+ error("SWFBitStream::GetBits() must read at least 1 and at most 32 bits at a time");
+ }
+
+ uint32 value = 0;
+ while (bitCount) {
+ if (m_WordMask == 0)
+ flushByte();
+
+ value <<= 1;
+ value |= ((m_Word & m_WordMask) != 0) ? 1 : 0;
+ m_WordMask >>= 1;
+
+ --bitCount;
+ }
+
+ return value;
+ }
+
+ inline int32 getSignedBits(uint bitCount) {
+ // Bits einlesen
+ uint32 temp = getBits(bitCount);
+
+ // Falls das Sign-Bit gesetzt ist, den Rest des Rückgabewertes mit 1-Bits auffüllen (Sign Extension)
+ if (temp & 1 << (bitCount - 1))
+ return (0xffffffff << bitCount) | temp;
+ else
+ return temp;
+ }
+
+ inline uint32 getUInt32() {
+ uint32 byte1 = getByte();
+ uint32 byte2 = getByte();
+ uint32 byte3 = getByte();
+ uint32 byte4 = getByte();
+
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+ }
+
+ inline uint16 getUInt16() {
+ uint32 byte1 = getByte();
+ uint32 byte2 = getByte();
+
+ return byte1 | (byte2 << 8);
+ }
+
+ inline byte getByte() {
+ flushByte();
+ byte value = m_Word;
+ m_WordMask = 0;
+ flushByte();
+
+ return value;
+ }
+
+ inline void flushByte() {
+ if (m_WordMask != 128) {
+ if (m_Pos >= m_End) {
+ error("Attempted to read past end of file");
+ } else {
+ m_Word = *m_Pos++;
+ m_WordMask = 128;
+ }
+ }
+ }
+
+ inline void skipBytes(uint skipLength) {
+ flushByte();
+ if (m_Pos + skipLength >= m_End) {
+ error("Attempted to read past end of file");
+ } else {
+ m_Pos += skipLength;
+ m_Word = *(m_Pos - 1);
+ }
+ }
+
+private:
+ const byte *m_Pos;
+ const byte *m_End;
+
+ byte m_Word;
+ uint m_WordMask;
+};
+
+
+// -----------------------------------------------------------------------------
+// Konstanten und Hilfsfunktionen
+// -----------------------------------------------------------------------------
+
+namespace {
+// -----------------------------------------------------------------------------
+// Konstanten
+// -----------------------------------------------------------------------------
+
+const uint32 MAX_ACCEPTED_FLASH_VERSION = 4; // Die höchste Flash-Dateiversion, die vom Lader akzeptiert wird
+
+
+// -----------------------------------------------------------------------------
+// Konvertiert SWF-Rechteckdaten in einem Bitstrom in Common::Rect-Objekte
+// -----------------------------------------------------------------------------
+
+Common::Rect flashRectToBSRect(VectorImage::SWFBitStream &bs) {
+ bs.flushByte();
+
+ // Feststellen mit wie vielen Bits die einzelnen Komponenten kodiert sind
+ uint32 bitsPerValue = bs.getBits(5);
+
+ // Die einzelnen Komponenten einlesen
+ int32 xMin = bs.getSignedBits(bitsPerValue);
+ int32 xMax = bs.getSignedBits(bitsPerValue);
+ int32 yMin = bs.getSignedBits(bitsPerValue);
+ int32 yMax = bs.getSignedBits(bitsPerValue);
+
+ return Common::Rect(xMin, yMin, xMax + 1, yMax + 1);
+}
+
+// -----------------------------------------------------------------------------
+// Berechnet die Bounding-Box eines BS_VectorImageElement
+// -----------------------------------------------------------------------------
+
+Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement) {
+ double x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
+
+ for (int j = vectorImageElement.getPathCount() - 1; j >= 0; j--) {
+ ArtBpath *bez = vectorImageElement.getPathInfo(j).getVec();
+ ArtVpath *vec = art_bez_path_to_vec(bez, 0.5);
+
+ if (vec[0].code == ART_END) {
+ continue;
+ } else {
+ x0 = x1 = vec[0].x;
+ y0 = y1 = vec[0].y;
+ for (int i = 1; vec[i].code != ART_END; i++) {
+ if (vec[i].x < x0) x0 = vec[i].x;
+ if (vec[i].x > x1) x1 = vec[i].x;
+ if (vec[i].y < y0) y0 = vec[i].y;
+ if (vec[i].y > y1) y1 = vec[i].y;
+ }
+ }
+ free(vec);
+ }
+
+ return Common::Rect(static_cast<int>(x0), static_cast<int>(y0), static_cast<int>(x1) + 1, static_cast<int>(y1) + 1);
+}
+
+}
+
+
+// -----------------------------------------------------------------------------
+// Konstruktion
+// -----------------------------------------------------------------------------
+
+VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success) : _pixelData(0) {
+ success = false;
+
+ // Bitstream-Objekt erzeugen
+ // Im Folgenden werden die Dateidaten aus diesem ausgelesen.
+ SWFBitStream bs(pFileData, fileSize);
+
+ // SWF-Signatur überprüfen
+ uint32 signature[3];
+ signature[0] = bs.getByte();
+ signature[1] = bs.getByte();
+ signature[2] = bs.getByte();
+ if (signature[0] != 'F' ||
+ signature[1] != 'W' ||
+ signature[2] != 'S') {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // Versionsangabe überprüfen
+ uint32 version = bs.getByte();
+ if (version > MAX_ACCEPTED_FLASH_VERSION) {
+ //BS_LOG_ERRORLN("File is of version %d. Highest accepted version is %d.", version, MAX_ACCEPTED_FLASH_VERSION);
+ return;
+ }
+
+ // Dateigröße auslesen und mit der tatsächlichen Größe vergleichen
+ uint32 storedFileSize = bs.getUInt32();
+ if (storedFileSize != fileSize) {
+ BS_LOG_ERRORLN("File is not a valid SWF-file");
+ return;
+ }
+
+ // SWF-Maße auslesen
+ Common::Rect movieRect = flashRectToBSRect(bs);
+
+ // Framerate und Frameanzahl auslesen
+ /* uint32 frameRate = */
+ bs.getUInt16();
+ /* uint32 frameCount = */
+ bs.getUInt16();
+
+ // Tags parsen
+ // Da wir uns nur für das erste DefineShape-Tag interessieren
+ bool keepParsing = true;
+ while (keepParsing) {
+ // Tags beginnen immer an Bytegrenzen
+ bs.flushByte();
+
+ // Tagtyp und Länge auslesen
+ uint16 tagTypeAndLength = bs.getUInt16();
+ uint32 tagType = tagTypeAndLength >> 6;
+ uint32 tagLength = tagTypeAndLength & 0x3f;
+ if (tagLength == 0x3f)
+ tagLength = bs.getUInt32();
+
+ switch (tagType) {
+ case 2:
+ // DefineShape
+ success = parseDefineShape(2, bs);
+ return;
+ case 22:
+ // DefineShape2
+ success = parseDefineShape(2, bs);
+ return;
+ case 32:
+ success = parseDefineShape(3, bs);
+ return;
+ default:
+ // Unbekannte Tags ignorieren
+ bs.skipBytes(tagLength);
+ }
+ }
+
+ // Die Ausführung darf nicht an dieser Stelle ankommen: Entweder es wird ein Shape gefunden, dann wird die Funktion mit vorher verlassen, oder
+ // es wird keines gefunden, dann tritt eine Exception auf sobald über das Ende der Datei hinaus gelesen wird.
+ BS_ASSERT(false);
+}
+
+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())
+ free(_elements[j].getPathInfo(i).getVec());
+
+ if (_pixelData)
+ free(_pixelData);
+}
+
+
+ArtBpath *ensureBezStorage(ArtBpath *bez, int nodes, int *allocated) {
+ if (*allocated <= nodes) {
+ (*allocated) += 20;
+
+ return art_renew(bez, ArtBpath, *allocated);
+ }
+
+ return bez;
+}
+
+ArtBpath *VectorImage::storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated) {
+ (*bezNodes)++;
+
+ bez = ensureBezStorage(bez, *bezNodes, bezAllocated);
+ bez[*bezNodes].code = ART_END;
+
+ ArtBpath *bez1 = art_new(ArtBpath, *bezNodes + 1);
+
+ for (int i = 0; i <= *bezNodes; i++)
+ bez1[i] = bez[i];
+
+#if DEBUG
+ printf("storeBez(bez, %d, %d, %d, %d, %d)\n", lineStyle, fillStyle0, fillStyle1, *bezNodes, *bezAllocated);
+#endif
+
+ _elements.back()._pathInfos.push_back(VectorPathInfo(bez1, *bezNodes, lineStyle, fillStyle0, fillStyle1));
+
+ return bez;
+}
+
+bool VectorImage::parseDefineShape(uint shapeType, SWFBitStream &bs) {
+ /*uint32 shapeID = */bs.getUInt16();
+
+ // Bounding Box auslesen
+ _boundingBox = flashRectToBSRect(bs);
+
+ // Erstes Image-Element erzeugen
+ _elements.resize(1);
+
+ // Styles einlesen
+ uint numFillBits;
+ uint numLineBits;
+ if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
+ return false;
+
+ uint lineStyle = 0;
+ uint fillStyle0 = 0;
+ uint fillStyle1 = 0;
+
+ // Shaperecord parsen
+ // ------------------
+
+ double curX = 0;
+ double curY = 0;
+ int bezNodes = 0;
+ int bezAllocated = 10;
+ ArtBpath *bez = art_new(ArtBpath, bezAllocated);
+
+ bool endOfShapeDiscovered = false;
+ while (!endOfShapeDiscovered) {
+ uint32 typeFlag = bs.getBits(1);
+
+ // Non-Edge Record
+ if (typeFlag == 0) {
+ // Feststellen welche Parameter gesetzt werden
+ uint32 stateNewStyles = bs.getBits(1);
+ uint32 stateLineStyle = bs.getBits(1);
+ uint32 stateFillStyle1 = bs.getBits(1);
+ uint32 stateFillStyle0 = bs.getBits(1);
+ uint32 stateMoveTo = bs.getBits(1);
+
+ uint prevLineStyle = lineStyle;
+ uint prevFillStyle0 = fillStyle0;
+ uint prevFillStyle1 = fillStyle1;
+
+ // End der Shape-Definition erreicht?
+ if (!stateNewStyles && !stateLineStyle && !stateFillStyle0 && !stateFillStyle1 && !stateMoveTo) {
+ endOfShapeDiscovered = true;
+ // Parameter dekodieren
+ } else {
+ if (stateMoveTo) {
+ uint32 moveToBits = bs.getBits(5);
+ curX = bs.getSignedBits(moveToBits);
+ curY = bs.getSignedBits(moveToBits);
+
+#if DEBUG
+ printf("M --> %lf, %lf\n", curX, curY);
+#endif
+ }
+
+ if (stateFillStyle0) {
+ if (numFillBits > 0)
+ fillStyle0 = bs.getBits(numFillBits);
+ else
+ fillStyle0 = 0;
+#if DEBUG
+ printf("0 --> %d\n", fillStyle0);
+#endif
+ }
+
+ if (stateFillStyle1) {
+ if (numFillBits > 0)
+ fillStyle1 = bs.getBits(numFillBits);
+ else
+ fillStyle1 = 0;
+#if DEBUG
+ printf("1 --> %d\n", fillStyle1);
+#endif
+ }
+
+ if (stateLineStyle) {
+ if (numLineBits)
+ lineStyle = bs.getBits(numLineBits);
+ else
+ lineStyle = 0;
+#if DEBUG
+ printf("L --> %d\n", lineStyle);
+#endif
+ }
+
+ if (stateNewStyles) {
+#if DEBUG
+ printf("New Style\n");
+#endif
+ // An dieser Stelle werden in Flash die alten Style-Definitionen verworfen und mit den neuen überschrieben.
+ // Es wird ein neues Element begonnen.
+ _elements.resize(_elements.size() + 1);
+ if (!parseStyles(shapeType, bs, numFillBits, numLineBits))
+ return false;
+ }
+
+ // Ein neuen Pfad erzeugen, es sei denn, es wurden nur neue Styles definiert
+ if (stateLineStyle || stateFillStyle0 || stateFillStyle1 || stateMoveTo) {
+ // Store previous curve if any
+ if (bezNodes) {
+ bez = storeBez(bez, prevLineStyle, prevFillStyle0, prevFillStyle1, &bezNodes, &bezAllocated);
+ }
+
+ // Start new curve
+ bez = ensureBezStorage(bez, 1, &bezAllocated);
+ bez[0].code = ART_MOVETO_OPEN;
+ bez[0].x3 = curX;
+ bez[0].y3 = curY;
+ bezNodes = 0;
+ }
+ }
+ } else {
+ // Edge Record
+ uint32 edgeFlag = bs.getBits(1);
+ uint32 numBits = bs.getBits(4) + 2;
+
+ // Curved edge
+ if (edgeFlag == 0) {
+ double controlDeltaX = bs.getSignedBits(numBits);
+ double controlDeltaY = bs.getSignedBits(numBits);
+ double anchorDeltaX = bs.getSignedBits(numBits);
+ double anchorDeltaY = bs.getSignedBits(numBits);
+
+ double controlX = curX + controlDeltaX;
+ double controlY = curY + controlDeltaY;
+ double newX = controlX + anchorDeltaX;
+ double newY = controlY + anchorDeltaY;
+
+#define WEIGHT (2.0/3.0)
+
+ bezNodes++;
+ bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
+ bez[bezNodes].code = ART_CURVETO;
+ bez[bezNodes].x1 = WEIGHT * controlX + (1 - WEIGHT) * curX;
+ bez[bezNodes].y1 = WEIGHT * controlY + (1 - WEIGHT) * curY;
+ bez[bezNodes].x2 = WEIGHT * controlX + (1 - WEIGHT) * newX;
+ bez[bezNodes].y2 = WEIGHT * controlY + (1 - WEIGHT) * newY;
+ bez[bezNodes].x3 = newX;
+ bez[bezNodes].y3 = newY;
+
+ curX = newX;
+ curY = newY;
+ } else {
+ // Staight edge
+ int32 deltaX = 0;
+ int32 deltaY = 0;
+
+ uint32 generalLineFlag = bs.getBits(1);
+ if (generalLineFlag) {
+ deltaX = bs.getSignedBits(numBits);
+ deltaY = bs.getSignedBits(numBits);
+ } else {
+ uint32 vertLineFlag = bs.getBits(1);
+ if (vertLineFlag)
+ deltaY = bs.getSignedBits(numBits);
+ else
+ deltaX = bs.getSignedBits(numBits);
+ }
+
+ curX += deltaX;
+ curY += deltaY;
+
+ bezNodes++;
+ bez = ensureBezStorage(bez, bezNodes, &bezAllocated);
+ bez[bezNodes].code = ART_LINETO;
+ bez[bezNodes].x3 = curX;
+ bez[bezNodes].y3 = curY;
+ }
+ }
+ }
+
+ // Store last curve
+ if (bezNodes)
+ bez = storeBez(bez, lineStyle, fillStyle0, fillStyle1, &bezNodes, &bezAllocated);
+
+ free(bez);
+
+ // Bounding-Boxes der einzelnen Elemente berechnen
+ Common::Array<VectorImageElement>::iterator it = _elements.begin();
+ for (; it != _elements.end(); ++it)
+ it->_boundingBox = CalculateBoundingBox(*it);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits) {
+ bs.flushByte();
+
+ // Fillstyles parsen
+ // -----------------
+
+ // Anzahl an Fillstyles bestimmen
+ uint fillStyleCount = bs.getByte();
+ if (fillStyleCount == 0xff)
+ fillStyleCount = bs.getUInt16();
+
+ // Alle Fillstyles einlesen, falls ein Fillstyle mit Typ != 0 gefunden wird, wird das Parsen abgebrochen.
+ // Es wird nur "solid fill" (Typ 0) unterstützt.
+ _elements.back()._fillStyles.reserve(fillStyleCount);
+ for (uint i = 0; i < fillStyleCount; ++i) {
+ byte type = bs.getByte();
+ uint32 color;
+ byte r = bs.getByte();
+ byte g = bs.getByte();
+ byte b = bs.getByte();
+ byte a = 0xff;
+
+ if (shapeType == 3)
+ a = bs.getByte();
+
+ //color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+ color = BS_ARGB(a, r, g, b);
+
+ if (type != 0)
+ return false;
+
+ _elements.back()._fillStyles.push_back(color);
+ }
+
+ // Linestyles parsen
+ // -----------------
+
+ // Anzahl an Linestyles bestimmen
+ uint lineStyleCount = bs.getByte();
+ if (lineStyleCount == 0xff)
+ lineStyleCount = bs.getUInt16();
+
+ // Alle Linestyles einlesen
+ _elements.back()._lineStyles.reserve(lineStyleCount);
+ for (uint i = 0; i < lineStyleCount; ++i) {
+ double width = bs.getUInt16();
+ uint32 color;
+ byte r = bs.getByte();
+ byte g = bs.getByte();
+ byte b = bs.getByte();
+ byte a = 0xff;
+
+ if (shapeType == 3)
+ a = bs.getByte();
+
+ //color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(a, r, g, b);
+ color = BS_ARGB(a, r, g, b);
+
+ _elements.back()._lineStyles.push_back(VectorImageElement::LineStyleType(width, color));
+ }
+
+ // Bitbreite für die folgenden Styleindizes auslesen
+ numFillBits = bs.getBits(4);
+ numLineBits = bs.getBits(4);
+
+ return true;
+}
+
+
+// -----------------------------------------------------------------------------
+
+#if 0
+bool VectorImage::fill(const Common::Rect *pFillRect, uint color) {
+ BS_LOG_ERRORLN("Fill() is not supported.");
+ return false;
+}
+#endif
+
+// -----------------------------------------------------------------------------
+
+uint VectorImage::getPixel(int x, int y) {
+ BS_LOG_ERRORLN("GetPixel() is not supported. Returning black.");
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+bool VectorImage::setContent(const byte *pixeldata, uint size, uint offset, uint stride) {
+ BS_LOG_ERRORLN("SetContent() is not supported.");
+ return 0;
+}
+
+#if 0
+bool VectorImage::blit(int posX, int posY,
+ int flipping,
+ Common::Rect *pPartRect,
+ uint color,
+ int width, int height) {
+ static VectorImage *oldThis = 0;
+ static int oldWidth = -2;
+ static int oldHeight = -2;
+
+ // Falls Breite oder Höhe 0 sind, muss nichts dargestellt werden.
+ if (width == 0 || height == 0)
+ return true;
+
+ // Feststellen, ob das alte Bild im Cache nicht wiederbenutzt werden kann und neu Berechnet werden muss
+ if (!(oldThis == this && oldWidth == width && oldHeight == height)) {
+ render(width, height);
+
+ oldThis = this;
+ oldHeight = height;
+ oldWidth = width;
+ }
+
+ GLImage *rend = new GLImage();
+
+ rend->replaceContent(_pixelData, width, height);
+ rend->blit(posX, posY, flipping, pPartRect, color, width, height);
+
+ delete rend;
+
+ return true;
+}
+#endif
+
+} // End of namespace Sword25
diff --git a/engines/sword25/tools/swfdisplay/vectorimage.h b/engines/sword25/tools/swfdisplay/vectorimage.h
new file mode 100644
index 0000000000..70a46b62d7
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/vectorimage.h
@@ -0,0 +1,233 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#ifndef SWORD25_VECTORIMAGE_H
+#define SWORD25_VECTORIMAGE_H
+
+// -----------------------------------------------------------------------------
+// Includes
+// -----------------------------------------------------------------------------
+
+#include "rect.h"
+#include "array.h"
+
+#include "art.h"
+
+namespace Sword25 {
+
+class VectorImage;
+
+/**
+ @brief Pfadinformationen zu BS_VectorImageElement Objekten
+
+ Jedes BS_VectorImageElement besteht aus Kantenzügen, oder auch Pfaden. Jeder dieser Pfad hat Eigenschaften, die in Objekten diesen Typs
+ gespeichert werden.
+*/
+
+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() {
+ _lineStyle = _fillStyle0 = _fillStyle1 = _len = 0;
+ _vec = 0;
+ }
+
+ ArtBpath *getVec() const {
+ return _vec;
+ }
+ int getVecLen() const {
+ return _len;
+ }
+ uint getLineStyle() const {
+ return _lineStyle;
+ }
+ uint getFillStyle0() const {
+ return _fillStyle0;
+ }
+ uint getFillStyle1() const {
+ return _fillStyle1;
+ }
+
+private:
+ ArtBpath *_vec;
+ uint _lineStyle;
+ uint _fillStyle0;
+ uint _fillStyle1;
+ uint _len;
+};
+
+/**
+ @brief Ein Element eines Vektorbild. Ein BS_VectorImage besteht aus diesen Elementen, die jeweils einen Teil der Graphik definieren.
+ Werden alle Elemente eines Vektorbildes übereinandergelegt, ergibt sich das komplette Bild.
+*/
+class VectorImageElement {
+ friend class VectorImage;
+public:
+ uint getPathCount() const {
+ return _pathInfos.size();
+ }
+ const VectorPathInfo &getPathInfo(uint pathNr) const {
+ BS_ASSERT(pathNr < getPathCount());
+ return _pathInfos[pathNr];
+ }
+
+ double getLineStyleWidth(uint lineStyle) const {
+ BS_ASSERT(lineStyle < _lineStyles.size());
+ return _lineStyles[lineStyle].width;
+ }
+
+ uint getLineStyleCount() const {
+ return _lineStyles.size();
+ }
+
+ uint32 getLineStyleColor(uint lineStyle) const {
+ BS_ASSERT(lineStyle < _lineStyles.size());
+ return _lineStyles[lineStyle].color;
+ }
+
+ uint getFillStyleCount() const {
+ return _fillStyles.size();
+ }
+
+ uint32 getFillStyleColor(uint fillStyle) const {
+ BS_ASSERT(fillStyle < _fillStyles.size());
+ return _fillStyles[fillStyle];
+ }
+
+ const Common::Rect &getBoundingBox() const {
+ return _boundingBox;
+ }
+
+private:
+ struct LineStyleType {
+ LineStyleType(double width_, uint32 color_) : width(width_), color(color_) {}
+ LineStyleType() {
+ width = 0;
+ color = 0;
+ }
+ double width;
+ uint32 color;
+ };
+
+ Common::Array<VectorPathInfo> _pathInfos;
+ Common::Array<LineStyleType> _lineStyles;
+ Common::Array<uint32> _fillStyles;
+ Common::Rect _boundingBox;
+};
+
+
+/**
+ @brief Eine Vektorgraphik
+
+ Objekte dieser Klasse enthalten die Informationen eines SWF-Shapes.
+*/
+
+class VectorImage {
+public:
+ VectorImage(const byte *pFileData, uint fileSize, bool &success);
+ ~VectorImage();
+
+ uint getElementCount() const {
+ return _elements.size();
+ }
+ const VectorImageElement &getElement(uint elementNr) const {
+ BS_ASSERT(elementNr < _elements.size());
+ return _elements[elementNr];
+ }
+ const Common::Rect &getBoundingBox() const {
+ return _boundingBox;
+ }
+
+ //
+ // Die abstrakten Methoden von BS_Image
+ //
+ virtual int getWidth() const {
+ return _boundingBox.width();
+ }
+ virtual int getHeight() const {
+ return _boundingBox.height();
+ }
+
+ void render(int width, int height);
+
+ virtual uint getPixel(int x, int y);
+ virtual bool isBlitSource() const {
+ return true;
+ }
+ virtual bool isBlitTarget() const {
+ return false;
+ }
+ virtual bool isScalingAllowed() const {
+ return true;
+ }
+ virtual bool isFillingAllowed() const {
+ return false;
+ }
+ virtual bool isAlphaAllowed() const {
+ return true;
+ }
+ virtual bool isColorModulationAllowed() const {
+ return true;
+ }
+ virtual bool isSetContentAllowed() const {
+ return false;
+ }
+ virtual bool setContent(const byte *pixeldata, uint size, uint offset, uint stride);
+
+ byte *getPixelData() { return _pixelData; }
+#if 0
+ virtual bool blit(int posX = 0, int posY = 0,
+ int flipping = FLIP_NONE,
+ Common::Rect *pPartRect = NULL,
+ uint color = BS_ARGB(255, 255, 255, 255),
+ int width = -1, int height = -1);
+#endif
+ class SWFBitStream;
+
+private:
+ bool parseDefineShape(uint shapeType, SWFBitStream &bs);
+ bool parseStyles(uint shapeType, SWFBitStream &bs, uint &numFillBits, uint &numLineBits);
+
+ ArtBpath *storeBez(ArtBpath *bez, int lineStyle, int fillStyle0, int fillStyle1, int *bezNodes, int *bezAllocated);
+ Common::Array<VectorImageElement> _elements;
+ Common::Rect _boundingBox;
+
+ byte *_pixelData;
+};
+
+} // End of namespace Sword25
+
+#endif
diff --git a/engines/sword25/tools/swfdisplay/vectorimagerenderer.cpp b/engines/sword25/tools/swfdisplay/vectorimagerenderer.cpp
new file mode 100644
index 0000000000..ba964b6a9d
--- /dev/null
+++ b/engines/sword25/tools/swfdisplay/vectorimagerenderer.cpp
@@ -0,0 +1,480 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on Broken Sword 2.5 engine
+ *
+ * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
+ *
+ * Licensed under GNU GPL v2
+ *
+ */
+
+#include "art_svp_vpath.h"
+#include "art_svp_vpath_stroke.h"
+#include "art_svp_render_aa.h"
+
+#include "vectorimage.h"
+
+namespace Sword25 {
+
+void art_rgb_fill_run1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n) {
+ int i;
+
+ if (r == g && g == b && r == 255) {
+ memset(buf, g, n + n + n + n);
+ } else {
+ uint32 *alt = (uint32 *)buf;
+ //uint32 color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(0xff, r, g, b);
+ uint32 color = BS_ARGB(0xff, r, g, b);
+
+ for (i = 0; i < n; i++)
+ *alt++ = color;
+ }
+}
+
+void art_rgb_run_alpha1(art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n) {
+ int i;
+ int v;
+
+ for (i = 0; i < n; i++) {
+ v = *buf;
+ *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+ v = *buf;
+ if (v + alpha > 255)
+ *buf++ = 255;
+ else
+ *buf++ = v + alpha; //(((alpha - v) * alpha + 0x80) >> 8);
+ }
+}
+
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ } else {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps) {
+ ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
+ }
+ }
+
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
+ else
+ art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
+ }
+ }
+ } else {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
+ else
+ art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+void art_rgb_svp_alpha1(const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ uint32 color,
+ art_u8 *buf, int rowstride) {
+ ArtRgbSVPAlphaData data;
+ byte r, g, b, alpha;
+ int i;
+ int a, da;
+
+ //Graphics::colorToARGB<Graphics::ColorMasks<8888> >(color, alpha, r, g, b);
+ alpha = (color >> 24) & 0xff;
+ b = (color >> 16) & 0xff;
+ g = (color >> 8) & 0xff;
+ r = (color >> 0) & 0xff;
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++) {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
+ else
+ art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
+}
+
+static int art_vpath_len(ArtVpath *a) {
+ int i;
+
+ for (i = 0; a[i].code != ART_END; i++);
+ return i;
+}
+
+ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
+ ArtVpath *dest;
+ ArtVpath *p;
+ int len_a, len_b;
+
+ len_a = art_vpath_len(a);
+ len_b = art_vpath_len(b);
+ dest = art_new(ArtVpath, len_a + len_b + 1);
+ p = dest;
+
+ for (int i = 0; i < len_a; i++)
+ *p++ = *a++;
+ for (int i = 0; i <= len_b; i++)
+ *p++ = *b++;
+
+ return dest;
+}
+
+void art_svp_make_convex(ArtSVP *svp) {
+ int i;
+
+ if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
+ for (i = 0; i < svp->n_segs; i++) {
+ svp->segs[i].dir = !svp->segs[i].dir;
+ }
+ }
+}
+
+ArtVpath *art_vpath_reverse(ArtVpath *a) {
+ ArtVpath *dest;
+ ArtVpath it;
+ int len;
+ int state = 0;
+ int i;
+
+ len = art_vpath_len(a);
+ dest = art_new(ArtVpath, len + 1);
+
+ for (i = 0; i < len; i++) {
+ it = a[len - i - 1];
+ if (state) {
+ it.code = ART_LINETO;
+ } else {
+ it.code = ART_MOVETO_OPEN;
+ state = 1;
+ }
+ if (a[len - i - 1].code == ART_MOVETO ||
+ a[len - i - 1].code == ART_MOVETO_OPEN) {
+ state = 0;
+ }
+ dest[i] = it;
+ }
+ dest[len] = a[len];
+
+ return dest;
+}
+
+ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
+ ArtVpath *dest;
+
+ dest = art_vpath_reverse(a);
+ free(a);
+
+ return dest;
+}
+
+void drawBez(ArtBpath *bez1, ArtBpath *bez2, art_u8 *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
+ ArtVpath *vec = NULL;
+ ArtVpath *vec1 = NULL;
+ ArtVpath *vec2 = NULL;
+ ArtSVP *svp = NULL;
+
+#if DEBUG
+ const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
+ for (int i = 0;; i++) {
+ printf(" bez1[%d].code = %s;\n", i, codes[bez1[i].code]);
+ if (bez1[i].code == ART_END)
+ break;
+ if (bez1[i].code == ART_CURVETO) {
+ printf(" bez1[%d].x1 = %f; bez1[%d].y1 = %f;\n", i, bez1[i].x1, i, bez1[i].y1);
+ printf(" bez1[%d].x2 = %f; bez1[%d].y2 = %f;\n", i, bez1[i].x2, i, bez1[i].y2);
+ }
+ printf(" bez1[%d].x3 = %f; bez1[%d].y3 = %f;\n", i, bez1[i].x3, i, bez1[i].y3);
+ }
+ if (bez2 != 0) {
+ for (int i = 0;; i++) {
+ printf(" bez2[%d].code = %s;\n", i, codes[bez2[i].code]);
+ if (bez2[i].code == ART_END)
+ break;
+ if (bez2[i].code == ART_CURVETO) {
+ printf(" bez2[%d].x1 = %f; bez2[%d].y1 = %f;\n", i, bez2[i].x1, i, bez2[i].y1);
+ printf(" bez2[%d].x2 = %f; bez2[%d].y2 = %f;\n", i, bez2[i].x2, i, bez2[i].y2);
+ }
+ printf(" bez2[%d].x3 = %f; bez2[%d].y3 = %f;\n", i, bez2[i].x3, i, bez2[i].y3);
+ }
+ printf(" drawBez(bez1, bez2, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
+ } else {
+ printf(" drawBez(bez1, 0, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
+ }
+
+#endif
+
+ vec1 = art_bez_path_to_vec(bez1, 0.5);
+ if (bez2 != 0) {
+ vec2 = art_bez_path_to_vec(bez2, 0.5);
+ vec2 = art_vpath_reverse_free(vec2);
+ vec = art_vpath_cat(vec1, vec2);
+
+ free(vec1);
+ free(vec2);
+ } else {
+ vec = vec1;
+ }
+
+ if (scaleX != 1.0 || scaleY != 1.0) {
+ ArtVpath *vect;
+ int size = art_vpath_len(vec);
+
+ vect = art_new(ArtVpath, size + 1);
+
+ int k;
+ for (k = 0; k < size; k++) {
+ vect[k].code = vec[k].code;
+ vect[k].x = vec[k].x * scaleX;
+ vect[k].y = vec[k].y * scaleY;
+ }
+ vect[k].code = ART_END;
+ free(vec);
+
+ vec = vect;
+ }
+
+ if (bez2 == 0) { // Line drawing
+ svp = art_svp_vpath_stroke(vec, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
+ } else {
+ svp = art_svp_from_vpath(vec);
+ art_svp_make_convex(svp);
+ }
+
+ art_rgb_svp_alpha1(svp, 0 + deltaX, 0 + deltaY, width + deltaX, height + deltaY, color, buffer, width * 4);
+
+ free(svp);
+ free(vec);
+}
+
+void VectorImage::render(int width, int height) {
+ double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
+ double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
+
+ //debug(0, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
+
+ printf("%d x %d\n", width, height);
+ printf("%d x %d - %d x %d\n", _boundingBox.left, _boundingBox.top, _boundingBox.right, _boundingBox.bottom);
+
+ if (_pixelData)
+ free(_pixelData);
+
+ _pixelData = (byte *)malloc(width * height * 4);
+ memset(_pixelData, 0, width * height * 4);
+
+ for (uint e = 0; e < _elements.size(); e++) {
+#if DEBUG
+ printf("----------------> Element\n");
+#endif
+
+ //// Draw shapes
+ for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
+ int fill0len = 0;
+ int fill1len = 0;
+
+ // Count vector sizes in order to minimize memory
+ // fragmentation
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
+ fill0len += _elements[e].getPathInfo(p).getVecLen();
+
+ if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
+ fill1len += _elements[e].getPathInfo(p).getVecLen();
+ }
+
+ // Now lump together vectors
+ ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
+ ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
+ ArtBpath *fill1pos = fill1;
+ ArtBpath *fill0pos = fill0;
+
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
+ for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
+ *fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
+ }
+
+ if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
+ for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
+ *fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
+ }
+ }
+
+ // Close vectors
+ (*fill0pos).code = ART_END;
+ (*fill1pos).code = ART_END;
+
+ drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
+
+ free(fill0);
+ free(fill1);
+ }
+
+ //// Draw strokes
+ for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
+ double penWidth = _elements[e].getLineStyleWidth(s);
+ penWidth *= sqrt(fabs(scaleX * scaleY));
+
+ for (uint p = 0; p < _elements[e].getPathCount(); p++) {
+ if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
+ drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
+ }
+ }
+ }
+ }
+}
+
+
+} // End of namespace Sword25