diff options
author | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-26 00:26:00 +0000 |
---|---|---|
committer | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-26 00:26:00 +0000 |
commit | 00d721c144d35882dfaf24b3869e95a8d6404ee5 (patch) | |
tree | 07243b1cb15a7fb0eddf54fce686c96cda594640 | |
parent | 0a46d19ce396112b8a42005d1369de30df3bc872 (diff) | |
download | chromium_src-00d721c144d35882dfaf24b3869e95a8d6404ee5.zip chromium_src-00d721c144d35882dfaf24b3869e95a8d6404ee5.tar.gz chromium_src-00d721c144d35882dfaf24b3869e95a8d6404ee5.tar.bz2 |
Added the bulk of the algorithm for GPU accelerated 2D vector curve
rendering from "Rendering Vector Art on the GPU" by Loop and Blinn,
GPU Gems 3, Chapter 25.
The main entry point to the algorithm is the PathProcessor, which
takes in a Skia path and converts it to two triangle meshes: one for
the exterior region of the shape containing the curve segments, and
one for the interior region of the shape which is filled with constant
(1.0) alpha.
The o3d.ProcessedPath class is the internal object which exposes the
needed entry points to JavaScript. However, o3djs.gpu2d is the
user-level entry point to the algorithm. This exposes a Path primitive
to which line, quadratic curve and cubic curve segments can be added,
and simple fills (currently only a solid color).
An SVG loader in samples/gpu2d/svgloader.js illustrates how content
might be imported at run time. Several samples and regression tests
demonstrate the current state of the implementation. More work is
planned.
Some small generalizations to the O3D code were necessary to support
two-dimensional vertices.
Note that I plan to submit gpu2d.js and/or svgloader.js for JavaScript
readability. I have run both through the JS compiler and have fixed as
many of the doc generation errors as possible in svgloader.js without
pulling this file into the o3djs namespace.
Tested in O3D on Windows and Mac OS X.
BUG=none
TEST=various SVG based tests
Review URL: http://codereview.chromium.org/652016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40079 0039d316-1c4b-4281-b951-d872f2087c98
39 files changed, 5909 insertions, 23 deletions
@@ -4,7 +4,7 @@ vars = { # When updating the chromium rev, you must also update the nss and sqlite # revs to match the version pulled-in by Chromium's own DEPS in the new rev. "chromium_rev": "37758", - "o3d_code_rev": "166", + "o3d_code_rev": "174", "skia_rev": "488", "gyp_rev": "775", "gtest_rev": "359", @@ -53,6 +53,9 @@ deps = { # Stuff that is O3D specific (from a Chrome point of view). + "o3d/third_party/glu": + "http://o3d.googlecode.com/svn/trunk/googleclient/third_party/glu@" + Var("o3d_code_rev"), + "o3d/third_party/vectormath": "http://o3d.googlecode.com/svn/trunk/googleclient/third_party/vectormath@" + Var("o3d_code_rev"), diff --git a/o3d/core/core.gyp b/o3d/core/core.gyp index 1b8ee2d..a3e171a 100644 --- a/o3d/core/core.gyp +++ b/o3d/core/core.gyp @@ -79,6 +79,7 @@ '../../<(pngdir)/libpng.gyp:libpng', '../../<(zlibdir)/zlib.gyp:zlib', '../../skia/skia.gyp:skia', + '../third_party/glu/libtess.gyp:libtess', ], 'sources': [ 'cross/bitmap.cc', @@ -207,6 +208,8 @@ 'cross/precompile.h', 'cross/primitive.cc', 'cross/primitive.h', + 'cross/processed_path.cc', + 'cross/processed_path.h', 'cross/profiler.cc', 'cross/profiler.h', 'cross/ray_intersection_info.cc', @@ -279,6 +282,10 @@ 'cross/gpu2d/interval_tree.h', 'cross/gpu2d/local_triangulator.cc', 'cross/gpu2d/local_triangulator.h', + 'cross/gpu2d/path_cache.cc', + 'cross/gpu2d/path_cache.h', + 'cross/gpu2d/path_processor.cc', + 'cross/gpu2d/path_processor.h', 'cross/gpu2d/red_black_tree.h', ], 'direct_dependent_settings': { diff --git a/o3d/core/cross/class_manager.cc b/o3d/core/cross/class_manager.cc index d74376d..1a3b909 100644 --- a/o3d/core/cross/class_manager.cc +++ b/o3d/core/cross/class_manager.cc @@ -54,6 +54,7 @@ #include "core/cross/param_array.h" #include "core/cross/param_operation.h" #include "core/cross/primitive.h" +#include "core/cross/processed_path.h" #include "core/cross/render_surface_set.h" #include "core/cross/sampler.h" #include "core/cross/shape.h" @@ -157,6 +158,7 @@ ClassManager::ClassManager(ServiceLocator* service_locator) AddTypedClass<ParamArray>(); AddTypedClass<ParamObject>(); AddTypedClass<Primitive>(); + AddTypedClass<ProcessedPath>(); AddTypedClass<RenderFrameCounter>(); AddTypedClass<RenderNode>(); AddTypedClass<RenderSurfaceSet>(); diff --git a/o3d/core/cross/gpu2d/cubic_math_utils.cc b/o3d/core/cross/gpu2d/cubic_math_utils.cc index a62ba3b..023ce6c 100644 --- a/o3d/core/cross/gpu2d/cubic_math_utils.cc +++ b/o3d/core/cross/gpu2d/cubic_math_utils.cc @@ -49,6 +49,59 @@ int Orientation(float x1, float y1, return (cross_product < 0.0f) ? -1 : ((cross_product > 0.0f) ? 1 : 0); } +bool EdgeEdgeTest(float ax, float ay, + float v0x, float v0y, + float u0x, float u0y, + float u1x, float u1y) { + // This edge to edge test is based on Franlin Antonio's gem: "Faster + // Line Segment Intersection", in Graphics Gems III, pp. 199-202. + float bx = u0x - u1x; + float by = u0y - u1y; + float cx = v0x - u0x; + float cy = v0y - u0y; + float f = ay * bx - ax * by; + float d = by * cx - bx * cy; + if ((f > 0 && d >= 0 && d <= f) || (f < 0 && d <= 0 && d >= f)) { + float e = ax * cy - ay * cx; + + // This additional test avoids reporting coincident edges, which + // is the behavior we want. + if (ApproxEqual(e, 0) || ApproxEqual(f, 0) || ApproxEqual(e, f)) { + return false; + } + + if (f > 0) { + return e >= 0 && e <= f; + } else { + return e <= 0 && e >= f; + } + } + return false; +} + +bool EdgeAgainstTriEdges(float v0x, float v0y, + float v1x, float v1y, + float u0x, float u0y, + float u1x, float u1y, + float u2x, float u2y) { + float ax, ay; + ax = v1x - v0x; + ay = v1y - v0y; + // Test edge u0, u1 against v0, v1. + if (EdgeEdgeTest(ax, ay, v0x, v0y, u0x, u0y, u1x, u1y)) { + return true; + } + // Test edge u1, u2 against v0, v1. + if (EdgeEdgeTest(ax, ay, v0x, v0y, u1x, u1y, u2x, u2y)) { + return true; + } + // Test edge u2, u1 against v0, v1. + if (EdgeEdgeTest(ax, ay, v0x, v0y, u2x, u2y, u0x, u0y)) { + return true; + } + return false; +} + } // anonymous namespace bool LinesIntersect(float p1x, float p1y, @@ -91,6 +144,44 @@ bool PointInTriangle(float px, float py, return (u > 0.0f) && (v > 0.0f) && (u + v < 1.0f); } +bool TrianglesOverlap(float a1x, float a1y, + float b1x, float b1y, + float c1x, float c1y, + float a2x, float a2y, + float b2x, float b2y, + float c2x, float c2y) { + // Derived from coplanar_tri_tri() at + // http://jgt.akpeters.com/papers/ShenHengTang03/tri_tri.html , + // simplified for the 2D case and modified so that overlapping edges + // do not report overlapping triangles. + + // Test all edges of triangle 1 against the edges of triangle 2. + if (EdgeAgainstTriEdges(a1x, a1y, b1x, b1y, a2x, a2y, b2x, b2y, c2x, c2y)) + return true; + if (EdgeAgainstTriEdges(b1x, b1y, c1x, c1y, a2x, a2y, b2x, b2y, c2x, c2y)) + return true; + if (EdgeAgainstTriEdges(c1x, c1y, a1x, a1y, a2x, a2y, b2x, b2y, c2x, c2y)) + return true; + // Finally, test if tri1 is totally contained in tri2 or vice versa. + if (PointInTriangle(a1x, a1y, a2x, a2y, b2x, b2y, c2x, c2y)) + return true; + if (PointInTriangle(a2x, a2y, a1x, a1y, b1x, b1y, c1x, c1y)) + return true; + + // Because we define that triangles sharing a vertex or edge don't + // overlap, we must perform additional point-in-triangle tests to + // see whether one triangle is contained in the other. + if (PointInTriangle(b1x, b1y, a2x, a2y, b2x, b2y, c2x, c2y)) + return true; + if (PointInTriangle(c1x, c1y, a2x, a2y, b2x, b2y, c2x, c2y)) + return true; + if (PointInTriangle(b2x, b2y, a1x, a1y, b1x, b1y, c1x, c1y)) + return true; + if (PointInTriangle(c2x, c2y, a1x, a1y, b1x, b1y, c1x, c1y)) + return true; + return false; +} + } // namespace cubic } // namespace gpu2d } // namespace o3d diff --git a/o3d/core/cross/gpu2d/cubic_math_utils.h b/o3d/core/cross/gpu2d/cubic_math_utils.h index 0e03939..1051bb3 100644 --- a/o3d/core/cross/gpu2d/cubic_math_utils.h +++ b/o3d/core/cross/gpu2d/cubic_math_utils.h @@ -96,6 +96,17 @@ bool PointInTriangle(float px, float py, float bx, float by, float cx, float cy); +// Determines whether the triangles defined by the points (a1, b1, c1) +// and (a2, b2, c2) overlap. The definition of this function is that +// if the two triangles only share an adjacent edge or vertex, they +// are not considered to overlap. +bool TrianglesOverlap(float a1x, float a1y, + float b1x, float b1y, + float c1x, float c1y, + float a2x, float a2y, + float b2x, float b2y, + float c2x, float c2y); + } // namespace cubic } // namespace gpu2d } // namespace o3d diff --git a/o3d/core/cross/gpu2d/path_cache.cc b/o3d/core/cross/gpu2d/path_cache.cc new file mode 100644 index 0000000..9dec156 --- /dev/null +++ b/o3d/core/cross/gpu2d/path_cache.cc @@ -0,0 +1,105 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core/cross/gpu2d/path_cache.h" + +namespace o3d { +namespace gpu2d { + +unsigned int PathCache::num_vertices() const { + return vertices_.size() / 2; +} + +const float* PathCache::vertices() const { + if (num_vertices() == 0) + return NULL; + return &vertices_.front(); +} + +const float* PathCache::texcoords() const { + if (num_vertices() == 0) + return NULL; + return &texcoords_.front(); +} + +void PathCache::AddVertex(float x, float y, + float k, float l, float m) { + vertices_.push_back(x); + vertices_.push_back(y); + texcoords_.push_back(k); + texcoords_.push_back(l); + texcoords_.push_back(m); +} + +void PathCache::Clear() { + vertices_.clear(); + texcoords_.clear(); + interior_vertices_.clear(); +#ifdef O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + interior_edge_vertices_.clear(); +#endif // O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES +} + +unsigned int PathCache::num_interior_vertices() const { + return interior_vertices_.size() / 2; +} + +const float* PathCache::interior_vertices() const { + if (num_interior_vertices() == 0) + return NULL; + return &interior_vertices_.front(); +} + +void PathCache::AddInteriorVertex(float x, float y) { + interior_vertices_.push_back(x); + interior_vertices_.push_back(y); +} + +#ifdef O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES +unsigned int PathCache::num_interior_edge_vertices() const { + return interior_edge_vertices_.size() / 2; +} + +const float* PathCache::interior_edge_vertices() const { + if (num_interior_edge_vertices() == 0) + return NULL; + return &interior_edge_vertices_.front(); +} + +void PathCache::AddInteriorEdgeVertex(float x, float y) { + interior_edge_vertices_.push_back(x); + interior_edge_vertices_.push_back(y); +} +#endif // O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + +} // namespace gpu2d +} // namespace o3d + diff --git a/o3d/core/cross/gpu2d/path_cache.h b/o3d/core/cross/gpu2d/path_cache.h new file mode 100644 index 0000000..93ba86c0 --- /dev/null +++ b/o3d/core/cross/gpu2d/path_cache.h @@ -0,0 +1,120 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef O3D_CORE_CROSS_GPU2D_PATH_CACHE_H_ +#define O3D_CORE_CROSS_GPU2D_PATH_CACHE_H_ + +#include <vector> + +#include "base/basictypes.h" + +namespace o3d { +namespace gpu2d { + +// A cache of the processed triangle mesh for a given path. Because +// these might be expensive to allocate (using malloc/free +// internally), it is recommended to try to reuse them when possible. + +// Uncomment the following to obtain debugging information for the edges facing +// the interior region of the mesh. +// #define O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + +class PathCache { + public: + PathCache() { + } + + // The number of vertices in the mesh. + unsigned int num_vertices() const; + + // Get the base pointer to the vertex information. There are two + // coordinates per vertex. This pointer is valid until the cache is + // cleared or another vertex is added. Returns NULL if there are no + // vertices in the mesh. + const float* vertices() const; + + // Get the base pointer to the texture coordinate information. There + // are three coordinates per vertex. This pointer is valid until the + // cache is cleared or another vertex is added. Returns NULL if + // there are no vertices in the mesh. + const float* texcoords() const; + + // Adds a vertex's information to the cache. The first two arguments + // are the x and y coordinates of the vertex on the plane; the last + // three arguments are the cubic texture coordinates associated with + // this vertex. + void AddVertex(float x, float y, + float k, float l, float m); + + // The number of interior vertices. + unsigned int num_interior_vertices() const; + // Base pointer to the interior vertices; two coordinates per + // vertex, which can be drawn as GL_TRIANGLES. Returns NULL if there + // are no interior vertices in the mesh. + const float* interior_vertices() const; + // Adds an interior vertex to the cache. + void AddInteriorVertex(float x, float y); + + // Clears all of the stored vertex information in this cache. + void Clear(); + +#ifdef O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + // The number of interior edge vertices + unsigned int num_interior_edge_vertices() const; + // Base pointer to the interior vertices; two coordinates per + // vertex, which can be drawn as GL_LINES. Returns NULL if there are + // no interior edge vertices in the mesh. + const float* interior_edge_vertices() const; + void AddInteriorEdgeVertex(float x, float y); +#endif // O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + + private: + // The two-dimensional vertices of the triangle mesh. + std::vector<float> vertices_; + + // The three-dimensional cubic texture coordinates. + std::vector<float> texcoords_; + + std::vector<float> interior_vertices_; + +#ifdef O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + // The following is only for debugging + std::vector<float> interior_edge_vertices_; +#endif // O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + + DISALLOW_COPY_AND_ASSIGN(PathCache); +}; + +} // namespace gpu2d +} // namespace o3d + +#endif // O3D_CORE_CROSS_GPU2D_PATH_CACHE_H_ + diff --git a/o3d/core/cross/gpu2d/path_processor.cc b/o3d/core/cross/gpu2d/path_processor.cc new file mode 100644 index 0000000..ccc31df --- /dev/null +++ b/o3d/core/cross/gpu2d/path_processor.cc @@ -0,0 +1,1251 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core/cross/gpu2d/path_processor.h" + +#include <algorithm> + +#include "base/logging.h" +#include "core/cross/gpu2d/arena.h" +#include "core/cross/gpu2d/cubic_classifier.h" +#include "core/cross/gpu2d/cubic_math_utils.h" +#include "core/cross/gpu2d/cubic_texture_coords.h" +#include "core/cross/gpu2d/interval_tree.h" +#include "core/cross/gpu2d/local_triangulator.h" +#include "core/cross/gpu2d/path_cache.h" +#include "third_party/skia/include/core/SkGeometry.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkScalar.h" +#include "third_party/glu/internal_glu.h" + +namespace o3d { +namespace gpu2d { + +class Contour; + +//---------------------------------------------------------------------- +// min / max helpers +// + +template <typename T> +T min2(const T& v1, const T& v2) { + return std::min(v1, v2); +} + +template <typename T> +T max2(const T& v1, const T& v2) { + return std::max(v1, v2); +} + +template <typename T> +T min3(const T& v1, const T& v2, const T& v3) { + return min2(min2(v1, v2), v3); +} + +template <typename T> +T max3(const T& v1, const T& v2, const T& v3) { + return max2(max2(v1, v2), v3); +} + +template <typename T> +T min4(const T& v1, const T& v2, const T& v3, const T& v4) { + return min2(min2(v1, v2), min2(v3, v4)); +} + +template <typename T> +T max4(const T& v1, const T& v2, const T& v3, const T& v4) { + return max2(max2(v1, v2), max2(v3, v4)); +} + +//---------------------------------------------------------------------- +// BBox +// + +// Extremely simple bounding box class for Segments. +class BBox { + public: + BBox() + : min_x_(0), + min_y_(0), + max_x_(0), + max_y_(0) { + } + + // Initializes the parameters of the bounding box. + void Setup(float min_x, + float min_y, + float max_x, + float max_y) { + min_x_ = min_x; + min_y_ = min_y; + max_x_ = max_x; + max_y_ = max_y; + } + + // Initializes the bounding box to surround the given triangle. + void Setup(LocalTriangulator::Triangle* triangle) { + Setup(min3(triangle->get_vertex(0)->x(), + triangle->get_vertex(1)->x(), + triangle->get_vertex(2)->x()), + min3(triangle->get_vertex(0)->y(), + triangle->get_vertex(1)->y(), + triangle->get_vertex(2)->y()), + max3(triangle->get_vertex(0)->x(), + triangle->get_vertex(1)->x(), + triangle->get_vertex(2)->x()), + max3(triangle->get_vertex(0)->y(), + triangle->get_vertex(1)->y(), + triangle->get_vertex(2)->y())); + } + + float min_x() const { return min_x_; } + float min_y() const { return min_y_; } + float max_x() const { return max_x_; } + float max_y() const { return max_y_; } + + private: + float min_x_; + float min_y_; + float max_x_; + float max_y_; + DISALLOW_COPY_AND_ASSIGN(BBox); +}; + +//---------------------------------------------------------------------- +// Segment +// + +// Describes a segment of the path: either a cubic or a line segment. +// These are stored in a doubly linked list to speed up curve +// subdivision, which occurs due to either rendering artifacts in the +// loop case or due to overlapping triangles. +class Segment { + public: + // The kind of segment: cubic or line. + enum Kind { + kCubic, + kLine + }; + + // No-argument constructor allows construction by the Arena class. + Segment() + : arena_(NULL), + kind_(kCubic), + prev_(NULL), + next_(NULL), + contour_(NULL), + triangulator_(NULL), + marked_for_subdivision_(false) { + } + + // Initializer for cubic curve segments. + void Setup(Arena* arena, + Contour* contour, + SkPoint cp0, + SkPoint cp1, + SkPoint cp2, + SkPoint cp3) { + arena_ = arena; + contour_ = contour; + kind_ = kCubic; + points_[0] = cp0; + points_[1] = cp1; + points_[2] = cp2; + points_[3] = cp3; + ComputeBoundingBox(); + } + + // Initializer for line segments. + void Setup(Arena* arena, + Contour* contour, + SkPoint p0, + SkPoint p1) { + arena_ = arena; + contour_ = contour; + kind_ = kLine; + points_[0] = p0; + points_[1] = p1; + ComputeBoundingBox(); + } + + // Returns the kind of segment. + Kind kind() const { + return kind_; + } + + // Returns the i'th control point, 0 <= i < 4. + const SkPoint& get_point(int i) { + DCHECK(i >= 0 && i < 4); + return points_[i]; + } + + // Returns the next segment in the contour. + Segment* next() const { return next_; } + + // Returns the previous segment in the contour. + Segment* prev() const { return prev_; } + + // Sets the next segment in the contour. + void set_next(Segment* next) { next_ = next; } + + // Sets the previous segment in the contour. + void set_prev(Segment* prev) { prev_ = prev; } + + // The contour this segment belongs to. + Contour* contour() const { return contour_; } + + // Subdivides the current segment at the given parameter value (0 <= + // t <= 1) and replaces it with the two newly created Segments in + // the linked list, if possible. Returns a pointer to the leftmost + // Segment. + Segment* Subdivide(float param) { + SkPoint dst[7]; + SkChopCubicAt(points_, dst, param); + Segment* left = arena_->Alloc<Segment>(); + Segment* right = arena_->Alloc<Segment>(); + left->Setup(arena_, contour_, dst[0], dst[1], dst[2], dst[3]); + right->Setup(arena_, contour_, dst[3], dst[4], dst[5], dst[6]); + left->set_next(right); + right->set_prev(left); + // Try to set up a link between "this->prev()" and "left". + if (prev() != NULL) { + left->set_prev(prev()); + prev()->set_next(left); + } + // Try to set up a link between "this->next()" and "right". + Segment* n = next(); + if (n != NULL) { + right->set_next(n); + n->set_prev(right); + } + // Set up a link between "this" and "left"; this is only to + // provide a certain amount of continuity during forward iteration. + set_next(left); + return left; + } + + // Subdivides the current segment at the halfway point and replaces + // it with the two newly created Segments in the linked list, if + // possible. Returns a pointer to the leftmost Segment. + Segment* Subdivide() { + return Subdivide(0.5f); + } + + // Returns the bounding box of this segment. + const BBox& bbox() const { + return bbox_; + } + + // Computes the number of times a query line starting at the given + // point and extending to x=+infinity crosses this segment. + int NumCrossingsForXRay(SkPoint pt) const { + if (kind_ == kCubic) { + // Should consider caching the monotonic cubics. + return SkNumXRayCrossingsForCubic(pt, points_); + } else { + return SkXRayCrossesLine(pt, points_) ? 1 : 0; + } + } + + // Performs a local triangulation of the control points in this + // segment. This operation only makes sense for cubic type segments. + void Triangulate(bool compute_inside_edges, + CubicTextureCoords::Result* tex_coords); + + // Returns the number of control point triangles associated with + // this segment. + int num_triangles() const { + if (!triangulator_) + return 0; + return triangulator_->num_triangles(); + } + + // Fetches the given control point triangle for this segment. + LocalTriangulator::Triangle* get_triangle(int index) { + DCHECK(triangulator_); + return triangulator_->get_triangle(index); + } + + // Number of vertices along the inside edge of this segment. This + // can be called either for line or cubic type segments. + int num_interior_vertices() const { + if (kind_ == kCubic) { + if (triangulator_) { + return triangulator_->num_interior_vertices(); + } else { + return 0; + } + } + + return 2; + } + + // Returns the given interior vertex, 0 <= index < num_interior_vertices(). + SkPoint get_interior_vertex(int index) const { + DCHECK(index >= 0 && index < num_interior_vertices()); + if (kind_ == kCubic) { + SkPoint res; + if (triangulator_) { + LocalTriangulator::Vertex* vertex = + triangulator_->get_interior_vertex(index); + if (vertex) + res.set(SkFloatToScalar(vertex->x()), + SkFloatToScalar(vertex->y())); + } + return res; + } + + return points_[index]; + } + + // State to assist with curve subdivision. + bool marked_for_subdivision() { + return marked_for_subdivision_; + } + + // State to assist with curve subdivision. + void set_marked_for_subdivision(bool marked_for_subdivision) { + marked_for_subdivision_ = marked_for_subdivision; + } + + private: + // Computes the bounding box of this Segment. + void ComputeBoundingBox() { + switch (kind_) { + case kCubic: + bbox_.Setup(SkScalarToFloat(min4(points_[0].fX, + points_[1].fX, + points_[2].fX, + points_[3].fX)), + SkScalarToFloat(min4(points_[0].fY, + points_[1].fY, + points_[2].fY, + points_[3].fY)), + SkScalarToFloat(max4(points_[0].fX, + points_[1].fX, + points_[2].fX, + points_[3].fX)), + SkScalarToFloat(max4(points_[0].fY, + points_[1].fY, + points_[2].fY, + points_[3].fY))); + break; + + case kLine: + bbox_.Setup(SkScalarToFloat(min2(points_[0].fX, + points_[1].fX)), + SkScalarToFloat(min2(points_[0].fY, + points_[1].fY)), + SkScalarToFloat(max2(points_[0].fX, + points_[1].fX)), + SkScalarToFloat(max2(points_[0].fY, + points_[1].fY))); + break; + + default: + NOTREACHED(); + break; + } + } + + Arena* arena_; + Kind kind_; + SkPoint points_[4]; + Segment* prev_; + Segment* next_; + Contour* contour_; + BBox bbox_; + LocalTriangulator* triangulator_; + bool marked_for_subdivision_; + + DISALLOW_COPY_AND_ASSIGN(Segment); +}; + +//---------------------------------------------------------------------- +// Contour +// + +// Describes a closed contour of the path. +class Contour { + public: + Contour() { + first_ = &sentinel_; + first_->set_next(first_); + first_->set_prev(first_); + ccw_ = true; + fill_right_side_ = true; + } + + // Adds a segment to this contour. + void Add(Segment* segment) { + if (first_ == &sentinel_) { + // First element is the sentinel. Replace it with the incoming + // segment. + segment->set_next(first_); + segment->set_prev(first_); + first_->set_next(segment); + first_->set_prev(segment); + first_ = segment; + } else { + // first_->prev() is the sentinel. + Segment* sentinel = first_->prev(); + Segment* last = sentinel->prev(); + last->set_next(segment); + segment->set_prev(last); + segment->set_next(sentinel); + sentinel->set_prev(segment); + } + } + + // Subdivides the given segment at the given parametric value. + // Returns a pointer to the first of the two portions of the + // subdivided segment. + Segment* Subdivide(Segment* segment, float param) { + Segment* left = segment->Subdivide(param); + if (first_ == segment) + first_ = left; + return left; + } + + // Subdivides the given segment at the halfway point. Returns a + // pointer to the first of the two portions of the subdivided + // segment. + Segment* Subdivide(Segment* segment) { + Segment* left = segment->Subdivide(); + if (first_ == segment) + first_ = left; + return left; + } + + // Returns the first segment in the contour for iteration. + Segment* begin() const { + return first_; + } + + // Returns the last segment in the contour for iteration. Callers + // should not iterate over this segment. In other words: + // for (Segment* cur = contour->begin(); + // cur != contour->end(); + // cur = cur->next()) { + // // .. process cur ... + // } + Segment* end() const { + return first_->prev(); + } + + // Returns whether this contour is oriented counterclockwise. + bool ccw() const { + return ccw_; + } + + void set_ccw(bool ccw) { + ccw_ = ccw; + } + + // Returns whether the right side of this contour is filled. + bool fill_right_side() const { return fill_right_side_; } + + void set_fill_right_side(bool fill_right_side) { + fill_right_side_ = fill_right_side; + } + + private: + // The start of the segment chain. The segments are kept in a + // circular doubly linked list for rapid access to the beginning and + // end. + Segment* first_; + + // The sentinel element at the end of the chain, needed for + // reasonable iteration semantics. + Segment sentinel_; + + // Whether this contour is oriented counterclockwise. + bool ccw_; + + // Whether we should fill the right (or left) side of this contour. + bool fill_right_side_; + + DISALLOW_COPY_AND_ASSIGN(Contour); +}; + +//---------------------------------------------------------------------- +// Segment +// + +// Definition of Segment::Triangulate(), which must come after +// declaration of Contour. +void Segment::Triangulate(bool compute_inside_edges, + CubicTextureCoords::Result* tex_coords) { + DCHECK(kind_ == kCubic); + if (triangulator_ == NULL) { + triangulator_ = arena_->Alloc<LocalTriangulator>(); + } + triangulator_->Reset(); + for (int i = 0; i < 4; i++) { + LocalTriangulator::Vertex* vertex = triangulator_->get_vertex(i); + if (tex_coords) { + vertex->Set(get_point(i).fX, + get_point(i).fY, + tex_coords->coords[i].getX(), + tex_coords->coords[i].getY(), + tex_coords->coords[i].getZ()); + } else { + vertex->Set(get_point(i).fX, + get_point(i).fY, + // No texture coordinates yet + 0, 0, 0); + } + } + triangulator_->Triangulate(compute_inside_edges, + contour()->fill_right_side()); +} + +//---------------------------------------------------------------------- +// PathProcessor +// + +PathProcessor::PathProcessor() + : arena_(new Arena()), + should_delete_arena_(true), + verbose_logging_(false) { +} + +PathProcessor::PathProcessor(Arena* arena) + : arena_(arena), + should_delete_arena_(false), + verbose_logging_(false) { +} + +PathProcessor::~PathProcessor() { + if (should_delete_arena_) { + delete arena_; + } +} + +void PathProcessor::Process(const SkPath& path, PathCache* cache) { + BuildContours(path); + + // Run plane-sweep algorithm to determine overlaps of control point + // curves and subdivide curves appropriately. + SubdivideCurves(); + + // Determine orientations of countours. Based on orientation and the + // number of curve crossings at a random point on the contour, + // determine whether to fill the left or right side of the contour. + DetermineSidesToFill(); + + // Classify curves, compute texture coordinates and subdivide as + // necessary to eliminate rendering artifacts. Do the final + // triangulation of the curve segments, determining the path along + // the interior of the shape. + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + if (seg->kind() == Segment::kCubic) { + CubicClassifier::Result classification = + CubicClassifier::Classify(seg->get_point(0).fX, + seg->get_point(0).fY, + seg->get_point(1).fX, + seg->get_point(1).fY, + seg->get_point(2).fX, + seg->get_point(2).fY, + seg->get_point(3).fX, + seg->get_point(3).fY); + DLOG_IF(INFO, verbose_logging_) << "Classification:" + << classification.curve_type(); + CubicTextureCoords::Result tex_coords; + CubicTextureCoords::Compute(classification, + cur->fill_right_side(), + &tex_coords); + if (tex_coords.has_rendering_artifact) { + // TODO(kbr): split at the subdivision parameter value + cur->Subdivide(seg); + // Next iteration will handle the newly subdivided halves + } else { + if (!tex_coords.is_line_or_point) { + seg->Triangulate(true, &tex_coords); + for (int i = 0; i < seg->num_triangles(); i++) { + LocalTriangulator::Triangle* triangle = + seg->get_triangle(i); + for (int j = 0; j < 3; j++) { + LocalTriangulator::Vertex* vert = + triangle->get_vertex(j); + cache->AddVertex(vert->x(), + vert->y(), + vert->k(), + vert->l(), + vert->m()); + } + } +#ifdef O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + // Show the end user the interior edges as well + for (int i = 1; i < seg->num_interior_vertices(); i++) { + SkPoint vert = seg->get_interior_vertex(i); + // Duplicate previous vertex to be able to draw GL_LINES + SkPoint prev = seg->get_interior_vertex(i - 1); + cache->AddInteriorEdgeVertex(prev.fX, prev.fY); + cache->AddInteriorEdgeVertex(vert.fX, vert.fY); + } +#endif // O3D_CORE_CROSS_GPU2D_PATH_CACHE_DEBUG_INTERIOR_EDGES + } + } + } + } + } + + // Run the interior paths through a tessellation algorithm + // supporting multiple contours. + TessellateInterior(cache); +} + +void PathProcessor::BuildContours(const SkPath& path) { + // Clear out the contours + contours_.clear(); + SkPath::Iter iter(path, false); + SkPoint pts[4]; + SkPath::Verb verb; + Contour* contour = NULL; + SkPoint cur_pt; + SkPoint move_to_pt; + do { + verb = iter.next(pts); + if (verb != SkPath::kMove_Verb) { + if (contour == NULL) { + contour = arena_->Alloc<Contour>(); + contours_.push_back(contour); + } + } + switch (verb) { + case SkPath::kMove_Verb: { + contour = arena_->Alloc<Contour>(); + contours_.push_back(contour); + cur_pt = pts[0]; + move_to_pt = pts[0]; + DLOG_IF(INFO, verbose_logging_) << "MoveTo (" << pts[0].fX + << ", " << pts[0].fY << ")"; + break; + } + case SkPath::kLine_Verb: { + Segment* segment = arena_->Alloc<Segment>(); + if (iter.isCloseLine()) { + segment->Setup(arena_, contour, cur_pt, pts[1]); + DLOG_IF(INFO, verbose_logging_) << "CloseLineTo (" << cur_pt.fX + << ", " << cur_pt.fY + << "), (" << pts[1].fX + << ", " << pts[1].fY << ")"; + contour->Add(segment); + contour = NULL; + } else { + segment->Setup(arena_, contour, pts[0], pts[1]); + DLOG_IF(INFO, verbose_logging_) << "LineTo (" << pts[0].fX + << ", " << pts[0].fY + << "), (" << pts[1].fX + << ", " << pts[1].fY << ")"; + contour->Add(segment); + cur_pt = pts[1]; + } + break; + } + case SkPath::kQuad_Verb: { + // Need to degree elevate the quadratic into a cubic + SkPoint cubic[4]; + SkConvertQuadToCubic(pts, cubic); + Segment* segment = arena_->Alloc<Segment>(); + segment->Setup(arena_, contour, + cubic[0], cubic[1], cubic[2], cubic[3]); + DLOG_IF(INFO, verbose_logging_) << "Quad->CubicTo (" << cubic[0].fX + << ", " << cubic[0].fY + << "), (" << cubic[1].fX + << ", " << cubic[1].fY + << "), (" << cubic[2].fX + << ", " << cubic[2].fY + << "), (" << cubic[3].fX + << ", " << cubic[3].fY << ")"; + contour->Add(segment); + cur_pt = cubic[3]; + break; + } + case SkPath::kCubic_Verb: { + Segment* segment = arena_->Alloc<Segment>(); + segment->Setup(arena_, contour, pts[0], pts[1], pts[2], pts[3]); + DLOG_IF(INFO, verbose_logging_) << "CubicTo (" << pts[0].fX + << ", " << pts[0].fY + << "), (" << pts[1].fX + << ", " << pts[1].fY + << "), (" << pts[2].fX + << ", " << pts[2].fY + << "), (" << pts[3].fX + << ", " << pts[3].fY << ")"; + contour->Add(segment); + cur_pt = pts[3]; + break; + } + case SkPath::kClose_Verb: { + Segment* segment = arena_->Alloc<Segment>(); + segment->Setup(arena_, contour, cur_pt, move_to_pt); + DLOG_IF(INFO, verbose_logging_) << "Close (" << cur_pt.fX + << ", " << cur_pt.fY + << ") -> (" << move_to_pt.fX + << ", " << move_to_pt.fY << ")"; + contour->Add(segment); + contour = NULL; + } + default: + break; + } + } while (verb != SkPath::kDone_Verb); +} + +std::vector<Segment*> PathProcessor::AllSegmentsOverlappingY(float y) { + std::vector<Segment*> res; + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + const BBox& bbox = seg->bbox(); + if (bbox.min_y() <= y && y <= bbox.max_y()) { + res.push_back(seg); + } + } + } + return res; +} + +// Uncomment this to debug the orientation computation +// #define O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_DEBUG_ORIENTATION + +void PathProcessor::DetermineSidesToFill() { + // Loop and Blinn's algorithm can only easily emulate the even/odd + // fill rule, and only for non-intersecting curves. We can determine + // which side of each curve segment to fill based on its + // clockwise/counterclockwise orientation and how many other + // contours surround it. + + // To optimize the query of all curve segments intersecting a + // horizontal line going to x=+infinity, we build up an interval + // tree whose keys are the y extents of the segments. + IntervalTree<float, Segment*> tree(arena_); + typedef IntervalTree<float, Segment*>::IntervalType IntervalType; + + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + DetermineOrientation(cur); + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + const BBox& bbox = seg->bbox(); + tree.Insert(tree.MakeInterval(bbox.min_y(), bbox.max_y(), seg)); + } + } + + // Now iterate through the contours and pick a random segment (in + // this case we use the first) and a random point on that segment. + // Find all segments from other contours which intersect this one + // and count the number of crossings a horizontal line to + // x=+infinity makes with those contours. This combined with the + // orientation of the curve tells us which side to fill -- again, + // assuming an even/odd fill rule, which is all we can easily + // handle. + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + Segment* seg = cur->begin(); + + int num_crossings = 0; + + // We use a zero-sized vertical interval for the query + std::vector<IntervalType> overlaps = + tree.AllOverlaps(IntervalType(SkScalarToFloat(seg->get_point(0).fY), + SkScalarToFloat(seg->get_point(0).fY), + NULL)); +#if !defined(O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_DEBUG_ORIENTATION) + for (std::vector<IntervalType>::iterator iter = overlaps.begin(); + iter != overlaps.end(); + iter++) { + const IntervalType& interval = *iter; + Segment* query_seg = interval.data(); + // Ignore segments coming from the same contour + if (query_seg->contour() != cur) { + num_crossings += query_seg->NumCrossingsForXRay(seg->get_point(0)); + } + } +#endif // !defined(O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_DEBUG_ORIENTATION) + +#ifdef O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_DEBUG_ORIENTATION + // For debugging + std::vector<Segment*> slow_overlaps = + AllSegmentsOverlappingY(seg->get_point(0).fY); + DCHECK(overlaps.size() == slow_overlaps.size()); + for (std::vector<Segment*>::iterator iter = slow_overlaps.begin(); + iter != slow_overlaps.end(); + iter++) { + Segment* query_seg = *iter; + // Ignore segments coming from the same contour + if (query_seg->contour() != cur) { + num_crossings += query_seg->NumCrossingsForXRay(seg->get_point(0)); + } + } +#endif // O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_DEBUG_ORIENTATION + + if (cur->ccw()) { + if (num_crossings & 1) { + cur->set_fill_right_side(true); + } else { + cur->set_fill_right_side(false); + } + } else { + if (num_crossings & 1) { + cur->set_fill_right_side(false); + } else { + cur->set_fill_right_side(true); + } + } + } +} + +void PathProcessor::DetermineOrientation(Contour* contour) { + // Determine signed area of the polygon represented by the points + // along the segments. Consider this an approximation to the true + // orientation of the polygon; it probably won't handle + // self-intersecting curves correctly. + // + // There is also a pretty basic assumption here that the contour is + // closed. + float signed_area = 0; + for (Segment* seg = contour->begin(); + seg != contour->end(); + seg = seg->next()) { + int limit = (seg->kind() == Segment::kCubic) ? 4 : 2; + for (int i = 1; i < limit; i++) { + const SkPoint& prev_point = seg->get_point(i - 1); + const SkPoint& point = seg->get_point(i); + float cur_area = prev_point.fX * point.fY - prev_point.fY * point.fX; + DLOG_IF(INFO, verbose_logging_) << "Adding to signed area (" + << prev_point.fX << ", " + << prev_point.fY << ") -> (" + << point.fX << ", " + << point.fY << ") = " + << cur_area; + signed_area += cur_area; + } + } + + if (signed_area > 0) + contour->set_ccw(true); + else + contour->set_ccw(false); +} + +//---------------------------------------------------------------------- +// Classes and typedefs needed for curve subdivision. +// Unfortunately it appears we can't scope these within the +// SubdivideCurves() method itself, because templates then fail to +// instantiate. + +// The user data which is placed in the IntervalTree. +struct SweepData { + SweepData() + : triangle(NULL), + segment(NULL) { + } + + // The triangle this interval is associated with + LocalTriangulator::Triangle* triangle; + // The segment the triangle is associated with + Segment* segment; +}; + +typedef IntervalTree<float, SweepData*> SweepTree; +typedef SweepTree::IntervalType SweepInterval; + +// The entry / exit events which occur at the minimum and maximum x +// coordinates of the control point triangles' bounding boxes. +// +// Note that this class requires its copy constructor and assignment +// operator since it needs to be stored in a std::vector. +class SweepEvent { + public: + SweepEvent() + : x_(0), + entry_(false), + interval_(0, 0, NULL) { + } + + // Initializes the SweepEvent. + void Setup(float x, bool entry, SweepInterval interval) { + x_ = x; + entry_ = entry; + interval_ = interval; + } + + float x() const { return x_; } + bool entry() const { return entry_; } + const SweepInterval& interval() const { return interval_; } + + bool operator<(const SweepEvent& other) const { + return x_ < other.x_; + } + + private: + float x_; + bool entry_; + SweepInterval interval_; +}; + +namespace { + +bool TrianglesOverlap(LocalTriangulator::Triangle* t0, + LocalTriangulator::Triangle* t1) { + LocalTriangulator::Vertex* t0v0 = t0->get_vertex(0); + LocalTriangulator::Vertex* t0v1 = t0->get_vertex(1); + LocalTriangulator::Vertex* t0v2 = t0->get_vertex(2); + LocalTriangulator::Vertex* t1v0 = t1->get_vertex(0); + LocalTriangulator::Vertex* t1v1 = t1->get_vertex(1); + LocalTriangulator::Vertex* t1v2 = t1->get_vertex(2); + return cubic::TrianglesOverlap(t0v0->x(), t0v0->y(), + t0v1->x(), t0v1->y(), + t0v2->x(), t0v2->y(), + t1v0->x(), t1v0->y(), + t1v1->x(), t1v1->y(), + t1v2->x(), t1v2->y()); +} + +} // anonymous namespace + +void PathProcessor::SubdivideCurves() { + // We need to determine all overlaps of all control point triangles + // (from different segments, not the same segment) and, if any + // exist, subdivide the associated curves. + // + // The plane-sweep algorithm determines all overlaps of a set of + // rectangles in the 2D plane. Our problem maps very well to this + // algorithm and significantly reduces the complexity compared to a + // naive implementation. + // + // Each bounding box of a control point triangle is converted into + // an "entry" event at its smallest X coordinate and an "exit" event + // at its largest X coordinate. Each event has an associated + // one-dimensional interval representing the Y span of the bounding + // box. We sort these events by increasing X coordinate. We then + // iterate through them. For each entry event we add the interval to + // a side interval tree, and query this tree for overlapping + // intervals. Any overlapping interval corresponds to an overlapping + // bounding box. For each exit event we remove the associated + // interval from the interval tree. + + std::vector<Segment*> cur_segments; + std::vector<Segment*> next_segments; + + // Start things off by considering all of the segments + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + if (seg->kind() == Segment::kCubic) { + seg->Triangulate(false, NULL); + cur_segments.push_back(seg); + } + } + } + + // Subdivide curves at most this many times + const int kMaxIter = 5; + std::vector<SweepInterval> overlaps; + + for (int cur_iter = 0; cur_iter < kMaxIter; ++cur_iter) { + if (cur_segments.size() == 0) { + // Done + break; + } + + std::vector<SweepEvent> events; + SweepTree tree(arena_); + for (std::vector<Segment*>::iterator iter = cur_segments.begin(); + iter != cur_segments.end(); + iter++) { + Segment* seg = *iter; + if (seg->kind() == Segment::kCubic) { + for (int i = 0; i < seg->num_triangles(); i++) { + LocalTriangulator::Triangle* triangle = seg->get_triangle(i); + BBox bbox; + bbox.Setup(triangle); + // Ignore zero-width triangles to avoid issues with + // coincident entry and exit events for the same triangle + if (bbox.max_x() > bbox.min_x()) { + SweepData* data = arena_->Alloc<SweepData>(); + data->triangle = triangle; + data->segment = seg; + SweepInterval interval = + tree.MakeInterval(bbox.min_y(), bbox.max_y(), data); + // Add entry and exit events + SweepEvent event; + event.Setup(bbox.min_x(), true, interval); + events.push_back(event); + event.Setup(bbox.max_x(), false, interval); + events.push_back(event); + } + } + } + } + + // Sort events by increasing X coordinate + std::sort(events.begin(), events.end()); + + // Now iterate through the events + for (std::vector<SweepEvent>::iterator iter = events.begin(); + iter != events.end(); + iter++) { + SweepEvent event = *iter; + if (event.entry()) { + // Add this interval into the tree + tree.Insert(event.interval()); + // See whether the associated segment has been subdivided yet + if (!event.interval().data()->segment->marked_for_subdivision()) { + // Query the tree + overlaps.clear(); + tree.AllOverlaps(event.interval(), overlaps); + // Now see exactly which triangles overlap this one + for (std::vector<SweepInterval>::iterator iter = overlaps.begin(); + iter != overlaps.end(); + iter++) { + SweepInterval overlap = *iter; + // Only pay attention to overlaps from a different Segment + if (event.interval().data()->segment != overlap.data()->segment) { + // See whether the triangles actually overlap + if (TrianglesOverlap(event.interval().data()->triangle, + overlap.data()->triangle)) { + // Actually subdivide the segments. + // Each one might already have been subdivided. + Segment* seg = event.interval().data()->segment; + ConditionallySubdivide(seg, &next_segments); + seg = overlap.data()->segment; + ConditionallySubdivide(seg, &next_segments); + } + } + } + } + } else { + // Remove this interval from the tree + tree.Delete(event.interval()); + } + } + + cur_segments = next_segments; + next_segments.clear(); + } +} + +void PathProcessor::ConditionallySubdivide( + Segment* seg, std::vector<Segment*>* next_segments) { + if (!seg->marked_for_subdivision()) { + seg->set_marked_for_subdivision(true); + Segment* next = seg->contour()->Subdivide(seg); + // Triangulate the newly subdivided segments. + next->Triangulate(false, NULL); + next->next()->Triangulate(false, NULL); + // Add them for the next iteration. + next_segments->push_back(next); + next_segments->push_back(next->next()); + } +} + +void PathProcessor::SubdivideCurvesSlow() { + // Alternate, significantly slower algorithm for curve subdivision + // for use in debugging. + std::vector<Segment*> cur_segments; + std::vector<Segment*> next_segments; + + // Start things off by considering all of the segments + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + if (seg->kind() == Segment::kCubic) { + seg->Triangulate(false, NULL); + cur_segments.push_back(seg); + } + } + } + + // Subdivide curves at most this many times + const int kMaxIter = 5; + + for (int cur_iter = 0; cur_iter < kMaxIter; ++cur_iter) { + if (cur_segments.size() == 0) { + // Done + break; + } + + for (std::vector<Segment*>::iterator iter = cur_segments.begin(); + iter != cur_segments.end(); + iter++) { + Segment* seg = *iter; + if (seg->kind() == Segment::kCubic) { + for (std::vector<Segment*>::iterator iter2 = cur_segments.begin(); + iter2 != cur_segments.end(); + iter2++) { + Segment* seg2 = *iter2; + if (seg2->kind() == Segment::kCubic && + seg != seg2) { + for (int i = 0; i < seg->num_triangles(); i++) { + LocalTriangulator::Triangle* triangle = seg->get_triangle(i); + for (int j = 0; j < seg2->num_triangles(); j++) { + LocalTriangulator::Triangle* triangle2 = seg2->get_triangle(j); + if (TrianglesOverlap(triangle, triangle2)) { + ConditionallySubdivide(seg, &next_segments); + ConditionallySubdivide(seg2, &next_segments); + } + } + } + } + } + } + } + + cur_segments = next_segments; + next_segments.clear(); + } +} + +//---------------------------------------------------------------------- +// Structures and callbacks for tessellation of the interior region of +// the contours. + +// The user data for the GLU tessellator. +struct TessellationState { + PathCache* cache; + std::vector<void*> allocated_pointers; +}; + +namespace { + +void VertexCallback(void* vertex_data, void* data) { + TessellationState* state = static_cast<TessellationState*>(data); + PathCache* cache = state->cache; + GLdouble* location = static_cast<GLdouble*>(vertex_data); + cache->AddInteriorVertex(static_cast<float>(location[0]), + static_cast<float>(location[1])); +} + +void CombineCallback(GLdouble coords[3], void* vertex_data[4], + GLfloat weight[4], void** out_data, + void* polygon_data) { + TessellationState* state = static_cast<TessellationState*>(polygon_data); + GLdouble* out_vertex = static_cast<GLdouble*>(malloc(3 * sizeof(GLdouble))); + state->allocated_pointers.push_back(out_vertex); + out_vertex[0] = coords[0]; + out_vertex[1] = coords[1]; + out_vertex[2] = coords[2]; + *out_data = out_vertex; +} + +void EdgeFlagCallback(GLboolean flag) { + // No-op just to prevent triangle strips and fans from being passed + // to us +} + +} // anonymous namespace + +void PathProcessor::TessellateInterior(PathCache* cache) { + // Because the GLU tessellator requires its input in + // double-precision format, we need to make a separate copy of the + // data. + std::vector<GLdouble> vertex_data; + std::vector<size_t> contour_endings; + // For avoiding adding coincident vertices. + float cur_x = 0, cur_y = 0; + for (std::vector<Contour*>::iterator iter = contours_.begin(); + iter != contours_.end(); + iter++) { + Contour* cur = *iter; + bool first = true; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + int num_interior_vertices = seg->num_interior_vertices(); + for (int i = 0; i < num_interior_vertices - 1; i++) { + SkPoint point = seg->get_interior_vertex(i); + if (first) { + first = false; + vertex_data.push_back(point.fX); + vertex_data.push_back(point.fY); + vertex_data.push_back(0); + cur_x = point.fX; + cur_y = point.fY; + } else if (point.fX != cur_x || point.fY != cur_y) { + vertex_data.push_back(point.fX); + vertex_data.push_back(point.fY); + vertex_data.push_back(0); + cur_x = point.fX; + cur_y = point.fY; + } + } + } + contour_endings.push_back(vertex_data.size()); + } + // Now that we have all of the vertex data in a stable location in + // memory, call the tessellator. + GLUtesselator* tess = internal_gluNewTess(); + TessellationState state; + state.cache = cache; + internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, + reinterpret_cast<GLvoid (*)()>(VertexCallback)); + internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, + reinterpret_cast<GLvoid (*)()>(CombineCallback)); + internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG, + reinterpret_cast<GLvoid (*)()>(EdgeFlagCallback)); + internal_gluTessBeginPolygon(tess, &state); + internal_gluTessBeginContour(tess); + GLdouble* base = &vertex_data.front(); + int contour_idx = 0; + for (size_t i = 0; i < vertex_data.size(); i += 3) { + if (i == contour_endings[contour_idx]) { + internal_gluTessEndContour(tess); + internal_gluTessBeginContour(tess); + ++contour_idx; + } + internal_gluTessVertex(tess, &base[i], &base[i]); + } + internal_gluTessEndContour(tess); + internal_gluTessEndPolygon(tess); + for (size_t i = 0; i < state.allocated_pointers.size(); i++) { + free(state.allocated_pointers[i]); + } + internal_gluDeleteTess(tess); +} + +} // namespace gpu2d +} // namespace o3d + diff --git a/o3d/core/cross/gpu2d/path_processor.h b/o3d/core/cross/gpu2d/path_processor.h new file mode 100644 index 0000000..f1af232 --- /dev/null +++ b/o3d/core/cross/gpu2d/path_processor.h @@ -0,0 +1,126 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// The main entry point for Loop and Blinn's GPU accelerated curve +// rendering algorithm. + +#ifndef O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_H_ +#define O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_H_ + +#include <vector> + +#include "base/basictypes.h" + +class SkPath; + +namespace o3d { +namespace gpu2d { + +class Arena; +class Contour; +class PathCache; +class Segment; + +// The PathProcessor turns an SkPath (assumed to contain one or more +// closed regions) into a set of exterior and interior triangles, +// stored in the PathCache. The exterior triangles have associated 3D +// texture coordinates which are used to evaluate the curve's +// inside/outside function on a per-pixel basis. The interior +// triangles are filled with 100% opacity. +// +// Note that the fill style and management of multiple layers are +// separate concerns, handled at a higher level with shaders and +// polygon offsets. +class PathProcessor { + public: + PathProcessor(); + explicit PathProcessor(Arena* arena); + ~PathProcessor(); + + // Transforms the given path into a triangle mesh for rendering + // using Loop and Blinn's shader, placing the result into the given + // PathCache. + void Process(const SkPath& path, PathCache* cache); + + // Enables or disables verbose logging in debug mode. + void set_verbose_logging(bool on_or_off); + + private: + // Builds a list of contours for the given path. + void BuildContours(const SkPath& path); + + // Determines whether the left or right side of each contour should + // be filled. + void DetermineSidesToFill(); + + // Determines whether the given (closed) contour is oriented + // clockwise or counterclockwise. + void DetermineOrientation(Contour* contour); + + // Subdivides the curves so that there are no overlaps of the + // triangles associated with the curves' control points. + void SubdivideCurves(); + + // Helper function used during curve subdivision. + void ConditionallySubdivide(Segment* seg, + std::vector<Segment*>* next_segments); + + // Tessellates the interior regions of the contours. + void TessellateInterior(PathCache* cache); + + // For debugging the orientation computation. Returns all of the + // segments overlapping the given Y coordinate. + std::vector<Segment*> AllSegmentsOverlappingY(float y); + + // For debugging the curve subdivision algorithm. Subdivides the + // curves using an alternate, slow (O(n^3)) algorithm. + void SubdivideCurvesSlow(); + + // Arena from which to allocate temporary objects. + Arena* arena_; + + // Whether to delete the arena upon deletion of the PathProcessor. + bool should_delete_arena_; + + // The contours described by the path. + std::vector<Contour*> contours_; + + // Whether or not to perform verbose logging in debug mode. + bool verbose_logging_; + + DISALLOW_COPY_AND_ASSIGN(PathProcessor); +}; + +} // namespace gpu2d +} // namespace o3d + +#endif // O3D_CORE_CROSS_GPU2D_PATH_PROCESSOR_H_ + diff --git a/o3d/core/cross/primitive.cc b/o3d/core/cross/primitive.cc index d223df0..e2dad14 100644 --- a/o3d/core/cross/primitive.cc +++ b/o3d/core/cross/primitive.cc @@ -182,8 +182,8 @@ class FieldReadAccessor { return *reinterpret_cast<T*>(reinterpret_cast<char*>(data_) + offset_ + stride_ * (translated_index + real_start_index_)); } - void Initialize(const Field& field, unsigned int start_index, - unsigned int length) { + virtual void Initialize(const Field& field, unsigned int start_index, + unsigned int length) { buffer_ = field.buffer(); locked_ = false; data_ = NULL; @@ -225,6 +225,42 @@ class FieldReadAccessor { DISALLOW_COPY_AND_ASSIGN(FieldReadAccessor); }; +// Specialization which pads out the fetched coordinates with zeros, +// to be able to handle 2D Position streams. +class FieldReadAccessorPoint3 : public FieldReadAccessor<Point3> { + public: + FieldReadAccessorPoint3() + : FieldReadAccessor<Point3>(), + num_components_(0), + cache_index_(0) { } + + virtual void Initialize(const Field& field, unsigned int start_index, + unsigned int length) { + FieldReadAccessor<Point3>::Initialize(field, start_index, length); + num_components_ = field.num_components(); + } + + virtual Point3& operator[](unsigned int translated_index) { + Point3& tmp = FieldReadAccessor<Point3>::operator[](translated_index); + Point3& cur = cache_[cache_index_]; + cache_index_ = (cache_index_ + 1) % 3; + for (unsigned int i = 0; i < num_components_; i++) { + cur[i] = tmp[i]; + } + for (unsigned int i = num_components_; i < 3; i++) { + cur[i] = 0; + } + return cur; + } + + protected: + unsigned int num_components_; + // We need a cache of the three most recently fetched vertices to + // match the usage elsewhere in this file + int cache_index_; + Point3 cache_[3]; +}; + class FieldReadAccessorUnsignedInt : public FieldReadAccessor<unsigned int> { public: FieldReadAccessorUnsignedInt() @@ -257,18 +293,18 @@ class FieldReadAccessorUnsignedInt : public FieldReadAccessor<unsigned int> { }; // Attempts to initialize a FieldReadAccessor for the stream of vertices of -// the primitive. Returns success. -bool GetVerticesAccessor(const Primitive* primitive, - int position_stream_index, - FieldReadAccessor<Point3>* accessor) { - if (!(accessor && primitive)) - return false; +// the primitive. Returns newly allocated accessor which must be deleted +// by caller, or NULL upon failure. +FieldReadAccessor<Point3>* GetVerticesAccessor(const Primitive* primitive, + int position_stream_index) { + if (!primitive) + return NULL; const StreamBank* stream_bank = primitive->stream_bank(); if (!stream_bank) { O3D_ERROR(primitive->service_locator()) << "No stream bank on Primitive '" << primitive->name() << "'"; - return false; + return NULL; } const Stream* vertex_stream = stream_bank->GetVertexStream( @@ -279,28 +315,33 @@ bool GetVerticesAccessor(const Primitive* primitive, O3D_ERROR(primitive->service_locator()) << "No POSITION stream index " << position_stream_index; - return false; + return NULL; } const Field& field = vertex_stream->field(); if (!field.buffer()) { O3D_ERROR(primitive->service_locator()) << "Vertex Buffer not set"; - return false; + return NULL; } if (!field.IsA(FloatField::GetApparentClass())) { O3D_ERROR(primitive->service_locator()) << "POSITION stream index " << position_stream_index << " is not a FLOAT stream"; - return false; + return NULL; } - if (field.num_components() != 3) { - O3D_ERROR(primitive->service_locator()) - << "POSITION stream index " << position_stream_index - << " does not have 3 components"; - return false; + // Don't even try to lock fields that don't have any data + if (vertex_stream->GetMaxVertices() == 0) { + return NULL; + } + + FieldReadAccessor<Point3>* accessor; + if (field.num_components() == 3) { + accessor = new FieldReadAccessor<Point3>(); + } else { + accessor = new FieldReadAccessorPoint3(); } accessor->Initialize(field, vertex_stream->start_index(), @@ -309,10 +350,11 @@ bool GetVerticesAccessor(const Primitive* primitive, if (!accessor->Valid()) { O3D_ERROR(primitive->service_locator()) << "Could not lock vertex buffer"; - return false; + delete accessor; + return NULL; } - return true; + return accessor; } // Attempts to initialize a FieldReadAccessor for the stream of indices of @@ -347,11 +389,12 @@ bool Primitive::WalkPolygons( PolygonFunctor* polygon_functor) const { DLOG_ASSERT(polygon_functor); - FieldReadAccessor<Point3> vertices; FieldReadAccessorUnsignedInt indices; - - if (!GetVerticesAccessor(this, position_stream_index, &vertices)) + scoped_ptr<FieldReadAccessor<Point3> > vertices_pointer( + GetVerticesAccessor(this, position_stream_index)); + if (vertices_pointer.get() == NULL) return false; + FieldReadAccessor<Point3>& vertices = *vertices_pointer; unsigned int index_count; if (indexed()) { diff --git a/o3d/core/cross/processed_path.cc b/o3d/core/cross/processed_path.cc new file mode 100644 index 0000000..ef5ab4e --- /dev/null +++ b/o3d/core/cross/processed_path.cc @@ -0,0 +1,159 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This file contains the definition of ProcessedPath. + +#include "core/cross/processed_path.h" + +#include "core/cross/buffer.h" +#include "core/cross/field.h" +#include "core/cross/gpu2d/path_processor.h" + +namespace o3d { + +O3D_DEFN_CLASS(ProcessedPath, ObjectBase); + +ProcessedPath::ProcessedPath(ServiceLocator* service_locator) + : ObjectBase(service_locator) { +} + +ObjectBase::Ref ProcessedPath::Create(ServiceLocator* service_locator) { + return ObjectBase::Ref( + ProcessedPath::Ref(new ProcessedPath(service_locator))); +} + +ProcessedPath::~ProcessedPath() { +} + +void ProcessedPath::Clear() { + path.reset(); +} + +void ProcessedPath::MoveTo(float x, float y) { + path.moveTo(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void ProcessedPath::LineTo(float x, float y) { + path.lineTo(SkFloatToScalar(x), SkFloatToScalar(y)); +} + +void ProcessedPath::QuadraticTo(float cx, float cy, float x, float y) { + path.quadTo(SkFloatToScalar(cx), + SkFloatToScalar(cy), + SkFloatToScalar(x), + SkFloatToScalar(y)); +} + +void ProcessedPath::CubicTo(float c0x, float c0y, + float c1x, float c1y, + float x, float y) { + path.cubicTo(SkFloatToScalar(c0x), + SkFloatToScalar(c0y), + SkFloatToScalar(c1x), + SkFloatToScalar(c1y), + SkFloatToScalar(x), + SkFloatToScalar(y)); +} + +void ProcessedPath::Close() { + path.close(); +} + +void ProcessedPath::CreateMesh(Field* exterior_positions, + Field* exterior_texture_coordinates, + Field* interior_positions) { + gpu2d::PathProcessor processor; + cache.Clear(); + processor.Process(path, &cache); + // Copy the exterior vertices and texture coordinates into the + // fields for the exterior triangles. + SetFields(cache.vertices(), exterior_positions, + cache.texcoords(), exterior_texture_coordinates, + cache.num_vertices()); + // Copy the interior vertices into the field for the interior + // triangles. + SetFields(cache.interior_vertices(), interior_positions, + NULL, NULL, + cache.num_interior_vertices()); +} + +void ProcessedPath::SetFields(const float* positions, + Field* position_field, + const float* texture_coordinates, + Field* texture_coordinate_field, + unsigned int num_vertices) { + // It may happen that there were no exterior or interior triangles. + // In this case we allocate a single vertex in the fields to + // indicate this to JavaScript since we can not allocate a + // zero-sized buffer. + DCHECK_NE(num_vertices, 1u); + if (num_vertices == 0) { + DCHECK_EQ(positions, static_cast<const float*>(NULL)); + DCHECK_EQ(texture_coordinates, static_cast<const float*>(NULL)); + num_vertices = 1; + } + Buffer* buffer = position_field->buffer(); + if (!buffer) + return; + if (buffer->num_elements() != num_vertices) { + if (!buffer->AllocateElements(num_vertices)) { + return; + } + } + if (num_vertices == 1) { + float tmp[2] = { 0 }; + position_field->SetFromFloats(tmp, 2, 0, 1); + } else { + position_field->SetFromFloats(positions, 2, 0, num_vertices); + } + + // The texture coordinates are NULL for the interior triangles. + if (texture_coordinate_field != NULL) { + buffer = texture_coordinate_field->buffer(); + if (!buffer) + return; + if (buffer->num_elements() != num_vertices) { + if (!buffer->AllocateElements(num_vertices)) { + return; + } + } + if (num_vertices == 1) { + float tmp[3] = { 0 }; + texture_coordinate_field->SetFromFloats(tmp, 3, 0, 1); + } else { + texture_coordinate_field->SetFromFloats(texture_coordinates, + 3, 0, num_vertices); + } + } +} + +} // namespace o3d + diff --git a/o3d/core/cross/processed_path.h b/o3d/core/cross/processed_path.h new file mode 100644 index 0000000..68233f2 --- /dev/null +++ b/o3d/core/cross/processed_path.h @@ -0,0 +1,115 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef O3D_CORE_CROSS_PROCESSED_PATH_H_ +#define O3D_CORE_CROSS_PROCESSED_PATH_H_ + +#include "core/cross/object_base.h" +#include "core/cross/gpu2d/path_cache.h" +#include "third_party/skia/include/core/SkPath.h" + +namespace o3d { + +class Field; + +// This class is the link between the generic curve processing code in +// this directory and O3D. It runs the PathProcessor on a set of +// curves and returns the resulting triangles' vertices and texture +// coordinates in Fields which can be incorporated into primitives in +// the scene graph. +class ProcessedPath : public ObjectBase { + public: + typedef SmartPointer<ProcessedPath> Ref; + + virtual ~ProcessedPath(); + + // Clears out all of the curve segments that have been added to this + // path. + void Clear(); + + // Moves the pen to the given absolute X,Y coordinates. If a contour + // isn't currently open on this path, one is opened. + void MoveTo(float x, float y); + + // Draws a line from the current coordinates to the given absolute + // X,Y coordinates. + void LineTo(float x, float y); + + // Draws a quadratic curve from the current coordinates through the + // given control point and end point, specified in absolute + // coordinates. + void QuadraticTo(float cx, float cy, float x, float y); + + // Draws a cubic curve from the current coordinates through the + // given control points and end point, specified in absolute + // coordinates. + void CubicTo(float c0x, float c0y, + float c1x, float c1y, + float x, float y); + + // Closes the currently open contour on this path. + void Close(); + + // Creates the triangle mesh which will render the given curve + // segments. There are two regions: exterior and interior. The + // exterior region covers the portions containing the curve + // segments. It has two associated fields: a 2D floating point field + // for the positions, and a 3D floating point field for the + // Loop/Blinn texture coordinates. The interior region has one 2D + // floating point field for the positions. The contents of the + // fields are organized for rendering as non-indexed triangles. + void CreateMesh(Field* exterior_positions, + Field* exterior_texture_coordinates, + Field* interior_positions); + + private: + explicit ProcessedPath(ServiceLocator* service_locator); + + void SetFields(const float* positions, + Field* position_field, + const float* texture_coordinates, + Field* texture_coordinate_field, + unsigned int num_vertices); + + friend class IClassManager; + static ObjectBase::Ref Create(ServiceLocator* service_locator); + + SkPath path; + gpu2d::PathCache cache; + + O3D_DECL_CLASS(ProcessedPath, ObjectBase); + DISALLOW_COPY_AND_ASSIGN(ProcessedPath); +}; + +} // namespace o3d + +#endif // O3D_CORE_CROSS_PROCESSED_PATH_H_ + diff --git a/o3d/plugin/idl/processed_path.idl b/o3d/plugin/idl/processed_path.idl new file mode 100644 index 0000000..0ba7ca8 --- /dev/null +++ b/o3d/plugin/idl/processed_path.idl @@ -0,0 +1,119 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace o3d { + +%[ + A ProcessedPath holds a series of 2D vector drawing commands (i.e., + move-to, line-to, quad-to, cubic-to) and the results of processing + this series of commands into a triangle mesh for rendering on the + GPU. It is only an internal class; this functionality is exposed via + the o3djs.gpu2d library. +%] + +[nocpp, include="core/cross/processed_path.h"] class ProcessedPath + : ObjectBase { + %[ + Clears out all of the curve segments that have been added to this + path. + %] + [nodocs] + void Clear(); + + %[ + Moves the pen to the given absolute X,Y coordinates. If a contour + isn't currently open on this path, one is opened. + \param x the x coordinate to move to + \param y the y coordinate to move to + %] + [nodocs] + void MoveTo(float x, float y); + + %[ + Draws a line from the current coordinates to the given absolute + X,Y coordinates. + \param x the x coordinate to draw a line to + \param y the y coordinate to draw a line to + %] + [nodocs] + void LineTo(float x, float y); + + %[ + Draws a quadratic curve from the current coordinates through the + given control point and end point, specified in absolute + coordinates. + \param cx the x coordinate of the quadratic's control point + \param cy the y coordinate of the quadratic's control point + \param x the x coordinate of the quadratic's end point + \param y the y coordinate of the quadratic's end point + %] + [nodocs] + void QuadraticTo(float cx, float cy, float x, float y); + + %[ + Draws a cubic curve from the current coordinates through the + given control points and end point, specified in absolute + coordinates. + \param c0x the x coordinate of the cubic's first control point + \param c0y the y coordinate of the cubic's first control point + \param c1x the x coordinate of the cubic's second control point + \param c1y the y coordinate of the cubic's second control point + \param x the x coordinate of the cubic's end point + \param y the y coordinate of the cubic's end point + %] + [nodocs] + void CubicTo(float c0x, float c0y, + float c1x, float c1y, + float x, float y); + + %[ + Closes the currently open contour on this path. + %] + [nodocs] + void Close(); + + %[ + Creates the triangle mesh which will render the given curve + segments. There are two regions: exterior and interior. The + exterior region covers the portions containing the curve + segments. It has two associated fields: a 2D floating point field + for the positions, and a 3D floating point field for the + Loop/Blinn texture coordinates. The interior region has one 2D + floating point field for the positions. The contents of the fields + are organized for rendering as non-indexed triangles. + %] + [nodocs] + void CreateMesh(Field exterior_positions, + Field exterior_texture_coordinates, + Field interior_positions); +}; + +} // namespace o3d diff --git a/o3d/plugin/idl_list.manifest b/o3d/plugin/idl_list.manifest index 9b4e73b..5e388dc 100644 --- a/o3d/plugin/idl_list.manifest +++ b/o3d/plugin/idl_list.manifest @@ -63,6 +63,7 @@ 'idl/param_operation.idl', 'idl/plugin.idl', 'idl/primitive.idl', + 'idl/processed_path.idl', 'idl/ray_intersection_info.idl', 'idl/render_event.idl', 'idl/render_node.idl', diff --git a/o3d/samples/gpu2d/Google_Logo.svg b/o3d/samples/gpu2d/Google_Logo.svg new file mode 100644 index 0000000..e5f0332 --- /dev/null +++ b/o3d/samples/gpu2d/Google_Logo.svg @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ + <!ENTITY ns_svg "http://www.w3.org/2000/svg"> + <!ENTITY ns_xlink "http://www.w3.org/1999/xlink"> +]> +<svg version="1.1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="720.002" height="238.598" viewBox="0 0 720.002 238.598" + overflow="visible" enable-background="new 0 0 720.002 238.598" xml:space="preserve"> +<g id="Grid" display="none"> +</g> +<g id="Layer_1"> +</g> +<g id="Desktop"> +</g> +<g id="Guides"> +</g> +<g id="Page_1"> + <g id="Layer_1_1_"> + + <linearGradient id="XMLID_8_" gradientUnits="userSpaceOnUse" x1="242.4595" y1="542.1074" x2="332.2358" y2="542.1074" gradientTransform="matrix(0.6652 0 0 0.6708 371.3542 -281.5009)"> + <stop offset="0.35" style="stop-color:#85C881"/> + <stop offset="0.503" style="stop-color:#097E3F"/> + <stop offset="0.65" style="stop-color:#205127"/> + </linearGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_8_)" d="M550.94,0c10.977,0,21.309,0.004,32.281,0.004 + c-4.492,2.91-6,4.117-7.93,7.172c-1.383,2.195-1.738,6.352-1.988,10.098c-1.887,43.109-0.73,86.543-1.008,129.652 + c0.176,11.75,4.793,10.371,20.074,9.922c-2.305,1.953-6.039,5.816-8.938,7.473c-13.781,0-26.945-0.293-40.723-0.293 + c5.195-5.363,7.855-8.82,7.5-22.227c0.273-45.055-1.512-88.285,1.234-131.672c-6.465,0.184-12.332,0.09-18.797,0.277 + C538.745,7.258,544.522,2.828,550.94,0L550.94,0z"/> + <path fill-rule="evenodd" clip-rule="evenodd" fill="#6B696B" d="M691.01,55.836c1.488,0,9.984,0,11.473,0l-0.77,2.113 + c-0.262,0.109-0.523,0.215-0.781,0.324c0.164-0.777,0.434-1.355-0.16-1.512l-2.691,0.004c-0.148,2.574-0.254,7.293-0.25,10.68 + c-0.133,1.121,1.086,1.051,2.348,1.301l-1.098,0.637c-1.867,0-3.727-0.039-5.594-0.039c0.891-0.469,1.016-1.195,0.969-3.016 + c-0.055-3.031,0.055-6.926,0.215-9.59c-0.902-0.023-1.699,0.012-2.574,0.039c-0.402,0.047-0.711,0.453-0.992,1.16l-0.852,0.355 + L691.01,55.836L691.01,55.836z"/> + + <radialGradient id="XMLID_9_" cx="-89.4995" cy="559.8066" r="94.2447" gradientTransform="matrix(0.6875 0 0 0.6652 291.9632 -263.6867)" gradientUnits="userSpaceOnUse"> + <stop offset="0.1124" style="stop-color:#FFFFFF"/> + <stop offset="0.12" style="stop-color:#F15C49"/> + <stop offset="0.31" style="stop-color:#4E140E"/> + <stop offset="0.4772" style="stop-color:#B21F24"/> + <stop offset="0.5684" style="stop-color:#B21F24"/> + <stop offset="0.6748" style="stop-color:#F15C49"/> + <stop offset="0.88" style="stop-color:#4E140E"/> + </radialGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_9_)" d="M236.346,56.355c37.484,0,57.039,24.629,58.791,53.484 + c1.736,28.742-22.246,54.461-51.596,57.543c-35.295,3.699-66.115-20.332-66.115-55.371 + C177.426,81.359,203.895,56.355,236.346,56.355L236.346,56.355z M271.77,113.535c2.146,21.148-5.029,40.781-24.227,45.176 + c-19.643,4.492-40.467-11.613-45.668-38.98c-5.705-30.012,3.725-49.777,22.268-54.637 + C246.108,59.336,268.495,81.25,271.77,113.535L271.77,113.535z"/> + + <radialGradient id="XMLID_10_" cx="50.7944" cy="559.8027" r="92.5321" gradientTransform="matrix(0.6794 0 0 0.6652 324.1435 -263.6867)" gradientUnits="userSpaceOnUse"> + <stop offset="0.11" style="stop-color:#FEEE15"/> + <stop offset="0.305" style="stop-color:#615120"/> + <stop offset="0.4298" style="stop-color:#B08D2F"/> + <stop offset="0.5858" style="stop-color:#B38F2F"/> + <stop offset="0.6794" style="stop-color:#F1E00B"/> + <stop offset="0.89" style="stop-color:#615120"/> + </radialGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_10_)" d="M363.333,56.355c37.045,0,56.373,24.629,58.104,53.484 + c1.715,28.742-21.988,54.461-50.996,57.543c-34.881,3.699-65.34-20.332-65.34-55.371C305.1,81.359,331.258,56.355,363.333,56.355 + L363.333,56.355z M398.338,113.535c2.125,21.148-4.969,40.781-23.941,45.176c-19.414,4.492-39.992-11.613-45.133-38.98 + c-5.641-30.012,3.68-49.777,22.006-54.637C372.979,59.336,395.104,81.25,398.338,113.535L398.338,113.535z"/> + + <radialGradient id="XMLID_11_" cx="397.9761" cy="536.8584" r="115.4805" gradientTransform="matrix(0.6652 0 0 0.6652 404.6628 -255.9006)" gradientUnits="userSpaceOnUse"> + <stop offset="0.08" style="stop-color:#F15C49"/> + <stop offset="0.0968" style="stop-color:#4E140E"/> + <stop offset="0.4748" style="stop-color:#B21F24"/> + <stop offset="0.5756" style="stop-color:#B21F24"/> + <stop offset="0.6932" style="stop-color:#F15C49"/> + <stop offset="0.92" style="stop-color:#4E140E"/> + </radialGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_11_)" d="M684.073,88.641c3.609-1.5,3.895-1.762,2.563-5.574 + c-7.629-21.77-36.344-33.523-60.797-23.324c-19.285,8.039-33.25,28.434-33.25,51.246c0,30.734,22.18,58.063,52.32,58.063 + c13.813,0,23.633-1.906,32.383-7.457c1.875-1.738,12.438-10.191,12.539-12.004c0.023-0.418-1.484,0.898-3.781,2.164 + c-6.813,3.586-17.023,4.957-25.023,5.105c-16.203,0.297-29.141-10.742-37.578-23.848c-5.43-8.434-8.938-18.078-9.813-28.434 + c-1.34-15.91,2.547-34.215,20.523-38.707c11.961-2.992,23.289,3.707,27.695,15.23c1.996,5.227,0.789,7.941-4.043,9.938 + l-43.246,18.063l14.746,0.941C646.87,102.762,666.518,95.922,684.073,88.641L684.073,88.641z"/> + + <radialGradient id="XMLID_12_" cx="-251.0381" cy="536.7227" r="139.9347" gradientTransform="matrix(0.6652 0 0 0.6652 256.4202 -274.632)" gradientUnits="userSpaceOnUse"> + <stop offset="0.14" style="stop-color:#6584C2"/> + <stop offset="0.32" style="stop-color:#263573"/> + <stop offset="0.5648" style="stop-color:#2C50A3"/> + <stop offset="0.6656" style="stop-color:#6584C2"/> + <stop offset="0.86" style="stop-color:#263573"/> + </radialGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_12_)" d="M143.196,40.938l16.85-17.18 + C131.444,0.078,82.385-3.027,49.879,13.406C21.606,27.703,1.049,56.098,0.042,87.805c-1.059,33.371,18.338,59.77,38.021,73.047 + c32.492,21.91,88.824,17.031,122.758-0.102l-0.008-3.176l0.004-0.004v-34.973l7.9-9.254h-49.436l-18.309,10.309l35.557-1.055 + c0,9.957,0,20.863,0,30.824c0,4.379,0.057,5.082-7.152,7.406c-10.145,2.668-21.598,3.188-32.063,2.316 + c-35.504-2.961-62.566-31.016-68.979-66.008c-6.082-33.199,9.574-64.605,36.045-77.566c12.928-6.328,28.438-9.297,45.25-3.918 + c10.539,3.375,20.109,9.449,28.449,17.828l-5.809,10.563L143.196,40.938L143.196,40.938z"/> + + <radialGradient id="XMLID_13_" cx="190.2485" cy="510.457" r="88.1475" gradientTransform="matrix(0.666 0 0 0.666 353.5035 -242.8212)" gradientUnits="userSpaceOnUse"> + <stop offset="0.08" style="stop-color:#6584C2"/> + <stop offset="0.248" style="stop-color:#263573"/> + <stop offset="0.4748" style="stop-color:#6584C2"/> + <stop offset="0.6596" style="stop-color:#2C50A3"/> + <stop offset="0.92" style="stop-color:#263573"/> + </radialGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_13_)" d="M507.112,131.16l-29.234,10.438 + c1.09-1.91,0.398-1.254-1.66-0.941c-18.895,2.855-38.363-9.48-42.918-28.121c-2.309-9.461-0.875-19.348,2.727-27.082 + c7.828-16.805,25.23-26.141,44.535-26.141c20.082,0,38.676,0.004,58.363,0.164l-13.805,8.938h-15.715 + c6.641,4.633,11.723,11.313,14.527,18.758c2.988,7.922,3.398,16.707,0.367,24.797C521.44,119.598,515.639,126.168,507.112,131.16 + L507.112,131.16z M504.178,100.457c2.457,14.922-1.332,29.074-14.824,32.805c-13.805,3.82-29.078-6.926-33.988-26.156 + c-5.387-21.09,0.184-34.484,13.191-38.527C483.963,63.789,500.424,77.68,504.178,100.457L504.178,100.457z"/> + + <radialGradient id="XMLID_14_" cx="169.6396" cy="660.5957" r="95.1706" gradientTransform="matrix(0.666 0 0 0.666 360.8121 -245.4305)" gradientUnits="userSpaceOnUse"> + <stop offset="0.07" style="stop-color:#6584C2"/> + <stop offset="0.242" style="stop-color:#263573"/> + <stop offset="0.4742" style="stop-color:#6584C2"/> + <stop offset="0.6634" style="stop-color:#2C50A3"/> + <stop offset="0.93" style="stop-color:#263573"/> + </radialGradient> + <path fill-rule="evenodd" clip-rule="evenodd" fill="url(#XMLID_14_)" d="M477.877,141.594l29.234-10.434 + c-4.844,2.84-7.293,6.434-7.563,9.883c-0.789,10.07,11.516,17.391,18.633,22.715c15.563,11.656,21.641,30.773,11.344,49.246 + c-9.84,17.637-33.281,25.504-58.168,25.594c-30.918-0.137-50.418-14.305-51.449-33.586c-1.328-24.922,24.242-36.781,47.344-39.484 + c6.469-0.758,12.25-0.637,17.688-0.254C476.764,159.895,473.706,148.922,477.877,141.594L477.877,141.594z M492.581,171.699 + c2.328,1.695,4.746,3.352,7.234,4.93c13.563,8.586,17.891,19.086,16.785,28.777c-1.328,11.688-12.543,20.902-31.742,22.066 + c-20.496,1.242-48.09-6.875-42.98-33.836c2.719-14.363,21.781-21.781,37.539-22.305 + C483.78,171.191,488.217,171.551,492.581,171.699L492.581,171.699z"/> + <path fill-rule="evenodd" clip-rule="evenodd" fill="#6B696B" d="M702.94,55.836c0.258,0,0.496,0,0.734,0v-0.004h2.914 + c0.711,0,0.758,0.344,0.996,1.008c1.012,2.809,3.316,9.676,3.34,9.637c0.953-2.594,2.402-6.457,3.523-9.129 + c-0.008-0.309-0.313-0.332-0.707-0.309c-0.523,0-1.043,0-1.566,0c0.008-0.008,0.023-0.016,0.031-0.023 + c-0.016,0.004-0.031,0.012-0.047,0.016c0.164-0.117,0.328-0.238,0.492-0.359c0.363-0.277,0.727-0.559,1.09-0.836 + c0.02,0,0.035,0,0.055,0h0.004c1.488,0,2.695,0,4.188,0l0,0c0.32,0,0.648,0,0.969,0c-0.086,0.199-0.172,0.402-0.258,0.605 + c-0.727,0.457-0.547,1.656-0.48,2.586c0.168,2.43,0.418,4.855,0.629,7.281c0.102,1.16,0.344,2.172,1.156,3.059 + c-0.781,0.223-1.305,0.301-2.352,0.301c-1.688,0-2.156-1.078-2.344-2.961c-0.172-2.629-0.344-5.258-0.516-7.883 + c-1.109,2.777-2.129,5.363-3.148,8.199c-0.219,0.672-0.313,0.742-0.07,1.41l-3.031,1.215l-3.863-11.012 + c-0.254,2.547-0.512,5.852-0.664,8.387c-0.043,1.242,0.141,1.547,2.23,1.652l-1.215,0.707c-1.871,0-1.23-0.039-3.098-0.039 + c0.887-0.609,1.148-1.199,1.227-3.016c0.188-2.797,0.492-6.828,0.84-9.492l-0.027-0.07c-0.762-0.016-1.352-0.129-2.313,0.266 + L702.94,55.836L702.94,55.836z"/> + </g> +</g> +</svg> diff --git a/o3d/samples/gpu2d/a.svg b/o3d/samples/gpu2d/a.svg new file mode 100644 index 0000000..aa93f17 --- /dev/null +++ b/o3d/samples/gpu2d/a.svg @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="500" + height="500" + id="svg3012" + version="1.1" + inkscape:version="0.47 r22583" + sodipodi:docname="a.svg"> + <defs + id="defs3014"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective3020" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="185.77464" + inkscape:cy="230.99609" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="939" + inkscape:window-height="861" + inkscape:window-x="723" + inkscape:window-y="0" + inkscape:window-maximized="0" /> + <metadata + id="metadata3017"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-189.22536,329.0208)"> + <g + style="font-size:600px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + id="text3078" + transform="translate(15,9)"> + <path + d="m 402.88942,-106.96221 c -43.55485,1.7e-4 -73.7306,4.98063 -90.52734,14.941408 -16.79698,9.961078 -25.19541,26.953249 -25.19532,50.976563 -9e-5,19.140705 6.24991,34.3750646 18.75,45.703125 12.69519,11.132855 29.88268,16.699255 51.5625,16.699219 29.88263,3.6e-5 53.80838,-10.546828 71.77735,-31.640625 18.16381,-21.288973 27.24583,-49.511601 27.24609,-84.667969 l 0,-12.011721 -53.61328,0 m 107.51953,-22.26562 0,187.207028 -53.90625,0 0,-49.804687 c -12.30493,19.921905 -27.63695,34.667984 -45.99609,44.238281 -18.35957,9.374996 -40.82049,14.062492 -67.38282,14.0625 -33.59386,-8e-6 -60.35164,-9.374999 -80.27343,-28.125 -19.72661,-18.945274 -29.58988,-44.2382172 -29.58985,-75.878906 -3e-5,-36.91393 12.30464,-64.745936 36.91407,-83.496096 24.80459,-18.7498 61.71861,-28.12479 110.74218,-28.125 l 75.58594,0 0,-5.27344 c -2.6e-4,-24.80445 -8.20338,-43.94505 -24.60937,-57.42187 -16.21116,-13.67159 -39.0627,-20.50752 -68.55469,-20.50781 -18.75015,2.9e-4 -37.01185,2.24638 -54.78516,6.73828 -17.77353,4.49247 -34.86336,11.23074 -51.26953,20.21484 l 0,-49.80469 c 19.72648,-7.61686 38.86709,-13.28092 57.42188,-16.99218 18.55455,-3.90592 36.62094,-5.85904 54.19921,-5.85938 47.46072,3.4e-4 82.90991,12.30501 106.34766,36.91406 23.4372,24.60965 35.15594,61.9143 35.15625,111.91407" + id="path3083" /> + </g> + </g> +</svg> diff --git a/o3d/samples/gpu2d/basic.html b/o3d/samples/gpu2d/basic.html new file mode 100644 index 0000000..8068503 --- /dev/null +++ b/o3d/samples/gpu2d/basic.html @@ -0,0 +1,229 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<!-- + Sample demonstrating basic 2D vector curve rendering in 3D. +--> + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +O3D GPU2D Sample: Basic Vector Shapes +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3djs/base.js"></script> + +<!-- Our javascript code --> +<script type="text/javascript" id="o3dscript"> +o3djs.require('o3djs.cameracontroller'); +o3djs.require('o3djs.gpu2d'); +o3djs.require('o3djs.io'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.util'); + +// Events +// init() once the page has finished loading. +// unload() when the page is unloaded. +window.onload = createClients; +window.onunload = unload; + +// Globals +var g_o3d; +var g_math; +var g_client; +var g_pack; +var g_viewInfo; +var g_o3dElement; +var g_finished = false; // for selenium testing. +var g_clientWidth; +var g_clientHeight; +var g_path; +var g_cameraController; + +/** + * Remove any callbacks so they don't get called after the page has unloaded. + */ +function unload() { + if (g_client) { + g_client.cleanup(); + + // Clear the mouse events. + onMouseUp(); + } +} + +function createClients() { + o3djs.util.makeClients(init); +} + +function init(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_o3dElement = o3dElement; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + g_client = o3dElement.client; + + // Store the size of the plugin, so that we can adjust coordinates in + // full-screen mode. This is necessary because we're not adjusting the aspect + // ratio; we'd rather that the canvas filled the available area, rather than + // staying a fixed size or aspect ratio. + g_clientWidth = g_o3dElement.clientWidth; + g_clientHeight = g_o3dElement.clientHeight; + + // Creates a pack to manage our resources/assets + g_pack = g_client.createPack(); + + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set the background color to light gray. + g_viewInfo.clearBuffer.clearColor = [0.8, 0.8, 0.8, 1]; + + // Set up our CameraController. + g_cameraController = o3djs.cameracontroller.createCameraController( + [0, 0, 0], // centerPos + 500, // backpedal + 0, // heightAngle + 0, // rotationAngle + g_math.degToRad(15), // fieldOfViewAngle + updateViewAndProjectionMatrices); // opt_onChange + g_cameraController.distancePerUnit = 100.0; + updateViewAndProjectionMatrices(); + + // Set up event handlers for mouse interaction. + o3djs.event.addEventListener(o3dElement, 'mousedown', onMouseDown); + o3djs.event.addEventListener(o3dElement, 'mousemove', onMouseMove); + o3djs.event.addEventListener(o3dElement, 'mouseup', onMouseUp); + + // Create a Path. + g_path = o3djs.gpu2d.createPath(g_pack, g_viewInfo.zOrderedDrawList); + g_client.root.addShape(g_path.shape); + + // Set the fill of the path. + g_path.setFill(o3djs.gpu2d.createColor(g_pack, 0.8, 0.0, 0.3, 1.0)); + + // Draw something on the path. + g_path.moveTo(25.0, -50.0); + g_path.cubicTo(75.0, 50.0, 125.0, 25.0, 175.0, 50.0); + g_path.cubicTo(125.0, -50.0, 75.0, -25.0, 25.0, -50.0); + + g_path.moveTo(-25.0, -50.0); + g_path.cubicTo(-75.0, 50.0, -125.0, 25.0, -175.0, 50.0); + g_path.cubicTo(-125.0, -50.0, -75.0, -25.0, -25.0, -50.0); + g_path.close(); + + // Force an update. + g_path.update(); + + g_finished = true; // for selenium testing. +} + +/** + * Event handler that gets called when a mouse click takes place in + * the O3D element. It changes the state of the camera controller + * based on which modifier keys are pressed. + * @param {!Event} e The mouse down event. + */ +function onMouseDown(e) { + if (e.button == 0) { + if (!e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.MOVE_CENTER_IN_VIEW_PLANE, e.x, e.y); + } else if (e.metaKey || e.altKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.SPIN_ABOUT_CENTER, e.x, e.y); + } else if (!e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_IN_OUT, e.x, e.y); + } else if (e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.ZOOM_IN_OUT, e.x, e.y); + } else if (e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_ZOOM, e.x, e.y); + } + } +} + +/** + * Event handler that gets called when a mouse move event takes place + * in the O3D element. It tells the camera controller that the mouse + * has moved. + * @param {!Event} e The mouse move event. + */ +function onMouseMove(e) { + g_cameraController.mouseMoved(e.x, e.y); +} + +/** + * Event handler that gets called when a mouse up event takes place in + * the O3D element. It tells the camera controller that the mouse has + * been released. + * @param {!Event} e The mouse up event. + */ +function onMouseUp(e) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.NONE, e.x, e.y); +} + +/** + * Updates the view and projection matrices. + */ +function updateViewAndProjectionMatrices() { + g_viewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + + // Set up a perspective transformation for the projection. + g_viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_cameraController.fieldOfViewAngle * 2, // Frustum angle. + g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. + 1, // Near plane. + 5000); // Far plane. +} +</script> +</head> + +<body> +<h1>O3D GPU2D Sample: Basic Vector Shapes</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 800px; height: 600px;"></div> +<!-- End of O3D plugin --> + +<p><p>See above for output. +</body> +</html> diff --git a/o3d/samples/gpu2d/crescent.svg b/o3d/samples/gpu2d/crescent.svg new file mode 100644 index 0000000..933dbb7 --- /dev/null +++ b/o3d/samples/gpu2d/crescent.svg @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.0" + width="80mm" + height="80mm" + id="svg2"> + <defs + id="defs4" /> + <g + id="layer1"> + <path + d="M 125.13485,36.187753 C 67.60819,37.282014 21.25984,84.307148 21.259843,142.094 C 21.259843,200.56607 68.72535,248.0315 127.19735,248.0315 C 154.53502,248.0315 179.46231,237.64775 198.25985,220.62525 C 185.00691,228.24342 169.63331,232.594 153.25985,232.594 C 103.33007,232.594 62.822343,192.08628 62.822343,142.1565 C 62.822343,92.226718 103.33007,51.719003 153.25985,51.719003 C 169.76352,51.719003 185.24789,56.144299 198.57235,63.875253 C 179.73892,46.68361 154.68725,36.187751 127.19735,36.187753 C 126.51213,36.187753 125.81698,36.174778 125.13485,36.187753 z " + style="fill:#007f00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path1315" /> + </g> +</svg> diff --git a/o3d/samples/gpu2d/javalogo.svg b/o3d/samples/gpu2d/javalogo.svg new file mode 100644 index 0000000..d03cb5b --- /dev/null +++ b/o3d/samples/gpu2d/javalogo.svg @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> +<path d="M117.151 349.592 C117.151 349.592 99.886 359.641 129.452 363.032 C165.276 367.122 183.582 366.533 223.048 359.073 C223.048 359.073 233.445 365.577 247.940 371.210 C159.439 409.127 47.644 369.013 117.151 349.592 L117.151 349.592 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M106.331 300.104 C106.331 300.104 86.964 314.443 116.552 317.501 C154.823 321.452 185.035 321.776 237.344 311.709 C237.344 311.709 244.562 319.041 255.929 323.048 C148.943 354.342 29.782 325.510 106.331 300.104 L106.331 300.104 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M197.494 216.145 C219.312 241.260 191.773 263.842 191.773 263.842 Q247.140 235.265 221.718 199.462 C197.966 166.089 179.756 149.511 278.341 92.338 C278.342 92.338 123.587 130.979 197.494 216.145 L197.494 216.145 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M314.542 386.202 C314.542 386.202 327.323 396.738 300.465 404.886 C249.394 420.355 87.861 425.021 42.995 405.505 C26.878 398.485 57.116 388.755 66.630 386.700 C76.550 384.554 82.214 384.946 82.214 384.946 C64.277 372.315 -33.721 409.753 32.442 420.490 C212.871 449.736 361.337 407.317 314.542 386.202 L314.542 386.202 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M125.460 248.822 C125.460 248.822 43.305 268.341 96.368 275.435 C118.780 278.434 163.431 277.744 205.053 274.252 C239.059 271.398 273.186 265.294 273.186 265.294 Q261.204 270.434 252.529 276.354 C169.085 298.301 7.917 288.080 54.322 265.642 C93.549 246.665 125.460 248.822 125.460 248.822 L125.460 248.822 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M272.846 331.198 C357.664 287.135 318.443 244.786 291.069 250.490 C284.375 251.887 281.372 253.097 281.372 253.097 Q283.866 249.190 288.615 247.509 C342.770 228.479 384.409 303.655 271.151 333.428 C271.150 333.428 272.453 332.248 272.846 331.198 L272.846 331.198 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M221.719 0.000 C221.719 0.000 268.682 46.990 177.162 119.227 C103.769 177.196 160.428 210.236 177.136 248.005 C134.291 209.352 102.865 175.321 123.944 143.653 C154.898 97.174 240.663 74.632 221.719 0.000 L221.719 0.000 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M133.799 450.637 C215.196 455.841 340.225 447.741 343.184 409.222 C343.184 409.222 337.496 423.824 275.908 435.410 C206.427 448.491 120.711 446.968 69.892 438.577 C69.892 438.577 80.303 447.194 133.799 450.637 L133.799 450.637 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M339.426 519.716 L336.947 519.716 L336.947 518.331 L343.623 518.331 L343.623 519.716 L341.154 519.716 L341.154 526.644 L339.425 526.644 L339.425 519.716 L339.426 519.716 L339.426 519.716 z M352.756 520.067 L352.724 520.067 L350.263 526.643 L349.131 526.643 L346.685 520.067 L346.660 520.067 L346.660 526.643 L345.022 526.643 L345.022 518.330 L347.424 518.330 L349.688 524.215 L351.959 518.330 L354.347 518.330 L354.347 526.643 L352.757 526.643 L352.757 520.067 L352.756 520.067 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M120.461 597.813 C112.785 604.467 104.668 608.209 97.382 608.209 C87.004 608.209 81.375 601.973 81.375 591.993 C81.375 581.190 87.402 573.283 111.538 573.283 L120.461 573.283 L120.461 597.813 L120.461 597.813 L120.461 597.813 z M141.650 621.716 L141.650 547.720 C141.650 528.805 130.863 516.324 104.868 516.324 C89.700 516.324 76.401 520.074 65.590 524.849 L68.701 537.955 C77.216 534.828 88.227 531.924 99.038 531.924 C114.016 531.924 120.461 537.955 120.461 550.429 L120.461 559.785 L112.976 559.785 C76.593 559.785 60.178 573.895 60.178 595.121 C60.178 613.406 70.997 623.803 91.363 623.803 C104.451 623.803 114.233 618.398 123.364 610.488 L125.027 621.716 L141.650 621.716 L141.650 621.716 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M212.362 621.716 L185.939 621.716 L154.132 518.214 L177.209 518.214 L196.952 581.814 L201.343 600.923 C211.308 573.284 218.375 545.222 221.909 518.214 L244.354 518.214 C238.352 552.294 227.523 589.706 212.362 621.716 L212.362 621.716 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M313.755 597.813 C306.054 604.467 297.935 608.209 290.669 608.209 C280.287 608.209 274.658 601.973 274.658 591.993 C274.658 581.190 280.697 573.283 304.812 573.283 L313.754 573.283 L313.754 597.813 L313.755 597.813 L313.755 597.813 z M334.951 621.716 L334.951 547.720 C334.951 528.805 324.133 516.324 298.162 516.324 C282.974 516.324 269.678 520.074 258.867 524.849 L261.983 537.955 C270.502 534.828 281.528 531.924 292.338 531.924 C307.298 531.924 313.754 537.955 313.754 550.429 L313.754 559.785 L306.269 559.785 C269.878 559.785 253.468 573.895 253.468 595.121 C253.468 613.406 264.272 623.803 284.637 623.803 C297.736 623.803 307.499 618.398 316.655 610.488 L318.329 621.716 L334.951 621.716 L334.951 621.716 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M36.941 639.306 C30.904 648.127 21.141 655.115 10.462 659.055 L0.000 646.731 C8.135 642.553 15.103 635.819 18.345 629.549 C21.141 623.960 22.305 616.776 22.305 599.578 L22.305 481.398 L44.824 481.398 L44.824 597.949 C44.824 620.950 42.988 630.249 36.941 639.306 L36.941 639.306 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M117.151 349.592 C117.151 349.592 99.886 359.641 129.452 363.032 C165.276 367.122 183.582 366.533 223.048 359.073 C223.048 359.073 233.445 365.577 247.940 371.210 C159.439 409.127 47.644 369.013 117.151 349.592 L117.151 349.592 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M106.331 300.104 C106.331 300.104 86.964 314.443 116.552 317.501 C154.823 321.452 185.035 321.776 237.344 311.709 C237.344 311.709 244.562 319.041 255.929 323.048 C148.943 354.342 29.782 325.510 106.331 300.104 L106.331 300.104 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M197.494 216.145 C219.312 241.260 191.773 263.842 191.773 263.842 Q247.140 235.265 221.718 199.462 C197.966 166.089 179.756 149.511 278.341 92.338 C278.342 92.338 123.587 130.979 197.494 216.145 L197.494 216.145 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M314.542 386.202 C314.542 386.202 327.323 396.738 300.465 404.886 C249.394 420.355 87.861 425.021 42.995 405.505 C26.878 398.485 57.116 388.755 66.630 386.700 C76.550 384.554 82.214 384.946 82.214 384.946 C64.277 372.315 -33.721 409.753 32.442 420.490 C212.871 449.736 361.337 407.317 314.542 386.202 L314.542 386.202 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M125.460 248.822 C125.460 248.822 43.305 268.341 96.368 275.435 C118.780 278.434 163.431 277.744 205.053 274.252 C239.059 271.398 273.186 265.294 273.186 265.294 Q261.204 270.434 252.529 276.354 C169.085 298.301 7.917 288.080 54.322 265.642 C93.549 246.665 125.460 248.822 125.460 248.822 L125.460 248.822 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M272.846 331.198 C357.664 287.135 318.443 244.786 291.069 250.490 C284.375 251.887 281.372 253.097 281.372 253.097 Q283.866 249.190 288.615 247.509 C342.770 228.479 384.409 303.655 271.151 333.428 C271.150 333.428 272.453 332.248 272.846 331.198 L272.846 331.198 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M221.719 0.000 C221.719 0.000 268.682 46.990 177.162 119.227 C103.769 177.196 160.428 210.236 177.136 248.005 C134.291 209.352 102.865 175.321 123.944 143.653 C154.898 97.174 240.663 74.632 221.719 0.000 L221.719 0.000 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M133.799 450.637 C215.196 455.841 340.225 447.741 343.184 409.222 C343.184 409.222 337.496 423.824 275.908 435.410 C206.427 448.491 120.711 446.968 69.892 438.577 C69.892 438.577 80.303 447.194 133.799 450.637 L133.799 450.637 z " + style="stroke-width:1;fill:rgb(0,116,189);stroke:none"/> +<path d="M339.426 519.716 L336.947 519.716 L336.947 518.331 L343.623 518.331 L343.623 519.716 L341.154 519.716 L341.154 526.644 L339.425 526.644 L339.425 519.716 L339.426 519.716 L339.426 519.716 z M352.756 520.067 L352.724 520.067 L350.263 526.643 L349.131 526.643 L346.685 520.067 L346.660 520.067 L346.660 526.643 L345.022 526.643 L345.022 518.330 L347.424 518.330 L349.688 524.215 L351.959 518.330 L354.347 518.330 L354.347 526.643 L352.757 526.643 L352.757 520.067 L352.756 520.067 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M120.461 597.813 C112.785 604.467 104.668 608.209 97.382 608.209 C87.004 608.209 81.375 601.973 81.375 591.993 C81.375 581.190 87.402 573.283 111.538 573.283 L120.461 573.283 L120.461 597.813 L120.461 597.813 L120.461 597.813 z M141.650 621.716 L141.650 547.720 C141.650 528.805 130.863 516.324 104.868 516.324 C89.700 516.324 76.401 520.074 65.590 524.849 L68.701 537.955 C77.216 534.828 88.227 531.924 99.038 531.924 C114.016 531.924 120.461 537.955 120.461 550.429 L120.461 559.785 L112.976 559.785 C76.593 559.785 60.178 573.895 60.178 595.121 C60.178 613.406 70.997 623.803 91.363 623.803 C104.451 623.803 114.233 618.398 123.364 610.488 L125.027 621.716 L141.650 621.716 L141.650 621.716 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M212.362 621.716 L185.939 621.716 L154.132 518.214 L177.209 518.214 L196.952 581.814 L201.343 600.923 C211.308 573.284 218.375 545.222 221.909 518.214 L244.354 518.214 C238.352 552.294 227.523 589.706 212.362 621.716 L212.362 621.716 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M313.755 597.813 C306.054 604.467 297.935 608.209 290.669 608.209 C280.287 608.209 274.658 601.973 274.658 591.993 C274.658 581.190 280.697 573.283 304.812 573.283 L313.754 573.283 L313.754 597.813 L313.755 597.813 L313.755 597.813 z M334.951 621.716 L334.951 547.720 C334.951 528.805 324.133 516.324 298.162 516.324 C282.974 516.324 269.678 520.074 258.867 524.849 L261.983 537.955 C270.502 534.828 281.528 531.924 292.338 531.924 C307.298 531.924 313.754 537.955 313.754 550.429 L313.754 559.785 L306.269 559.785 C269.878 559.785 253.468 573.895 253.468 595.121 C253.468 613.406 264.272 623.803 284.637 623.803 C297.736 623.803 307.499 618.398 316.655 610.488 L318.329 621.716 L334.951 621.716 L334.951 621.716 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +<path d="M36.941 639.306 C30.904 648.127 21.141 655.115 10.462 659.055 L0.000 646.731 C8.135 642.553 15.103 635.819 18.345 629.549 C21.141 623.960 22.305 616.776 22.305 599.578 L22.305 481.398 L44.824 481.398 L44.824 597.949 C44.824 620.950 42.988 630.249 36.941 639.306 L36.941 639.306 z " + style="stroke-width:1;fill:rgb(234,45,46);stroke:none"/> +</svg> diff --git a/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-1.html b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-1.html new file mode 100644 index 0000000..43d3cea --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-1.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +Curve Subdivision Bug 1 +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="../svgloader.js"></script> +<script type="text/javascript" src="../svgsample.js"></script> +</head> +<body onload="init('curve-subdivision-bug-1.svg');" onunload="unload();"> +<h1>Curve Subdivision Bug 1</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 500px; height: 200px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-1.svg b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-1.svg new file mode 100644 index 0000000..d174e74 --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-1.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="500" + height="500" + id="svg4764"> + <defs + id="defs4820" /> + <path + d="M 460.70202,26.83982 C 460.70202,26.83982 480.58076,43.22683 438.80757,55.8997 359.37505,79.959179 108.13711,87.21637 38.355451,56.862453 L 21.942,80.169149 C 302.56955,125.65648 533.48393,59.68072 460.70202,26.83982 L 460.70202,26.83982 z" + id="path4772" + style="fill:#0074bd;stroke:none" /> +</svg> diff --git a/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-2.html b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-2.html new file mode 100644 index 0000000..12ee88f --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-2.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +Curve Subdivision Bug 2 +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="../svgloader.js"></script> +<script type="text/javascript" src="../svgsample.js"></script> +</head> +<body onload="init('curve-subdivision-bug-2.svg');" onunload="unload();"> +<h1>Curve Subdivision Bug 2</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 500px; height: 200px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-2.svg b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-2.svg new file mode 100644 index 0000000..4479506 --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-2.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="500" + height="500" + id="svg4764"> + <defs + id="defs4820" /> + <path + d="M 460.70202,26.83982 C 460.70202,26.83982 480.58076,43.22683 438.80757,55.8997 359.37505,79.959179 108.13711,87.21637 38.355451,56.862453 L 21.942,80.169149 C 302.56955,125.65648 533.48393,59.68072 460.70202,26.83982 z" + id="path4772" + style="fill:#0074bd;stroke:none" /> +</svg> diff --git a/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-3.html b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-3.html new file mode 100644 index 0000000..a9b17f2 --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-3.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +Curve Subdivision Bug 3 +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="../svgloader.js"></script> +<script type="text/javascript" src="../svgsample.js"></script> +</head> +<body onload="init('curve-subdivision-bug-3.svg');" onunload="unload();"> +<h1>Curve Subdivision Bug 3</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 500px; height: 200px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-3.svg b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-3.svg new file mode 100644 index 0000000..590709c --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/curve-subdivision-bug-3.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="500" + height="500" + id="svg4764"> + <defs + id="defs4820" /> + <path + d="M 224.66972,14.854959 C 224.66972,14.854959 270.94528,53.002149 173.7018,82.503222 -11.208535,138.5111 -596.06334,155.40506 -758.50751,84.744408 L -796.71625,138.99988 C -143.44562,244.88947 394.09814,91.305029 224.66972,14.854959 L 224.66972,14.854959 z" + id="path4772" + style="fill:#0074bd;stroke:none" /> +</svg> diff --git a/o3d/samples/gpu2d/regression-tests/orientation-bug-1.html b/o3d/samples/gpu2d/regression-tests/orientation-bug-1.html new file mode 100644 index 0000000..3790d61 --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/orientation-bug-1.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +Orientation Determination Bug +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="../svgloader.js"></script> +<script type="text/javascript" src="../svgsample.js"></script> +</head> +<body onload="init('orientation-bug-1.svg');" onunload="unload();"> +<h1>Orientation Determination Bug</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 800px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/regression-tests/orientation-bug-1.svg b/o3d/samples/gpu2d/regression-tests/orientation-bug-1.svg new file mode 100644 index 0000000..aa93f17 --- /dev/null +++ b/o3d/samples/gpu2d/regression-tests/orientation-bug-1.svg @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="500" + height="500" + id="svg3012" + version="1.1" + inkscape:version="0.47 r22583" + sodipodi:docname="a.svg"> + <defs + id="defs3014"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective3020" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1" + inkscape:cx="185.77464" + inkscape:cy="230.99609" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:window-width="939" + inkscape:window-height="861" + inkscape:window-x="723" + inkscape:window-y="0" + inkscape:window-maximized="0" /> + <metadata + id="metadata3017"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(-189.22536,329.0208)"> + <g + style="font-size:600px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans" + id="text3078" + transform="translate(15,9)"> + <path + d="m 402.88942,-106.96221 c -43.55485,1.7e-4 -73.7306,4.98063 -90.52734,14.941408 -16.79698,9.961078 -25.19541,26.953249 -25.19532,50.976563 -9e-5,19.140705 6.24991,34.3750646 18.75,45.703125 12.69519,11.132855 29.88268,16.699255 51.5625,16.699219 29.88263,3.6e-5 53.80838,-10.546828 71.77735,-31.640625 18.16381,-21.288973 27.24583,-49.511601 27.24609,-84.667969 l 0,-12.011721 -53.61328,0 m 107.51953,-22.26562 0,187.207028 -53.90625,0 0,-49.804687 c -12.30493,19.921905 -27.63695,34.667984 -45.99609,44.238281 -18.35957,9.374996 -40.82049,14.062492 -67.38282,14.0625 -33.59386,-8e-6 -60.35164,-9.374999 -80.27343,-28.125 -19.72661,-18.945274 -29.58988,-44.2382172 -29.58985,-75.878906 -3e-5,-36.91393 12.30464,-64.745936 36.91407,-83.496096 24.80459,-18.7498 61.71861,-28.12479 110.74218,-28.125 l 75.58594,0 0,-5.27344 c -2.6e-4,-24.80445 -8.20338,-43.94505 -24.60937,-57.42187 -16.21116,-13.67159 -39.0627,-20.50752 -68.55469,-20.50781 -18.75015,2.9e-4 -37.01185,2.24638 -54.78516,6.73828 -17.77353,4.49247 -34.86336,11.23074 -51.26953,20.21484 l 0,-49.80469 c 19.72648,-7.61686 38.86709,-13.28092 57.42188,-16.99218 18.55455,-3.90592 36.62094,-5.85904 54.19921,-5.85938 47.46072,3.4e-4 82.90991,12.30501 106.34766,36.91406 23.4372,24.60965 35.15594,61.9143 35.15625,111.91407" + id="path3083" /> + </g> + </g> +</svg> diff --git a/o3d/samples/gpu2d/svg_a.html b/o3d/samples/gpu2d/svg_a.html new file mode 100644 index 0000000..c770276 --- /dev/null +++ b/o3d/samples/gpu2d/svg_a.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +SVG Letter A +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="svgloader.js"></script> +<script type="text/javascript" src="svgsample.js"></script> +</head> +<body onload="init('a.svg');" onunload="unload();"> +<h1>SVG Letter A</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 800px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/svg_crescent.html b/o3d/samples/gpu2d/svg_crescent.html new file mode 100644 index 0000000..324a654 --- /dev/null +++ b/o3d/samples/gpu2d/svg_crescent.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +SVG Crescent +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="svgloader.js"></script> +<script type="text/javascript" src="svgsample.js"></script> +</head> +<body onload="init('crescent.svg');" onunload="unload();"> +<h1>SVG Crescent</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 800px; height: 600px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/svg_google_logo.html b/o3d/samples/gpu2d/svg_google_logo.html new file mode 100644 index 0000000..8b29944 --- /dev/null +++ b/o3d/samples/gpu2d/svg_google_logo.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +SVG Google Logo +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="svgloader.js"></script> +<script type="text/javascript" src="svgsample.js"></script> +</head> +<body onload="init('Google_Logo.svg');" onunload="unload();"> +<h1>SVG Google Logo</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 800px; height: 400px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/svg_java_logo.html b/o3d/samples/gpu2d/svg_java_logo.html new file mode 100644 index 0000000..b818338 --- /dev/null +++ b/o3d/samples/gpu2d/svg_java_logo.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +SVG Java Logo +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="svgloader.js"></script> +<script type="text/javascript" src="svgsample.js"></script> +</head> +<body onload="init('javalogo.svg');" onunload="unload();"> +<h1>SVG Java Logo</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 800px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/svg_thin_crescent.html b/o3d/samples/gpu2d/svg_thin_crescent.html new file mode 100644 index 0000000..b2b9f1f --- /dev/null +++ b/o3d/samples/gpu2d/svg_thin_crescent.html @@ -0,0 +1,51 @@ +<!-- +Copyright 2010, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<html> +<head> +<title> +SVG Thin Crescent +</title> +<!-- Include sample javascript library functions--> +<script type="text/javascript" src="../o3djs/base.js"></script> +<!-- Include SVG sample and dependencies --> +<script type="text/javascript" src="../third_party/xmljs/tinyxmlsax.js"></script> +<script type="text/javascript" src="svgloader.js"></script> +<script type="text/javascript" src="svgsample.js"></script> +</head> +<body onload="init('thincrescent.svg');" onunload="unload();"> +<h1>SVG Thin Crescent</h1> +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 800px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/gpu2d/svgloader.js b/o3d/samples/gpu2d/svgloader.js new file mode 100644 index 0000000..5a88415 --- /dev/null +++ b/o3d/samples/gpu2d/svgloader.js @@ -0,0 +1,974 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @fileoverview Loads SVG files and creates o3djs.gpu2d.Paths from + * them, inserting all of the associated geometric nodes under the + * passed Transform. + * <P> + * This file depends on the O3D APIs, o3djs.base, o3djs.io, o3djs.math, + * o3djs.gpu2d, and the XML for <SCRIPT> parser available from + * http://xmljs.sourceforge.net/ . + */ + +/** + * Constructs a new SVGLoader. + * @constructor + */ +function SVGLoader() { +} + +/** + * Helper function to defer the execution of another function. + * @param {function(): void} func function to execute later. + * @private + */ +function runLater_(func) { + setTimeout(func, 0); +} + +/** + * Loads an SVG file at the given URL. The file is downloaded in the + * background. Graphical objects are allocated in the given Pack, and + * created materials are registered under the given DrawList, + * typically the zOrderedDrawList from an + * o3djs.rendergraph.ViewInfo. The created Shapes are parented under + * the given Transform. If the optional completion callback is + * specified, it is called once the file has been downloaded and + * processed. + * @param {string} url URL of the SVG file to load. + * @param {boolean} flipY Whether the Y coordinates should be flipped + * to better match the 3D coordinate system. + * @param {!o3d.Pack} pack Pack to manage created objects. + * @param {!o3d.DrawList} drawList DrawList to use for created + * materials. + * @param {!o3d.Transform} transform Transform under which to place + * created Shapes. + * @param {function(string, *): void} + * opt_completionCallback Optional completion callback which is + * called after the file has been processed. The URL of the file + * is passed as the first argument. The second argument indicates + * whether the file was loaded successfully (true) or not (false). + * The third argument is an error detail if the file failed to + * load successfully. + */ +SVGLoader.prototype.load = function(url, + flipY, + pack, + drawList, + transform, + opt_completionCallback) { + var that = this; + o3djs.io.loadTextFile(url, function(text, exception) { + if (exception) { + runLater_(function() { + if (opt_completionCallback) + opt_completionCallback(url, + false, + e); + }); + } else { + runLater_(function() { + try { + that.parse_(text, + flipY, + pack, + drawList, + transform); + if (opt_completionCallback) + opt_completionCallback(url, true, null); + } catch (e) { + if (window.console) + window.console.log(e); + if (opt_completionCallback) + opt_completionCallback(url, false, e); + } + }); + } + }); +}; + +/** + * Does the parsing of the SVG file. + * @param {string} svgText the text of the SVG file. + * @param {boolean} flipY Whether the Y coordinates should be flipped + * to better match the 3D coordinate system. + * @param {!o3d.Pack} pack Pack to manage created objects. + * @param {!o3d.DrawList} drawList DrawList to use for created + * materials. + * @param {!o3d.Transform} transform Transform under which to place + * created Shapes. + * @private + */ +SVGLoader.prototype.parse_ = function(svgText, + flipY, + pack, + drawList, + transform) { + /** + * The pack in which to create shapes, materials, etc. + * @type {!o3d.Pack} + * @private + */ + this.pack_ = pack; + + /** + * Stack of matrices. Entering a new graphics context pushes a new + * matrix. + * @type {!Array.<!o3djs.math.Matrix4>} + * @private + */ + this.matrixStack_ = []; + + /** + * Stack of transforms. Entering a new graphics context pushes a new + * transform. + * @type {!Array.<!o3d.Transform>} + * @private + */ + this.transformStack_ = []; + + this.matrixStack_.push(o3djs.math.identity(4)); + this.transformStack_.push(transform); + var parser = new SAXDriver(); + var eventHandler = new SVGSAXHandler_(this, + parser, + flipY, + pack, + drawList); + parser.setDocumentHandler(eventHandler); + parser.parse(svgText); +}; + +/** + * Returns the current transform. + * @return {!o3d.Transform} + * @private + */ +SVGLoader.prototype.currentTransform_ = function() { + var len = this.transformStack_.length; + return this.transformStack_[len - 1]; +}; + +/** + * Returns the current matrix. + * @return {!o3djs.math.Matrix4} + * @private + */ +SVGLoader.prototype.currentMatrix_ = function() { + var len = this.matrixStack_.length; + return this.matrixStack_[len - 1]; +}; + +/** + * Sets the current matrix. + * @param {!o3djs.math.Matrix4} matrix the new current matrix. + * @private + */ +SVGLoader.prototype.setCurrentMatrix_ = function(matrix) { + var length = this.matrixStack_.length; + this.matrixStack_[length - 1] = matrix; + this.transformStack_[length - 1].localMatrix = matrix; +}; + +/** + * Pushes a new transform / matrix pair. + * @private + */ +SVGLoader.prototype.pushTransform_ = function() { + this.matrixStack_.push(o3djs.math.identity(4)); + var xform = this.pack_.createObject('o3d.Transform'); + xform.parent = this.currentTransform_(); + this.transformStack_.push(xform); +}; + +/** + * Pops a transform / matrix pair. + * @private + */ +SVGLoader.prototype.popTransform_ = function() { + this.matrixStack_.pop(); + this.transformStack_.pop(); +}; + +/** + * Supports the "matrix" command in the graphics context; not yet + * implemented. + * @param {number} a matrix element + * @param {number} b matrix element + * @param {number} c matrix element + * @param {number} d matrix element + * @param {number} e matrix element + * @param {number} f matrix element + * @private + */ +SVGLoader.prototype.matrix_ = function(a, b, c, d, e, f) { + // TODO(kbr): implement + throw 'matrix command not yet implemented'; +}; + +/** + * Supports the "translate" command in the graphics context. + * @param {number} x x translation + * @param {number} y y translation + * @private + */ +SVGLoader.prototype.translate_ = function(x, y) { + var tmp = o3djs.math.matrix4.translation([x, y, 0]); + this.setCurrentMatrix_( + o3djs.math.mulMatrixMatrix(tmp, this.currentMatrix_())); +}; + +/** + * Supports the "scale" command in the graphics context; not yet + * implemented. + * @param {number} sx x scale + * @param {number} sy y scale + * @private + */ +SVGLoader.prototype.scale_ = function(sx, sy) { + // TODO(kbr): implement + throw 'scale command not yet implemented'; +}; + +/** + * Supports the "rotate" command in the graphics context. + * @param {number} angle angle to rotate, in degrees. + * @param {number} cx x component of rotation center. + * @param {number} cy y component of rotation center. + * @private + */ +SVGLoader.prototype.rotate_ = function(angle, cx, cy) { + var rot = o3djs.math.matrix4.rotationZ(o3djs.math.degToRad(angle)); + if (cx || cy) { + var xlate1 = o3djs.math.matrix4.translation([cx, cy, 0]); + var xlate2 = o3djs.math.matrix4.translation([-cx, -cy, 0]); + rot = o3djs.math.mulMatrixMatrix(xlate2, rot); + rot = o3djs.math.mulMatrixMatrix(rot, xlate1); + } + this.setCurrentMatrix_(rot); +}; + +/** + * Supports the "skewX" command in the graphics context; not yet + * implemented. + * @param {number} angle skew X angle, in degrees. + * @private + */ +SVGLoader.prototype.skewX_ = function(angle) { + // TODO(kbr): implement + throw 'skewX command not yet implemented'; +}; + +/** + * Supports the "skewY" command in the graphics context; not yet + * implemented. + * @param {number} angle skew Y angle, in degrees. + * @private + */ +SVGLoader.prototype.skewY_ = function(angle) { + // TODO(kbr): implement + throw 'skewY command not yet implemented'; +}; + +/** + * Parses the data from an SVG path element, constructing an + * o3djs.gpu2d.Path from it. + * @param {string} pathData the path's data (the "d" attribute). + * @param {number} lineNumber the line number of the current parse, + * for error reporting. + * @param {boolean} flipY Whether the Y coordinates should be flipped + * to better match the 3D coordinate system. + * @param {!o3d.Pack} pack Pack to manage created objects. + * @param {!o3d.DrawList} drawList DrawList to use for created + * materials. + * @private + */ +SVGLoader.prototype.parsePath_ = function(pathData, + lineNumber, + flipY, + pack, + drawList) { + var parser = new PathDataParser_(this, + lineNumber, + flipY, + pack, + drawList); + var path = parser.parse(pathData); + if (this.fill_) { + path.setFill(this.fill_); + } + this.currentTransform_().addShape(path.shape); +}; + +/** + * Parses the style from an SVG path element or graphics context, + * preparing to set it on the next created o3djs.gpu2d.Path. If it + * doesn't know how to handle it, the default color of solid black + * will be used. + * @param {string} styleData the string containing the "style" + * attribute. + * @param {number} lineNumber the line number of the current parse, + * for error reporting. + * @param {!o3d.Pack} pack Pack to manage created objects. + * @private + */ +SVGLoader.prototype.parseStyle_ = function(styleData, + lineNumber, + pack) { + this.fill_ = null; + var portions = styleData.split(';'); + for (var i = 0; i < portions.length; i++) { + var keyVal = portions[i].split(':'); + var key = keyVal[0]; + var val = keyVal[1]; + if (key == 'stroke') { + // TODO(kbr): support strokes + } else if (key == 'stroke-width') { + // TODO(kbr): support stroke width + } else if (key == 'fill') { + if (val.charAt(0) == '#') { + var r = parseInt(val.substr(1, 2), 16); + var g = parseInt(val.substr(3, 2), 16); + var b = parseInt(val.substr(5, 2), 16); + var fill = o3djs.gpu2d.createColor(pack, + r / 255.0, + g / 255.0, + b / 255.0, + 1.0); + this.fill_ = fill; + } else if (val.substr(0, 4) == 'rgb(' && + val.charAt(val.length - 1) == ')') { + var rgbStrings = val.substr(4, val.length - 5).split(','); + var r = parseInt(rgbStrings[0]); + var g = parseInt(rgbStrings[1]); + var b = parseInt(rgbStrings[2]); + var fill = o3djs.gpu2d.createColor(pack, + r / 255.0, + g / 255.0, + b / 255.0, + 1.0); + this.fill_ = fill; + } + } + } +}; + +/** + * Parses the data from an SVG transform attribute, storing the result + * in the current o3d.Transform. + * @param {string} data the transform's data. + * @param {number} lineNumber the line number of the current parse, + * for error reporting. + * @private + */ +SVGLoader.prototype.parseTransform_ = function(data, + lineNumber) { + var parser = new TransformDataParser_(this, + lineNumber); + parser.parse(data); +}; + +//---------------------------------------------------------------------- +// BaseDataParser -- base class for parsers dealing with SVG data + +/** + * Base class for parsers dealing with SVG data. + * @param {SVGLoader} loader The SVG loader. + * @param {number} lineNumber the line number of the current parse, + * for error reporting. + * @constructor + * @private + */ +BaseDataParser_ = function(loader, + lineNumber) { + this.loader_ = loader; + this.lineNumber_ = lineNumber; + this.putBackToken_ = null; +} + +/** + * Types of tokens. + * @enum + * @private + */ +BaseDataParser_.TokenTypes = { + WORD: 1, + NUMBER: 2, + LPAREN: 3, + RPAREN: 4, + NONE: 5 +}; + +/** + * Parses the given SVG data. + * @param {string} data the SVG data. + * @private + */ +BaseDataParser_.prototype.parse = function(data) { + var parseState = { + // First index of current token + firstIndex: 0, + // Last index of current token (exclusive) + lastIndex: 0, + // Line number of the path element + lineNumber: this.lineNumber_ + }; + var done = false; + while (!done) { + var tok = this.nextToken_(parseState, data); + switch (tok.kind) { + case BaseDataParser_.TokenTypes.WORD: + // Allow the parsing to override the specification of the last + // command + this.setLastCommand_(tok.val); + this.parseWord_(parseState, data, tok.val); + break; + case BaseDataParser_.TokenTypes.NUMBER: + // Assume this is a repeat of the last command + this.putBack_(tok); + this.parseWord_(parseState, data, this.lastCommand_); + break; + default: + done = true; + break; + } + } +}; + +/** + * Sets the last parsed command. + * @param {string} command the last parsed command. + * @private + */ +BaseDataParser_.prototype.setLastCommand_ = function(command) { + this.lastCommand_ = command; +}; + +/** + * Returns true if the given character is a whitespace or separator. + * @param {string} c the character to test. + * @private + */ +BaseDataParser_.prototype.isWhitespaceOrSeparator_ = function(c) { + return (c == ',' || + c == ' ' || + c == '\t' || + c == '\r' || + c == '\n'); +}; + +/** + * Puts back a token to be consumed during the next iteration. There + * is only a one-token put back buffer. + * @param {!{kind: BaseDataParser_TokenTypes, val: string}} tok The + * token to put back. + * @private + */ +BaseDataParser_.prototype.putBack_ = function(tok) { + this.putBackToken_ = tok; +}; + +/** + * Returns the next token. + * @param {!{firstIndex: number, lastIndex: number, lineNumber: + * number}} parseState The parse state. + * @param {string} data The data being parsed. + * @private + */ +BaseDataParser_.prototype.nextToken_ = function(parseState, data) { + if (this.putBackToken_) { + var tmp = this.putBackToken_; + this.putBackToken_ = null; + return tmp; + } + parseState.firstIndex = parseState.lastIndex; + if (parseState.firstIndex < data.length) { + // Eat whitespace and separators + while (true) { + var curChar = data.charAt(parseState.firstIndex); + if (this.isWhitespaceOrSeparator_(curChar)) { + ++parseState.firstIndex; + if (parseState.firstIndex >= data.length) + break; + } else { + break; + } + } + } + if (parseState.firstIndex >= data.length) + return { kind: BaseDataParser_.TokenTypes.NONE, val: null }; + parseState.lastIndex = parseState.firstIndex; + // Surround the next token + var curChar = data.charAt(parseState.lastIndex++); + if (curChar == '-' || + curChar == '.' || + (curChar >= '0' && curChar <= '9')) { + while (true) { + var t = data.charAt(parseState.lastIndex); + if (t == '.' || + (t >= '0' && t <= '9')) { + ++parseState.lastIndex; + } else { + break; + } + } + // See whether an exponential format follows: i.e. 136e-3 + if (data.charAt(parseState.lastIndex) == 'e') { + ++parseState.lastIndex; + if (data.charAt(parseState.lastIndex) == '-') { + ++parseState.lastIndex; + } + while (true) { + var t = data.charAt(parseState.lastIndex); + if (t >= '0' && t <= '9') { + ++parseState.lastIndex; + } else { + break; + } + } + } + return { kind: BaseDataParser_.TokenTypes.NUMBER, + val: parseFloat(data.substring(parseState.firstIndex, + parseState.lastIndex)) }; + } else if ((curChar >= 'A' && curChar <= 'Z') || + (curChar >= 'a' && curChar <= 'z')) { + // Consume all adjacent letters -- this is satisfactory both for + // the grammars of the "transform" and "d" attributes + while (true) { + var t = data.charAt(parseState.lastIndex); + if ((t >= 'A' && t <= 'Z') || + (t >= 'a' && t <= 'z')) { + ++parseState.lastIndex; + } else { + break; + } + } + return { kind: BaseDataParser_.TokenTypes.WORD, + val: data.substring(parseState.firstIndex, + parseState.lastIndex) }; + } else if (curChar == '(') { + return { kind: BaseDataParser_.TokenTypes.LPAREN, + val: data.substring(parseState.firstIndex, + parseState.lastIndex) }; + } else if (curChar == ')') { + return { kind: BaseDataParser_.TokenTypes.RPAREN, + val: data.substring(parseState.firstIndex, + parseState.lastIndex) }; + } + throw 'Expected number or word at line ' + parseState.lineNumber; +}; + +/** + * Verifies that the next token is of the given kind, throwing an + * exception if not. + * @param {!{firstIndex: number, lastIndex: number, lineNumber: + * number}} parseState The parse state. + * @param {string} data The data being parsed. + * @param {BaseDataParser_TokenTypes} tokenType The expected token + * type. + */ +BaseDataParser_.prototype.expect_ = function(parseState, data, tokenType) { + var tok = this.nextToken_(parseState, data); + if (tok.kind != tokenType) { + throw 'At line number ' + parseState.lineNumber + + ': expected token type ' + tokenType + + ', got ' + tok.kind; + } +}; + +/** + * Parses a series of floating-point numbers. + * @param {!{firstIndex: number, lastIndex: number, lineNumber: + * number}} parseState The parse state. + * @param {string} data The data being parsed. + * @param {number} numFloats The number of floating-point numbers to + * parse. + * @return {!Array.<number>} + * @private + */ +BaseDataParser_.prototype.parseFloats_ = function(parseState, + data, + numFloats) { + var result = []; + for (var i = 0; i < numFloats; ++i) { + var tok = this.nextToken_(parseState, data); + if (tok.kind != BaseDataParser_.TokenTypes.NUMBER) + throw "Expected number at line " + + parseState.lineNumber + + ", character " + + parseState.firstIndex + + "; got \"" + tok.val + "\""; + result.push(tok.val); + } + return result; +}; + +//---------------------------------------------------------------------- +// PathDataParser + +/** + * Parser for the data in an SVG path. + * @param {SVGLoader} loader The SVG loader. + * @param {number} lineNumber the line number of the current parse, + * for error reporting. + * @param {boolean} flipY Whether the Y coordinates should be flipped + * to better match the 3D coordinate system. + * @param {!o3d.Pack} pack Pack to manage created objects. + * @param {!o3d.DrawList} drawList DrawList to use for created + * materials. + * @constructor + * @extends {BaseDataParser_} + * @private + */ +PathDataParser_ = function(loader, + lineNumber, + flipY, + pack, + drawList) { + BaseDataParser_.call(this, loader, lineNumber); + this.flipY_ = flipY; + this.pack_ = pack; + this.drawList_ = drawList; + this.curX_ = 0; + this.curY_ = 0; +}; + +o3djs.base.inherit(PathDataParser_, BaseDataParser_); + +/** + * Parses data from an SVG path. + * @param {string} data SVG path data. + * @private + */ +PathDataParser_.prototype.parse = function(data) { + this.path_ = o3djs.gpu2d.createPath(this.pack_, this.drawList_); + BaseDataParser_.prototype.parse.call(this, data); + this.path_.update(); + return this.path_; +}; + +/** + * Parses the data for the given word (command) from the path data. + * @param {!{firstIndex: number, lastIndex: number, lineNumber: + * number}} parseState The parse state. + * @param {string} data The data being parsed. + * @param {string} word The character for the current command. + * @private + */ +PathDataParser_.prototype.parseWord_ = function(parseState, + data, + word) { + if (word == 'M' || word == 'm') { + var absolute = (word == 'M'); + var coords = this.parseFloats_(parseState, data, 2); + this.doYFlip_(coords); + if (!absolute) { + this.makeAbsolute_(coords); + } + this.path_.moveTo(coords[0], coords[1]); + this.curX_ = coords[0]; + this.curY_ = coords[1]; + // This is a horrible portion of the SVG spec + if (absolute) { + this.setLastCommand_('L'); + } else { + this.setLastCommand_('l'); + } + } else if (word == 'L' || word == 'l') { + var absolute = (word == 'L'); + var coords = this.parseFloats_(parseState, data, 2); + this.doYFlip_(coords); + if (!absolute) { + this.makeAbsolute_(coords); + } + this.path_.lineTo(coords[0], coords[1]); + this.curX_ = coords[0]; + this.curY_ = coords[1]; + } else if (word == 'H' || word == 'h') { + var absolute = (word == 'H'); + var coords = this.parseFloats_(parseState, data, 1); + if (!absolute) { + coords[0] += this.curX_; + } + this.path_.lineTo(coords[0], this.curY_); + this.curX_ = coords[0]; + } else if (word == 'V' || word == 'v') { + var absolute = (word == 'V'); + var coords = this.parseFloats_(parseState, data, 1); + if (this.flipY_) { + coords[0] = -coords[0]; + } + if (!absolute) { + coords[0] += this.curY_; + } + this.path_.lineTo(this.curX_, coords[0]); + this.curY_ = coords[0]; + } else if (word == 'Q' || word == 'q') { + var absolute = (word == 'Q'); + var coords = this.parseFloats_(parseState, data, 4); + this.doYFlip_(coords); + if (!absolute) { + this.makeAbsolute_(coords); + } + this.path_.quadraticTo(coords[0], coords[1], coords[2], coords[3]); + this.curX_ = coords[2]; + this.curY_ = coords[3]; + // TODO(kbr): support shorthand quadraticTo (T/t) + } else if (word == 'C' || word == 'c') { + var absolute = (word == 'C'); + var coords = this.parseFloats_(parseState, data, 6); + this.doYFlip_(coords); + if (!absolute) { + this.makeAbsolute_(coords); + } + this.path_.cubicTo(coords[0], coords[1], + coords[2], coords[3], + coords[4], coords[5]); + this.curX_ = coords[4]; + this.curY_ = coords[5]; + // TODO(kbr): support shorthand cubicTo (S/s) + } else if (word == 'Z' || word == 'z') { + this.path_.close(); + } else { + throw 'Unknown word ' + word + ' at line ' + parseState.lineNumber; + } +}; + +/** + * Negates the Y coordinates of the passed 2D coordinate list if the + * flipY flag is set on this parser. + * @param {!Array.<number>} coords Array of 2D coordinates. + * @private + */ + +PathDataParser_.prototype.doYFlip_ = function(coords) { + if (this.flipY_) { + for (var i = 0; i < coords.length; i += 2) { + coords[i+1] = -coords[i+1]; + } + } +}; + +/** + * Transforms relative coordinates to absolute coordinates. + * @param {!Array.<number>} coords Array of 2D coordinates. + * @private + */ +PathDataParser_.prototype.makeAbsolute_ = function(coords) { + for (var i = 0; i < coords.length; i += 2) { + coords[i] += this.curX_; + coords[i+1] += this.curY_; + } +}; + +//---------------------------------------------------------------------- +// TransformDataParser + +/** + * Parser for the data in an SVG transform. + * @param {SVGLoader} loader The SVG loader. + * @param {number} lineNumber the line number of the current parse, + * for error reporting. + * @constructor + * @extends {BaseDataParser_} + * @private + */ +TransformDataParser_ = function(loader, + lineNumber) { + BaseDataParser_.call(this, loader, lineNumber); +}; + +o3djs.base.inherit(TransformDataParser_, BaseDataParser_); + +/** + * Parses the data for the given word (command) from the path data. + * @param {!{firstIndex: number, lastIndex: number, lineNumber: + * number}} parseState The parse state. + * @param {string} data The data being parsed. + * @param {string} word The character for the current command. + * @private + */ +TransformDataParser_.prototype.parseWord_ = function(parseState, + data, + word) { + if (!((word == 'matrix') || + (word == 'translate') || + (word == 'scale') || + (word == 'rotate') || + (word == 'skewX') || + (word == 'skewY'))) { + throw 'Unknown transform definition ' + word + + ' at line ' + parseState.lineNumber; + } + + this.expect_(parseState, data, BaseDataParser_.TokenTypes.LPAREN); + // Some of the commands take a variable number of arguments + var floats; + switch (word) { + case 'matrix': + floats = this.parseFloats_(parseState, data, 6); + this.loader_.matrix_(floats[0], floats[1], + floats[2], floats[3], + floats[4], floats[5]); + break; + case 'translate': + floats = this.parseFloats_(parseState, data, 1); + var tok = this.nextToken_(parseState, data); + if (tok.kind == BaseDataParser_.TokenTypes.NUMBER) { + floats.push(tok.val); + } else { + this.putBack_(tok); + floats.push(0); + } + this.loader_.translate_(floats[0], floats[1]); + break; + case 'scale': + floats = this.parseFloats_(parseState, data, 1); + var tok = this.nextToken_(parseState, data); + if (tok.kind == BaseDataParser_.TokenTypes.NUMBER) { + floats.push(tok.val); + } else { + this.putBack_(tok); + floats.push(floats[0]); + } + this.loader_.scale_(floats[0], floats[1]); + break; + case 'rotate': + floats = this.parseFloats_(parseState, data, 1); + var tok = this.nextToken_(parseState, data); + this.putBack_(tok); + if (tok.kind == BaseDataParser_.TokenTypes.NUMBER) { + floats = floats.concat(this.parseFloats_(parseState, data, 2)); + } else { + floats.push(0); + floats.push(0); + } + this.loader_.rotate_(floats[0], floats[1], floats[2]); + break; + case 'skewX': + floats = this.parseFloats_(parseState, data, 1); + this.loader_.skewX_(floats[0]); + break; + case 'skewY': + floats = this.parseFloats_(parseState, data, 1); + this.loader_.skewY_(floats[0]); + break; + default: + throw 'Unknown word ' + word + ' at line ' + parseState.lineNumber; + } + this.expect_(parseState, data, BaseDataParser_.TokenTypes.RPAREN); +}; + +//---------------------------------------------------------------------- +// SVGSaxHandler + +/** + * Handler which integrates with the XMLJS SAX parser. + * @param {!SVGLoader} loader The SVG loader. + * @param {Object} parser the XMLJS SAXDriver. + * @param {boolean} flipY Whether the Y coordinates should be flipped + * to better match the 3D coordinate system. + * @param {!o3d.Pack} pack Pack to manage created objects. + * @param {!o3d.DrawList} drawList DrawList to use for created + * materials. + * @constructor + * @private + */ +SVGSAXHandler_ = function(loader, parser, flipY, pack, drawList) { + this.loader_ = loader; + this.parser_ = parser; + this.flipY_ = flipY; + this.pack_ = pack; + this.drawList_ = drawList; +}; + +/** + * Handler for the start of an element. + * @param {string} name the name of the element. + * @param {Object} attributes the XMLJS attributes object. + * @private + */ +SVGSAXHandler_.prototype.startElement = function(name, attributes) { + switch (name) { + case 'path': + var pushed = false; + var transformData = attributes.getValueByName('transform'); + if (transformData) { + pushed = true; + this.loader_.pushTransform_(); + this.loader_.parseTransform_(transformData, + this.parser_.getLineNumber()); + } + if (attributes.getValueByName('style')) { + this.loader_.parseStyle_(attributes.getValueByName('style'), + this.parser_.getLineNumber(), + this.pack_); + } + this.loader_.parsePath_(attributes.getValueByName('d'), + this.parser_.getLineNumber(), + this.flipY_, + this.pack_, + this.drawList_); + if (pushed) { + this.loader_.popTransform_(); + } + break; + case 'g': + this.loader_.pushTransform_(); + if (attributes.getValueByName('style')) { + this.loader_.parseStyle_(attributes.getValueByName('style'), + this.parser_.getLineNumber(), + this.pack_); + } + var data = attributes.getValueByName('transform'); + if (data) { + this.loader_.parseTransform_(data, + this.parser_.getLineNumber()); + } + break; + default: + // No other commands supported yet + break; + } +}; + +/** + * Handler for the end of an element. + * @param {string} name the name of the element. + * @private + */ +SVGSAXHandler_.prototype.endElement = function(name) { + if (name == 'g') { + this.loader_.popTransform_(); + } +}; + diff --git a/o3d/samples/gpu2d/svgsample.js b/o3d/samples/gpu2d/svgsample.js new file mode 100644 index 0000000..c041c74 --- /dev/null +++ b/o3d/samples/gpu2d/svgsample.js @@ -0,0 +1,226 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @fileoverview Framework for samples that load and display SVG files. + * + * This is purely *example* code, showing how to use the SVG loader. + */ + +o3djs.require('o3djs.cameracontroller'); +o3djs.require('o3djs.event'); +o3djs.require('o3djs.gpu2d'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.picking'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.util'); +// Also requires the following files to be loaded by the containing page: +// - svgloader.js +// - ../third_party/xmljs/tinyxmlsax.js + +// Globals +var g_filename; +var g_o3d; +var g_math; +var g_client; +var g_pack; +var g_viewInfo; +var g_o3dElement; +var g_finished = false; // for selenium testing. +var g_clientWidth; +var g_clientHeight; +var g_cameraController; + +if (!("console" in window)) { + var logElement = document.getElementById('log'); + window.console = + { log: function(s) { + if (logElement) { + logElement.innerHTML = logElement.innerHTML + "<span>" + + s.toString() + "</span><br>"; + } + } + }; +} + +/** + * Initializes the sample with the given URL. + * @param {string} filename The URL of the SVG file to load. + */ +function init(filename) { + g_filename = filename; + o3djs.util.makeClients(initStep2); +} + +/** + * Remove any callbacks so they don't get called after the page has unloaded. + */ +function unload() { + if (g_client) { + g_client.cleanup(); + } +} + +/** + * Completes initialization of the sample. + * @param {!Array.<!Element>} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_o3dElement = o3dElement; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + g_client = o3dElement.client; + + // Creates a pack to manage our resources/assets + g_pack = g_client.createPack(); + + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set the background color to light gray. + g_viewInfo.clearBuffer.clearColor = [0.8, 0.8, 0.8, 1]; + + // Load the file. + var loader = new SVGLoader(); + loader.load(g_filename, + true, + g_pack, + g_viewInfo.zOrderedDrawList, + g_client.root, + function(url, success, detail) { + if (!success) { + window.console.log('Failed to load ' + url + ": " + detail); + } else { + var tmpManager = + o3djs.picking.createPickManager(g_client.root); + tmpManager.update(); + var bbox = tmpManager.getTransformInfo(g_client.root). + getBoundingBox(); + g_cameraController.viewAll(bbox, + g_client.width / g_client.height); + updateViewAndProjectionMatrices(); + } + }); + + // Set up the view and projection transformations. + initContext(); + + // Set up event handlers for mouse interaction. + o3djs.event.addEventListener(o3dElement, 'mousedown', onMouseDown); + o3djs.event.addEventListener(o3dElement, 'mousemove', onMouseMove); + o3djs.event.addEventListener(o3dElement, 'mouseup', onMouseUp); + + g_finished = true; // for selenium testing. +} + +/** + * Event handler that gets called when a mouse click takes place in + * the O3D element. It changes the state of the camera controller + * based on which modifier keys are pressed. + * @param {!Event} e The mouse down event. + */ +function onMouseDown(e) { + if (e.button == 0) { + if (!e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.MOVE_CENTER_IN_VIEW_PLANE, e.x, e.y); + } else if (e.metaKey || e.altKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.SPIN_ABOUT_CENTER, e.x, e.y); + } else if (!e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_IN_OUT, e.x, e.y); + } else if (e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.ZOOM_IN_OUT, e.x, e.y); + } else if (e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_ZOOM, e.x, e.y); + } + } +} + +/** + * Event handler that gets called when a mouse move event takes place + * in the O3D element. It tells the camera controller that the mouse + * has moved. + * @param {!Event} e The mouse move event. + */ +function onMouseMove(e) { + g_cameraController.mouseMoved(e.x, e.y); +} + +/** + * Event handler that gets called when a mouse up event takes place in + * the O3D element. It tells the camera controller that the mouse has + * been released. + * @param {!Event} e The mouse up event. + */ +function onMouseUp(e) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.NONE, e.x, e.y); +} + +/** + * Sets up reasonable view and projection matrices. + */ +function initContext() { + // Set up our CameraController. + g_cameraController = o3djs.cameracontroller.createCameraController( + [0, 0, 0], // centerPos + 500, // backpedal + 0, // heightAngle + 0, // rotationAngle + g_math.degToRad(15), // fieldOfViewAngle + updateViewAndProjectionMatrices); // opt_onChange + g_cameraController.distancePerUnit = 100.0; + + updateViewAndProjectionMatrices(); +} + +/** + * Updates the view and projection matrices. + */ +function updateViewAndProjectionMatrices() { + g_viewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + + // Set up a perspective transformation for the projection. + g_viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_cameraController.fieldOfViewAngle * 2, // Frustum angle. + g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. + 1, // Near plane. + 5000); // Far plane. +} + diff --git a/o3d/samples/gpu2d/thincrescent.svg b/o3d/samples/gpu2d/thincrescent.svg new file mode 100644 index 0000000..e31717d --- /dev/null +++ b/o3d/samples/gpu2d/thincrescent.svg @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="500" + height="500" + id="svg4075"> + <defs + id="defs4077" /> + <g + transform="translate(0,-552.36218)" + id="layer1"> + <path + d="M 221,60 C -330,318 813.95124,298.69865 237,60 754,300 -275,299 221,60 z" + transform="translate(0,552.36218)" + id="path4085" + style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> +</svg> diff --git a/o3d/samples/o3djs/gpu2d.js b/o3d/samples/o3djs/gpu2d.js new file mode 100644 index 0000000..4e19322 --- /dev/null +++ b/o3d/samples/o3djs/gpu2d.js @@ -0,0 +1,680 @@ +/* + * Copyright 2010, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/** + * @fileoverview This file provides GPU-accelerated rendering of 2D + * vector graphics in 3D. + */ +o3djs.provide('o3djs.gpu2d'); + +/** + * A module providing GPU-accelerated rendering of 2D vector graphics + * in 3D. + * @namespace + */ +o3djs.gpu2d = o3djs.gpu2d || {}; + +/** + * Creates a new Path, which holds one or more closed contours + * composed of 2D primitives like lines, quadratic curves, and cubic + * curves. + * @param {!o3d.Pack} pack Pack in which geometry and materials + * associated with the curves will be created. + * @param {!o3d.DrawList} drawList The DrawList on which the triangle + * mesh will be drawn. Typically this will be the + * zOrderedDrawList from an o3djs.rendergraph.ViewInfo. + * @return {!o3djs.gpu2d.Path} The created Path. + */ +o3djs.gpu2d.createPath = function(pack, + drawList) { + return new o3djs.gpu2d.Path(pack, drawList); +}; + +/** + * Constructs a new Path. Do not call this directly; use + * o3djs.gpu2d.createPath instead. + * @param {!o3d.Pack} pack Pack in which geometry and materials + * associated with the curves will be created. + * @param {!o3d.DrawList} drawList The DrawList on which the triangle + * mesh will be drawn. Typically this will be the + * zOrderedDrawList. + * @constructor + */ +o3djs.gpu2d.Path = function(pack, drawList) { + /** + * Pack in which curves' geometry and materials are created. + * @type {!o3d.Pack} + * @private + */ + this.pack_ = pack; + + /** + * DrawList in which curves' geometry and materials will be + * rendered. + * @type {!o3d.DrawList} + * @private + */ + this.drawList_ = drawList; + + /** + * Internal object which manages the triangle mesh associated with + * the curves. + * @type {!o3d.ProcessedPath} + * @private + */ + this.path_ = pack.createObject('ProcessedPath'); + + // Set up the Primitives in the ProcessedPath. + // + // The mesh is separated into two different regions. The exterior + // region of the mesh is the portion containing the cubic curve + // segments. It is this region whose alpha value is computed using + // Loop and Blinn's shader. The interior region of the mesh is + // simply filled with a constant alpha. The reason for the split is + // that it is difficult to assign texture coordinates to cause Loop + // and Blinn's shader to fill a region with constant alpha. While + // there is some cost associated with switching shaders and + // performing two draw calls, doing so simplifies the logic. + + // Create state objects so we can turn on alpha blending for the + // exterior triangles. We also disable backface culling so that we + // can view the vector shapes from both sides. + var exteriorState = pack.createObject('State'); + exteriorState.getStateParam('o3d.AlphaBlendEnable').value = true; + exteriorState.getStateParam('o3d.SourceBlendFunction').value = + o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA; + exteriorState.getStateParam('o3d.DestinationBlendFunction').value = + o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA; + exteriorState.getStateParam('o3d.CullMode').value = + o3djs.base.o3d.State.CULL_NONE; + + var interiorState = pack.createObject('State'); + interiorState.getStateParam('o3d.CullMode').value = + o3djs.base.o3d.State.CULL_NONE; + + // Create the materials for the exterior and interior regions. + + /** + * The material for the exterior triangles, filled with Loop and + * Blinn's shader. + * @type {!o3d.Material} + * @private + */ + this.exteriorMaterial_ = pack.createObject('Material'); + this.exteriorMaterial_.name = 'ExteriorMaterial'; + this.exteriorMaterial_.state = exteriorState; + this.exteriorMaterial_.drawList = drawList; + + /** + * The material for the interior triangles, filled with a solid + * shader. + * @type {!o3d.Material} + * @private + */ + this.interiorMaterial_ = pack.createObject('Material'); + this.interiorMaterial_.name = 'InteriorMaterial'; + this.interiorMaterial_.state = interiorState; + this.interiorMaterial_.drawList = drawList; + + /** + * The Shape which is the transform graph's view of the Path. + * @type {!o3d.Shape} + */ + this.shape = pack.createObject('Shape'); + + // Create the exterior region. + var primitive = pack.createObject('Primitive'); + var streamBank = pack.createObject('StreamBank'); + var vertexBuffer = pack.createObject('VertexBuffer'); + // The coordinates of the triangles are 2D + var vertices = vertexBuffer.createField('FloatField', 2); + /** + * The Field for the exterior vertices. + * @type {!o3d.FloatField} + * @private + */ + this.exteriorVertices_ = vertices; + // The (Loop/Blinn) texture coordinates are 3D + var texcoords = vertexBuffer.createField('FloatField', 3); + /** + * The Field for the exterior texture coordinates. + * @type {!o3d.FloatField} + * @private + */ + this.exteriorTexCoords_ = texcoords; + streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION, 0, vertices, 0); + streamBank.setVertexStream(o3djs.base.o3d.Stream.TEXCOORD, 0, texcoords, 0); + primitive.streamBank = streamBank; + primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST; + primitive.material = this.exteriorMaterial_; + primitive.owner = this.shape; + /** + * The Primitive for the exterior triangles. + * @type {!o3d.Primitive} + * @private + */ + this.exteriorTriangles_ = primitive; + + // Create the interior region. + primitive = pack.createObject('Primitive'); + streamBank = pack.createObject('StreamBank'); + vertexBuffer = pack.createObject('VertexBuffer'); + // The coordinates of the triangles are 2D + vertices = vertexBuffer.createField('FloatField', 2); + /** + * The Field for the interior vertices. + * @type {!o3d.FloatField} + * @private + */ + this.interiorVertices_ = vertices; + streamBank.setVertexStream(o3djs.base.o3d.Stream.POSITION, 0, vertices, 0); + primitive.streamBank = streamBank; + primitive.primitiveType = o3djs.base.o3d.Primitive.TRIANGLELIST; + primitive.material = this.interiorMaterial_; + primitive.owner = this.shape; + /** + * The Primitive for the interior triangles. + * @type {!o3d.Primitive} + * @private + */ + this.interiorTriangles_ = primitive; + + // Initialize the fill to a solid color. + this.setFill(o3djs.gpu2d.createColor(pack, 0.0, 0.0, 0.0, 1.0)); + + // Create draw elements for the shape. + this.shape.createDrawElements(pack, null); +}; + +/** + * Clears out any previously added segments or generated triangles + * from this Path. + */ +o3djs.gpu2d.Path.prototype.clear = function() { + this.path_.clear(); +}; + +/** + * Moves the pen to the given absolute X,Y coordinates. If a contour + * isn't currently open on this path, one is opened. + * @param {number} x the x coordinate to move to. + * @param {number} y the y coordinate to move to. + */ +o3djs.gpu2d.Path.prototype.moveTo = function(x, y) { + this.path_.moveTo(x, y); +}; + +/** + * Draws a line from the current coordinates to the given absolute + * X,Y coordinates. + * @param {number} x the x coordinate to draw a line to. + * @param {number} y the y coordinate to draw a line to. + */ +o3djs.gpu2d.Path.prototype.lineTo = function(x, y) { + this.path_.lineTo(x, y); +}; + +/** + * Draws a quadratic curve from the current coordinates through the + * given control point and end point, specified in absolute + * coordinates. + * @param {number} cx the x coordinate of the quadratic's control point + * @param {number} cy the y coordinate of the quadratic's control point + * @param {number} x the x coordinate of the quadratic's end point + * @param {number} y the y coordinate of the quadratic's end point + */ +o3djs.gpu2d.Path.prototype.quadraticTo = function(cx, cy, x, y) { + this.path_.quadraticTo(cx, cy, x, y); +}; + +/** + * Draws a cubic curve from the current coordinates through the + * given control points and end point, specified in absolute + * coordinates. + * @param {number} c0x the x coordinate of the cubic's first control point + * @param {number} c0y the y coordinate of the cubic's first control point + * @param {number} c1x the x coordinate of the cubic's second control point + * @param {number} c1y the y coordinate of the cubic's second control point + * @param {number} x the x coordinate of the cubic's end point + * @param {number} y the y coordinate of the cubic's end point + */ +o3djs.gpu2d.Path.prototype.cubicTo = function(c0x, c0y, c1x, c1y, x, y) { + this.path_.cubicTo(c0x, c0y, c1x, c1y, x, y); +}; + +/** + * Closes the current contour on this Path. + */ +o3djs.gpu2d.Path.prototype.close = function() { + this.path_.close(); +}; + +/** + * Updates the triangle mesh associated with this Path. Call this + * after adding any new segments to the Path. + */ +o3djs.gpu2d.Path.prototype.update = function() { + this.path_.createMesh(this.exteriorVertices_, + this.exteriorTexCoords_, + this.interiorVertices_); + var numVertices = this.exteriorVertices_.buffer.numElements; + if (numVertices == 1) { + this.exteriorTriangles_.numberVertices = 0; + this.exteriorTriangles_.numberPrimitives = 0; + } else { + this.exteriorTriangles_.numberVertices = numVertices; + this.exteriorTriangles_.numberPrimitives = numVertices / 3; + } + numVertices = this.interiorVertices_.buffer.numElements; + if (numVertices == 1) { + this.interiorTriangles_.numberVertices = 0; + this.interiorTriangles_.numberPrimitives = 0; + } else { + this.interiorTriangles_.numberVertices = numVertices; + this.interiorTriangles_.numberPrimitives = numVertices / 3; + } +}; + +//---------------------------------------------------------------------- +// Fills + +/** + * Sets the fill for this Path. + * @param {!o3djs.gpu2d.Fill} fill the fill for this Path. + */ +o3djs.gpu2d.Path.prototype.setFill = function(fill) { + if (this.fill_) { + this.fill_.detach_(this); + } + this.interiorMaterial_.effect = fill.interiorEffect; + this.exteriorMaterial_.effect = fill.exteriorEffect; + this.fill_ = fill; + fill.attach_(this); +}; + +/** + * Base class for all Fills. Do not call this directly; use, for + * example, o3djs.gpu2d.createColor instead. + * @param {!o3d.Pack} pack the Pack in which to create materials. + * @constructor + */ +o3djs.gpu2d.Fill = function(pack) { + this.pack_ = pack; + this.attachedPaths_ = []; +}; + +/** + * Attaches this Fill to the given path. + * @param {!o3djs.gpu2d.Path} path Path to attach the fill to. + * @private + */ +o3djs.gpu2d.Fill.prototype.attach_ = function(path) { + if (this.attachedPaths_.indexOf(path) < 0) + this.attachedPaths_.push(path); + this.apply_(path); +}; + +/** + * Detaches this Fill from the given path. + * @param {!o3djs.gpu2d.Path} path Path to detach the fill from. + * @private + */ +o3djs.gpu2d.Fill.prototype.detach_ = function(path) { + var idx = this.attachedPaths_.indexOf(path); + if (idx >= 0) + this.attachedPaths_.splice(idx, idx); +}; + +/** + * Applies this Fill to all attached paths. + * @private + */ +o3djs.gpu2d.Fill.prototype.applyToPaths_ = function() { + for (var i = 0; i < this.attachedPaths_.length; i++) { + this.apply_(this.attachedPaths_[i]); + } +}; + +/** + * Base "apply" operation for fills -- a no-op. + * @private + */ +o3djs.gpu2d.Fill.prototype.apply_ = function(path) { +}; + +/** + * A class for a solid color fill. Do not call this directly; use + * o3djs.gpu2d.createColor instead. + * @param {!o3d.Pack} pack the Pack in which to create materials. + * @constructor + * @extends {o3djs.gpu2d.Fill} + */ +o3djs.gpu2d.Color = function(pack) { + o3djs.gpu2d.Fill.call(this, pack); + this.interiorEffect = + o3djs.gpu2d.loadEffect_(pack, o3djs.gpu2d.FillTypes_.COLOR, true); + this.exteriorEffect = + o3djs.gpu2d.loadEffect_(pack, o3djs.gpu2d.FillTypes_.COLOR, false); + this.r_ = 0.0; + this.g_ = 0.0; + this.b_ = 0.0; + this.a_ = 1.0; +}; + +o3djs.base.inherit(o3djs.gpu2d.Color, + o3djs.gpu2d.Fill); + +/** + * Sets the color of this fill. + * @param {number} r Red component (0.0 - 1.0). + * @param {number} g Green component (0.0 - 1.0). + * @param {number} b Blue component (0.0 - 1.0). + * @param {number} a Alpha component (0.0 - 1.0). + */ +o3djs.gpu2d.Color.prototype.set = function(r, g, b, a) { + this.r_ = r; + this.g_ = g; + this.b_ = b; + this.a_ = a; + this.applyToPaths_(); +}; + +/** + * Gets the value of the Color fill as an array. + * @return {!o3d.Float4} + */ +o3djs.gpu2d.Color.prototype.get = function() { + return [this.r_, this.g_, this.b_, this.a_]; +}; + +/** + * Applies this color to the given path. + * @param {!o3djs.gpu2d.Path} path to apply the fill to. + * @private + */ +o3djs.gpu2d.Color.prototype.apply_ = function(path) { + this.applyToMaterial_(path.interiorMaterial_); + this.applyToMaterial_(path.exteriorMaterial_); +}; + +/** + * Applies this color to the given material + * @param {!o3d.Material} material to apply the fill to. + * @private + */ +o3djs.gpu2d.Color.prototype.applyToMaterial_ = function(material) { + var paramName = 'color'; + var paramType = 'ParamFloat4'; + var param = material.getParam(paramName); + if (!param) { + param = material.createParam(paramName, paramType); + } + param.set(this.r_, this.g_, this.b_, this.a_); +}; + +/** + * Creates a solid color fill. + * @param {!o3d.Pack} pack the Pack in which to create materials. + * @param {number} red Red component (0.0 - 1.0). + * @param {number} green Green component (0.0 - 1.0). + * @param {number} blue Blue component (0.0 - 1.0). + * @param {number} alpha Alpha component (0.0 - 1.0). + * @return {!o3djs.gpu2d.Color} The created Color. + */ +o3djs.gpu2d.createColor = function(pack, red, green, blue, alpha) { + var result = new o3djs.gpu2d.Color(pack); + result.set(red, green, blue, alpha); + return result; +}; + +//---------------------------------------------------------------------- +// Shaders and effects + +// TODO(kbr): antialiasing is not supported yet because the ddx +// and ddy instructions are not part of the shader model 2.0. On +// Windows we could easily upgrade to ps2.0a, but on Mac and Linux +// there isn't an easy upgrade path from ARBVP1.0 and ARBFP1.0 which +// incorporates these instructions. +// +// The solution within O3D is to compute the gradients using the +// closed-form solution in Loop and Blinn's SIGGRAPH '05 paper. This +// requires computation of the Psi matrix per vertex. In GLSL this is +// not necessary; derivative instructions are always available there. + +/** + * Generates the source for the shader used on the exterior triangles + * of the shape -- the ones that evaluate the curve function. + * @param {boolean} antialias whether to enable antialiasing. + * @param {string} fillUniforms the uniforms for the fill. + * @param {string} fillSource the source code snippet for the fill. + * @return {string} + * @private + */ +o3djs.gpu2d.generateLoopBlinnShaderSource_ = function(antialias, + fillUniforms, + fillSource) { + var result = '' + + 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + + fillUniforms + + '\n' + + 'struct VertexShaderInput {\n' + + ' float2 position : POSITION;\n' + + ' float3 klm : TEXCOORD0;\n' + + '};\n' + + '\n' + + 'struct PixelShaderInput {\n' + + ' float4 position : POSITION;\n' + + ' float3 klm : TEXCOORD0;\n' + + '};\n' + + '\n' + + 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + + ' PixelShaderInput output;\n' + + '\n' + + ' output.position = mul(float4(input.position, 0, 1),\n' + + ' worldViewProjection);\n' + + ' output.klm = input.klm;\n' + + ' return output;\n' + + '}\n' + + '\n' + + 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' + + ' float3 klm = input.klm;\n'; + var alphaComputation; + if (antialias) { + alphaComputation = '' + + ' // Gradients\n' + + ' float3 px = ddx(input.klm);\n' + + ' float3 py = ddy(input.klm);\n' + + '\n' + + ' // Chain rule\n' + + ' float k2 = klm.x * klm.x;\n' + + ' float c = k2 * klm.x - klm.y * klm.z;\n' + + ' float k23 = 3.0 * k2;\n' + + ' float cx = k23 * px.x - klm.z * px.y - klm.y * px.z;\n' + + ' float cy = k23 * py.x - klm.z * py.y - klm.y * py.z;\n' + + '\n' + + ' // Signed distance\n' + + ' float sd = c / sqrt(cx * cx + cy * cy);\n' + + '\n' + + ' // Linear alpha\n' + + ' float alpha = clamp(0.5 - sd, 0.0, 1.0);\n'; + } else { + alphaComputation = '' + + ' float t = klm.x * klm.x * klm.x - klm.y * klm.z;\n' + + ' float alpha = clamp(sign(t), 0.0, 1.0);\n'; + } + + return result + alphaComputation + + '\n' + + fillSource + + '}\n' + + '\n' + + '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + + '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + + '// #o3d MatrixLoadOrder RowMajor\n'; +}; + +/** + * Generates the source for the shader used on the interior triangles + * of the shape. + * @param {string} fillUniforms the uniforms for the fill. + * @param {string} fillSource the source code snippet for the fill. + * @return {string} + * @private + */ +o3djs.gpu2d.generateSolidShaderSource_ = function(fillUniforms, fillSource) { + var result = '' + + 'uniform float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + + fillUniforms + + '\n' + + 'struct VertexShaderInput {\n' + + ' float2 position : POSITION;\n' + + '};\n' + + '\n' + + 'struct PixelShaderInput {\n' + + ' float4 position : POSITION;\n' + + '};\n' + + '\n' + + 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + + ' PixelShaderInput output;\n' + + '\n' + + ' output.position = mul(float4(input.position, 0, 1),\n' + + ' worldViewProjection);\n' + + ' return output;\n' + + '}\n' + + '\n' + + 'float4 pixelShaderFunction(PixelShaderInput input) : COLOR {\n' + + ' float alpha = 1.0;\n' + + fillSource + + '}\n' + + '\n' + + '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + + '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + + '// #o3d MatrixLoadOrder RowMajor\n'; + return result; +}; + +/** + * Enum for the types of fills. + * @enum + * @private + */ +o3djs.gpu2d.FillTypes_ = { + COLOR: 0 +}; + +/** + * Shader code for the various fills, indexed by FillTypes_. + * @type {!Array.<{uniforms: string, source: string}>} + * @private + */ +o3djs.gpu2d.FILL_CODE_ = [ + { uniforms: + 'uniform float4 color;\n', + source: + 'return float4(color.r, color.g, color.b, color.a * alpha);\n' + } +]; + +/** + * Cache of effects indexed by pack's client ID. Each entry is an + * array indexed by fill type. + * @type {!Array.<!Array.<!o3d.Effect>>} + * @private + */ +o3djs.gpu2d.interiorEffectCache_ = []; + +/** + * Cache of effects indexed by pack's client ID. Each entry is an + * array indexed by fill type. + * @type {!Array.<!Array.<!o3d.Effect>>} + * @private + */ +o3djs.gpu2d.exteriorEffectCache_ = []; + +/** + * Loads a fill effect for a Path. + * @param {!o3d.Pack} pack the Pack in which to create materials. + * @param {o3djs.gpu2d.FillTypes_} fillType the fill type to create. + * @param {boolean} interior whether this effect is filling the solid + * interior portion of the shape or the exterior region containing + * the curves. + * @return {!o3d.Effect} + * @private + */ +o3djs.gpu2d.loadEffect_ = function(pack, fillType, interior) { + var effectCache; + if (interior) { + effectCache = o3djs.gpu2d.interiorEffectCache_; + } else { + effectCache = o3djs.gpu2d.exteriorEffectCache_; + } + var effectList = o3djs.gpu2d.getEffectList_(pack, effectCache); + var effect = effectList[fillType]; + if (!effect) { + effect = pack.createObject('Effect'); + var result = false; + var sourceSnippets = o3djs.gpu2d.FILL_CODE_[fillType]; + if (interior) { + result = effect.loadFromFXString( + o3djs.gpu2d.generateSolidShaderSource_(sourceSnippets.uniforms, + sourceSnippets.source)); + } else { + result = effect.loadFromFXString( + o3djs.gpu2d.generateLoopBlinnShaderSource_(false, + sourceSnippets.uniforms, + sourceSnippets.source)); + } + if (!result) { + alert('Error loading shader: interior = ' + interior); + } + effectList[fillType] = effect; + } + return effect; +}; + +/** + * Fetches and/or creates the effect list for a given pack from the + * passed effect cache. + * @param {!o3d.Pack} pack the Pack in which to create materials. + * @param {!Array.<!Array.<!o3d.Effect>>} effectCache the effect cache. + * @return {!Array.<o3d.Effect>} + * @private + */ +o3djs.gpu2d.getEffectList_ = function(pack, effectCache) { + var list = effectCache[pack.clientId]; + if (!list) { + list = []; + effectCache[pack.clientId] = list; + } + return list; +}; + diff --git a/o3d/samples/o3djs/js_list.manifest b/o3d/samples/o3djs/js_list.manifest index 3820b06..5e7423e 100644 --- a/o3d/samples/o3djs/js_list.manifest +++ b/o3d/samples/o3djs/js_list.manifest @@ -38,6 +38,7 @@ 'error.js', 'event.js', 'fps.js', + 'gpu2d.js', 'io.js', 'loader.js', 'material.js', diff --git a/o3d/samples/third_party/xmljs/COPYING b/o3d/samples/third_party/xmljs/COPYING new file mode 100755 index 0000000..a5dee30 --- /dev/null +++ b/o3d/samples/third_party/xmljs/COPYING @@ -0,0 +1,491 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/o3d/samples/third_party/xmljs/tinyxmlsax.js b/o3d/samples/third_party/xmljs/tinyxmlsax.js new file mode 100755 index 0000000..1e0cc73 --- /dev/null +++ b/o3d/samples/third_party/xmljs/tinyxmlsax.js @@ -0,0 +1,245 @@ +// ========================================================================= +// +// tinyxmlsax.js - an XML SAX parser in JavaScript compressed for downloading +// +// version 3.1 +// +// ========================================================================= +// +// Copyright (C) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com) +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 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 +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser 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 +// +// Visit the XML for <SCRIPT> home page at http://xmljs.sourceforge.net +// + + +var whitespace = "\n\r\t "; XMLP = function(strXML) { strXML = SAXStrings.replace(strXML, null, null, "\r\n", "\n"); strXML = SAXStrings.replace(strXML, null, null, "\r", "\n"); this.m_xml = strXML; this.m_iP = 0; this.m_iState = XMLP._STATE_PROLOG; this.m_stack = new Stack(); this._clearAttributes();} +XMLP._NONE = 0; XMLP._ELM_B = 1; XMLP._ELM_E = 2; XMLP._ELM_EMP = 3; XMLP._ATT = 4; XMLP._TEXT = 5; XMLP._ENTITY = 6; XMLP._PI = 7; XMLP._CDATA = 8; XMLP._COMMENT = 9; XMLP._DTD = 10; XMLP._ERROR = 11; XMLP._CONT_XML = 0; XMLP._CONT_ALT = 1; XMLP._ATT_NAME = 0; XMLP._ATT_VAL = 1; XMLP._STATE_PROLOG = 1; XMLP._STATE_DOCUMENT = 2; XMLP._STATE_MISC = 3; XMLP._errs = new Array(); XMLP._errs[XMLP.ERR_CLOSE_PI = 0 ] = "PI: missing closing sequence"; XMLP._errs[XMLP.ERR_CLOSE_DTD = 1 ] = "DTD: missing closing sequence"; XMLP._errs[XMLP.ERR_CLOSE_COMMENT = 2 ] = "Comment: missing closing sequence"; XMLP._errs[XMLP.ERR_CLOSE_CDATA = 3 ] = "CDATA: missing closing sequence"; XMLP._errs[XMLP.ERR_CLOSE_ELM = 4 ] = "Element: missing closing sequence"; XMLP._errs[XMLP.ERR_CLOSE_ENTITY = 5 ] = "Entity: missing closing sequence"; XMLP._errs[XMLP.ERR_PI_TARGET = 6 ] = "PI: target is required"; XMLP._errs[XMLP.ERR_ELM_EMPTY = 7 ] = "Element: cannot be both empty and closing"; XMLP._errs[XMLP.ERR_ELM_NAME = 8 ] = "Element: name must immediatly follow \"<\""; XMLP._errs[XMLP.ERR_ELM_LT_NAME = 9 ] = "Element: \"<\" not allowed in element names"; XMLP._errs[XMLP.ERR_ATT_VALUES = 10] = "Attribute: values are required and must be in quotes"; XMLP._errs[XMLP.ERR_ATT_LT_NAME = 11] = "Element: \"<\" not allowed in attribute names"; XMLP._errs[XMLP.ERR_ATT_LT_VALUE = 12] = "Attribute: \"<\" not allowed in attribute values"; XMLP._errs[XMLP.ERR_ATT_DUP = 13] = "Attribute: duplicate attributes not allowed"; XMLP._errs[XMLP.ERR_ENTITY_UNKNOWN = 14] = "Entity: unknown entity"; XMLP._errs[XMLP.ERR_INFINITELOOP = 15] = "Infininte loop"; XMLP._errs[XMLP.ERR_DOC_STRUCTURE = 16] = "Document: only comments, processing instructions, or whitespace allowed outside of document element"; XMLP._errs[XMLP.ERR_ELM_NESTING = 17] = "Element: must be nested correctly"; XMLP.prototype._addAttribute = function(name, value) { this.m_atts[this.m_atts.length] = new Array(name, value);} +XMLP.prototype._checkStructure = function(iEvent) { if(XMLP._STATE_PROLOG == this.m_iState) { if((XMLP._TEXT == iEvent) || (XMLP._ENTITY == iEvent)) { if(SAXStrings.indexOfNonWhitespace(this.getContent(), this.getContentBegin(), this.getContentEnd()) != -1) { return this._setErr(XMLP.ERR_DOC_STRUCTURE);} +} +if((XMLP._ELM_B == iEvent) || (XMLP._ELM_EMP == iEvent)) { this.m_iState = XMLP._STATE_DOCUMENT;} +} +if(XMLP._STATE_DOCUMENT == this.m_iState) { if((XMLP._ELM_B == iEvent) || (XMLP._ELM_EMP == iEvent)) { this.m_stack.push(this.getName());} +if((XMLP._ELM_E == iEvent) || (XMLP._ELM_EMP == iEvent)) { var strTop = this.m_stack.pop(); if((strTop == null) || (strTop != this.getName())) { return this._setErr(XMLP.ERR_ELM_NESTING);} +} +if(this.m_stack.count() == 0) { this.m_iState = XMLP._STATE_MISC; return iEvent;} +} +if(XMLP._STATE_MISC == this.m_iState) { if((XMLP._ELM_B == iEvent) || (XMLP._ELM_E == iEvent) || (XMLP._ELM_EMP == iEvent) || (XMLP.EVT_DTD == iEvent)) { return this._setErr(XMLP.ERR_DOC_STRUCTURE);} +if((XMLP._TEXT == iEvent) || (XMLP._ENTITY == iEvent)) { if(SAXStrings.indexOfNonWhitespace(this.getContent(), this.getContentBegin(), this.getContentEnd()) != -1) { return this._setErr(XMLP.ERR_DOC_STRUCTURE);} +} +} +return iEvent;} +XMLP.prototype._clearAttributes = function() { this.m_atts = new Array();} +XMLP.prototype._findAttributeIndex = function(name) { for(var i = 0; i < this.m_atts.length; i++) { if(this.m_atts[i][XMLP._ATT_NAME] == name) { return i;} +} +return -1;} +XMLP.prototype.getAttributeCount = function() { return this.m_atts ? this.m_atts.length : 0;} +XMLP.prototype.getAttributeName = function(index) { return ((index < 0) || (index >= this.m_atts.length)) ? null : this.m_atts[index][XMLP._ATT_NAME];} +XMLP.prototype.getAttributeValue = function(index) { return ((index < 0) || (index >= this.m_atts.length)) ? null : __unescapeString(this.m_atts[index][XMLP._ATT_VAL]);} +XMLP.prototype.getAttributeValueByName = function(name) { return this.getAttributeValue(this._findAttributeIndex(name));} +XMLP.prototype.getColumnNumber = function() { return SAXStrings.getColumnNumber(this.m_xml, this.m_iP);} +XMLP.prototype.getContent = function() { return (this.m_cSrc == XMLP._CONT_XML) ? this.m_xml : this.m_cAlt;} +XMLP.prototype.getContentBegin = function() { return this.m_cB;} +XMLP.prototype.getContentEnd = function() { return this.m_cE;} +XMLP.prototype.getLineNumber = function() { return SAXStrings.getLineNumber(this.m_xml, this.m_iP);} +XMLP.prototype.getName = function() { return this.m_name;} +XMLP.prototype.next = function() { return this._checkStructure(this._parse());} +XMLP.prototype._parse = function() { if(this.m_iP == this.m_xml.length) { return XMLP._NONE;} +if(this.m_iP == this.m_xml.indexOf("<?", this.m_iP)) { return this._parsePI (this.m_iP + 2);} +else if(this.m_iP == this.m_xml.indexOf("<!DOCTYPE", this.m_iP)) { return this._parseDTD (this.m_iP + 9);} +else if(this.m_iP == this.m_xml.indexOf("<!--", this.m_iP)) { return this._parseComment(this.m_iP + 4);} +else if(this.m_iP == this.m_xml.indexOf("<![CDATA[", this.m_iP)) { return this._parseCDATA (this.m_iP + 9);} +else if(this.m_iP == this.m_xml.indexOf("<", this.m_iP)) { return this._parseElement(this.m_iP + 1);} +else if(this.m_iP == this.m_xml.indexOf("&", this.m_iP)) { return this._parseEntity (this.m_iP + 1);} +else{ return this._parseText (this.m_iP);} +} +XMLP.prototype._parseAttribute = function(iB, iE) { var iNB, iNE, iEq, iVB, iVE; var cQuote, strN, strV; this.m_cAlt = ""; iNB = SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iE); if((iNB == -1) ||(iNB >= iE)) { return iNB;} +iEq = this.m_xml.indexOf("=", iNB); if((iEq == -1) || (iEq > iE)) { return this._setErr(XMLP.ERR_ATT_VALUES);} +iNE = SAXStrings.lastIndexOfNonWhitespace(this.m_xml, iNB, iEq); iVB = SAXStrings.indexOfNonWhitespace(this.m_xml, iEq + 1, iE); if((iVB == -1) ||(iVB > iE)) { return this._setErr(XMLP.ERR_ATT_VALUES);} +cQuote = this.m_xml.charAt(iVB); if(SAXStrings.QUOTES.indexOf(cQuote) == -1) { return this._setErr(XMLP.ERR_ATT_VALUES);} +iVE = this.m_xml.indexOf(cQuote, iVB + 1); if((iVE == -1) ||(iVE > iE)) { return this._setErr(XMLP.ERR_ATT_VALUES);} +strN = this.m_xml.substring(iNB, iNE + 1); strV = this.m_xml.substring(iVB + 1, iVE); if(strN.indexOf("<") != -1) { return this._setErr(XMLP.ERR_ATT_LT_NAME);} +if(strV.indexOf("<") != -1) { return this._setErr(XMLP.ERR_ATT_LT_VALUE);} +strV = SAXStrings.replace(strV, null, null, "\n", " "); strV = SAXStrings.replace(strV, null, null, "\t", " "); iRet = this._replaceEntities(strV); if(iRet == XMLP._ERROR) { return iRet;} +strV = this.m_cAlt; if(this._findAttributeIndex(strN) == -1) { this._addAttribute(strN, strV);} +else { return this._setErr(XMLP.ERR_ATT_DUP);} +this.m_iP = iVE + 2; return XMLP._ATT;} +XMLP.prototype._parseCDATA = function(iB) { var iE = this.m_xml.indexOf("]]>", iB); if (iE == -1) { return this._setErr(XMLP.ERR_CLOSE_CDATA);} +this._setContent(XMLP._CONT_XML, iB, iE); this.m_iP = iE + 3; return XMLP._CDATA;} +XMLP.prototype._parseComment = function(iB) { var iE = this.m_xml.indexOf("-" + "->", iB); if (iE == -1) { return this._setErr(XMLP.ERR_CLOSE_COMMENT);} +this._setContent(XMLP._CONT_XML, iB, iE); this.m_iP = iE + 3; return XMLP._COMMENT;} +XMLP.prototype._parseDTD = function(iB) { var iE, strClose, iInt, iLast; iE = this.m_xml.indexOf(">", iB); if(iE == -1) { return this._setErr(XMLP.ERR_CLOSE_DTD);} +iInt = this.m_xml.indexOf("[", iB); strClose = ((iInt != -1) && (iInt < iE)) ? "]>" : ">"; while(true) { if(iE == iLast) { return this._setErr(XMLP.ERR_INFINITELOOP);} +iLast = iE; iE = this.m_xml.indexOf(strClose, iB); if(iE == -1) { return this._setErr(XMLP.ERR_CLOSE_DTD);} +if (this.m_xml.substring(iE - 1, iE + 2) != "]]>") { break;} +} +this.m_iP = iE + strClose.length; return XMLP._DTD;} +XMLP.prototype._parseElement = function(iB) { var iE, iDE, iNE, iRet; var iType, strN, iLast; iDE = iE = this.m_xml.indexOf(">", iB); if(iE == -1) { return this._setErr(XMLP.ERR_CLOSE_ELM);} +if(this.m_xml.charAt(iB) == "/") { iType = XMLP._ELM_E; iB++;} else { iType = XMLP._ELM_B;} +if(this.m_xml.charAt(iE - 1) == "/") { if(iType == XMLP._ELM_E) { return this._setErr(XMLP.ERR_ELM_EMPTY);} +iType = XMLP._ELM_EMP; iDE--;} +iDE = SAXStrings.lastIndexOfNonWhitespace(this.m_xml, iB, iDE); if (iE - iB != 1 ) { if(SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iDE) != iB) { return this._setErr(XMLP.ERR_ELM_NAME);} +} +this._clearAttributes(); iNE = SAXStrings.indexOfWhitespace(this.m_xml, iB, iDE); if(iNE == -1) { iNE = iDE + 1;} +else { this.m_iP = iNE; while(this.m_iP < iDE) { if(this.m_iP == iLast) return this._setErr(XMLP.ERR_INFINITELOOP); iLast = this.m_iP; iRet = this._parseAttribute(this.m_iP, iDE); if(iRet == XMLP._ERROR) return iRet;} +} +strN = this.m_xml.substring(iB, iNE); if(strN.indexOf("<") != -1) { return this._setErr(XMLP.ERR_ELM_LT_NAME);} +this.m_name = strN; this.m_iP = iE + 1; return iType;} +XMLP.prototype._parseEntity = function(iB) { var iE = this.m_xml.indexOf(";", iB); if(iE == -1) { return this._setErr(XMLP.ERR_CLOSE_ENTITY);} +this.m_iP = iE + 1; return this._replaceEntity(this.m_xml, iB, iE);} +XMLP.prototype._parsePI = function(iB) { var iE, iTB, iTE, iCB, iCE; iE = this.m_xml.indexOf("?>", iB); if(iE == -1) { return this._setErr(XMLP.ERR_CLOSE_PI);} +iTB = SAXStrings.indexOfNonWhitespace(this.m_xml, iB, iE); if(iTB == -1) { return this._setErr(XMLP.ERR_PI_TARGET);} +iTE = SAXStrings.indexOfWhitespace(this.m_xml, iTB, iE); if(iTE == -1) { iTE = iE;} +iCB = SAXStrings.indexOfNonWhitespace(this.m_xml, iTE, iE); if(iCB == -1) { iCB = iE;} +iCE = SAXStrings.lastIndexOfNonWhitespace(this.m_xml, iCB, iE); if(iCE == -1) { iCE = iE - 1;} +this.m_name = this.m_xml.substring(iTB, iTE); this._setContent(XMLP._CONT_XML, iCB, iCE + 1); this.m_iP = iE + 2; return XMLP._PI;} +XMLP.prototype._parseText = function(iB) { var iE, iEE; iE = this.m_xml.indexOf("<", iB); if(iE == -1) { iE = this.m_xml.length;} +iEE = this.m_xml.indexOf("&", iB); if((iEE != -1) && (iEE <= iE)) { iE = iEE;} +this._setContent(XMLP._CONT_XML, iB, iE); this.m_iP = iE; return XMLP._TEXT;} +XMLP.prototype._replaceEntities = function(strD, iB, iE) { if(SAXStrings.isEmpty(strD)) return ""; iB = iB || 0; iE = iE || strD.length; var iEB, iEE, strRet = ""; iEB = strD.indexOf("&", iB); iEE = iB; while((iEB > 0) && (iEB < iE)) { strRet += strD.substring(iEE, iEB); iEE = strD.indexOf(";", iEB) + 1; if((iEE == 0) || (iEE > iE)) { return this._setErr(XMLP.ERR_CLOSE_ENTITY);} +iRet = this._replaceEntity(strD, iEB + 1, iEE - 1); if(iRet == XMLP._ERROR) { return iRet;} +strRet += this.m_cAlt; iEB = strD.indexOf("&", iEE);} +if(iEE != iE) { strRet += strD.substring(iEE, iE);} +this._setContent(XMLP._CONT_ALT, strRet); return XMLP._ENTITY;} +XMLP.prototype._replaceEntity = function(strD, iB, iE) { if(SAXStrings.isEmpty(strD)) return -1; iB = iB || 0; iE = iE || strD.length; switch(strD.substring(iB, iE)) { case "amp": strEnt = "&"; break; case "lt": strEnt = "<"; break; case "gt": strEnt = ">"; break; case "apos": strEnt = "'"; break; case "quot": strEnt = "\""; break; default: +if(strD.charAt(iB) == "#") { strEnt = String.fromCharCode(parseInt(strD.substring(iB + 1, iE)));} else { return this._setErr(XMLP.ERR_ENTITY_UNKNOWN);} +break;} +this._setContent(XMLP._CONT_ALT, strEnt); return XMLP._ENTITY;} +XMLP.prototype._setContent = function(iSrc) { var args = arguments; if(XMLP._CONT_XML == iSrc) { this.m_cAlt = null; this.m_cB = args[1]; this.m_cE = args[2];} else { this.m_cAlt = args[1]; this.m_cB = 0; this.m_cE = args[1].length;} +this.m_cSrc = iSrc;} +XMLP.prototype._setErr = function(iErr) { var strErr = XMLP._errs[iErr]; this.m_cAlt = strErr; this.m_cB = 0; this.m_cE = strErr.length; this.m_cSrc = XMLP._CONT_ALT; return XMLP._ERROR;} +SAXDriver = function() { this.m_hndDoc = null; this.m_hndErr = null; this.m_hndLex = null;} +SAXDriver.DOC_B = 1; SAXDriver.DOC_E = 2; SAXDriver.ELM_B = 3; SAXDriver.ELM_E = 4; SAXDriver.CHARS = 5; SAXDriver.PI = 6; SAXDriver.CD_B = 7; SAXDriver.CD_E = 8; SAXDriver.CMNT = 9; SAXDriver.DTD_B = 10; SAXDriver.DTD_E = 11; SAXDriver.prototype.parse = function(strD) { var parser = new XMLP(strD); if(this.m_hndDoc && this.m_hndDoc.setDocumentLocator) { this.m_hndDoc.setDocumentLocator(this);} +this.m_parser = parser; this.m_bErr = false; if(!this.m_bErr) { this._fireEvent(SAXDriver.DOC_B);} +this._parseLoop(); if(!this.m_bErr) { this._fireEvent(SAXDriver.DOC_E);} +this.m_xml = null; this.m_iP = 0;} +SAXDriver.prototype.setDocumentHandler = function(hnd) { this.m_hndDoc = hnd;} +SAXDriver.prototype.setErrorHandler = function(hnd) { this.m_hndErr = hnd;} +SAXDriver.prototype.setLexicalHandler = function(hnd) { this.m_hndLex = hnd;} +SAXDriver.prototype.getColumnNumber = function() { return this.m_parser.getColumnNumber();} +SAXDriver.prototype.getLineNumber = function() { return this.m_parser.getLineNumber();} +SAXDriver.prototype.getMessage = function() { return this.m_strErrMsg;} +SAXDriver.prototype.getPublicId = function() { return null;} +SAXDriver.prototype.getSystemId = function() { return null;} +SAXDriver.prototype.getLength = function() { return this.m_parser.getAttributeCount();} +SAXDriver.prototype.getName = function(index) { return this.m_parser.getAttributeName(index);} +SAXDriver.prototype.getValue = function(index) { return this.m_parser.getAttributeValue(index);} +SAXDriver.prototype.getValueByName = function(name) { return this.m_parser.getAttributeValueByName(name);} +SAXDriver.prototype._fireError = function(strMsg) { this.m_strErrMsg = strMsg; this.m_bErr = true; if(this.m_hndErr && this.m_hndErr.fatalError) { this.m_hndErr.fatalError(this);} +} +SAXDriver.prototype._fireEvent = function(iEvt) { var hnd, func, args = arguments, iLen = args.length - 1; if(this.m_bErr) return; if(SAXDriver.DOC_B == iEvt) { func = "startDocument"; hnd = this.m_hndDoc;} +else if (SAXDriver.DOC_E == iEvt) { func = "endDocument"; hnd = this.m_hndDoc;} +else if (SAXDriver.ELM_B == iEvt) { func = "startElement"; hnd = this.m_hndDoc;} +else if (SAXDriver.ELM_E == iEvt) { func = "endElement"; hnd = this.m_hndDoc;} +else if (SAXDriver.CHARS == iEvt) { func = "characters"; hnd = this.m_hndDoc;} +else if (SAXDriver.PI == iEvt) { func = "processingInstruction"; hnd = this.m_hndDoc;} +else if (SAXDriver.CD_B == iEvt) { func = "startCDATA"; hnd = this.m_hndLex;} +else if (SAXDriver.CD_E == iEvt) { func = "endCDATA"; hnd = this.m_hndLex;} +else if (SAXDriver.CMNT == iEvt) { func = "comment"; hnd = this.m_hndLex;} +if(hnd && hnd[func]) { if(0 == iLen) { hnd[func]();} +else if (1 == iLen) { hnd[func](args[1]);} +else if (2 == iLen) { hnd[func](args[1], args[2]);} +else if (3 == iLen) { hnd[func](args[1], args[2], args[3]);} +} +} +SAXDriver.prototype._parseLoop = function(parser) { var iEvent, parser; parser = this.m_parser; while(!this.m_bErr) { iEvent = parser.next(); if(iEvent == XMLP._ELM_B) { this._fireEvent(SAXDriver.ELM_B, parser.getName(), this);} +else if(iEvent == XMLP._ELM_E) { this._fireEvent(SAXDriver.ELM_E, parser.getName());} +else if(iEvent == XMLP._ELM_EMP) { this._fireEvent(SAXDriver.ELM_B, parser.getName(), this); this._fireEvent(SAXDriver.ELM_E, parser.getName());} +else if(iEvent == XMLP._TEXT) { this._fireEvent(SAXDriver.CHARS, parser.getContent(), parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());} +else if(iEvent == XMLP._ENTITY) { this._fireEvent(SAXDriver.CHARS, parser.getContent(), parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());} +else if(iEvent == XMLP._PI) { this._fireEvent(SAXDriver.PI, parser.getName(), parser.getContent().substring(parser.getContentBegin(), parser.getContentEnd()));} +else if(iEvent == XMLP._CDATA) { this._fireEvent(SAXDriver.CD_B); this._fireEvent(SAXDriver.CHARS, parser.getContent(), parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin()); this._fireEvent(SAXDriver.CD_E);} +else if(iEvent == XMLP._COMMENT) { this._fireEvent(SAXDriver.CMNT, parser.getContent(), parser.getContentBegin(), parser.getContentEnd() - parser.getContentBegin());} +else if(iEvent == XMLP._DTD) { } +else if(iEvent == XMLP._ERROR) { this._fireError(parser.getContent());} +else if(iEvent == XMLP._NONE) { return;} +} +} +SAXStrings = function() { } +SAXStrings.WHITESPACE = " \t\n\r"; SAXStrings.QUOTES = "\"'"; SAXStrings.getColumnNumber = function(strD, iP) { if(SAXStrings.isEmpty(strD)) { return -1;} +iP = iP || strD.length; var arrD = strD.substring(0, iP).split("\n"); var strLine = arrD[arrD.length - 1]; arrD.length--; var iLinePos = arrD.join("\n").length; return iP - iLinePos;} +SAXStrings.getLineNumber = function(strD, iP) { if(SAXStrings.isEmpty(strD)) { return -1;} +iP = iP || strD.length; return strD.substring(0, iP).split("\n").length +} +SAXStrings.indexOfNonWhitespace = function(strD, iB, iE) { if(SAXStrings.isEmpty(strD)) { return -1;} +iB = iB || 0; iE = iE || strD.length; for(var i = iB; i < iE; i++){ if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) == -1) { return i;} +} +return -1;} +SAXStrings.indexOfWhitespace = function(strD, iB, iE) { if(SAXStrings.isEmpty(strD)) { return -1;} +iB = iB || 0; iE = iE || strD.length; for(var i = iB; i < iE; i++) { if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) != -1) { return i;} +} +return -1;} +SAXStrings.isEmpty = function(strD) { return (strD == null) || (strD.length == 0);} +SAXStrings.lastIndexOfNonWhitespace = function(strD, iB, iE) { if(SAXStrings.isEmpty(strD)) { return -1;} +iB = iB || 0; iE = iE || strD.length; for(var i = iE - 1; i >= iB; i--){ if(SAXStrings.WHITESPACE.indexOf(strD.charAt(i)) == -1){ return i;} +} +return -1;} +SAXStrings.replace = function(strD, iB, iE, strF, strR) { if(SAXStrings.isEmpty(strD)) { return "";} +iB = iB || 0; iE = iE || strD.length; return strD.substring(iB, iE).split(strF).join(strR);} +Stack = function() { this.m_arr = new Array();} +Stack.prototype.clear = function() { this.m_arr = new Array();} +Stack.prototype.count = function() { return this.m_arr.length;} +Stack.prototype.destroy = function() { this.m_arr = null;} +Stack.prototype.peek = function() { if(this.m_arr.length == 0) { return null;} +return this.m_arr[this.m_arr.length - 1];} +Stack.prototype.pop = function() { if(this.m_arr.length == 0) { return null;} +var o = this.m_arr[this.m_arr.length - 1]; this.m_arr.length--; return o;} +Stack.prototype.push = function(o) { this.m_arr[this.m_arr.length] = o;} +function isEmpty(str) { return (str==null) || (str.length==0);} +function trim(trimString, leftTrim, rightTrim) { if (isEmpty(trimString)) { return "";} +if (leftTrim == null) { leftTrim = true;} +if (rightTrim == null) { rightTrim = true;} +var left=0; var right=0; var i=0; var k=0; if (leftTrim == true) { while ((i<trimString.length) && (whitespace.indexOf(trimString.charAt(i++))!=-1)) { left++;} +} +if (rightTrim == true) { k=trimString.length-1; while((k>=left) && (whitespace.indexOf(trimString.charAt(k--))!=-1)) { right++;} +} +return trimString.substring(left, trimString.length - right);} +function __escapeString(str) { var escAmpRegEx = /&/g; var escLtRegEx = /</g; var escGtRegEx = />/g; var quotRegEx = /"/g; + var aposRegEx = /'/g; + + str = str.replace(escAmpRegEx, "&"); + str = str.replace(escLtRegEx, "<"); + str = str.replace(escGtRegEx, ">"); + str = str.replace(quotRegEx, """); + str = str.replace(aposRegEx, "'"); + + return str; +} + +/** + * function __unescapeString + * + * author: David Joham djoham@yahoo.com + * + * @param str : string - The string to be unescaped + * + * @return : string - The unescaped string + */ +function __unescapeString(str) { + + var escAmpRegEx = /&/g; + var escLtRegEx = /</g; + var escGtRegEx = />/g; + var quotRegEx = /"/g; + var aposRegEx = /'/g; + + str = str.replace(escAmpRegEx, "&"); + str = str.replace(escLtRegEx, "<"); + str = str.replace(escGtRegEx, ">"); + str = str.replace(quotRegEx, "\""); + str = str.replace(aposRegEx, "'"); + return str; +} |