1
0
mirror of https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git synced 2026-07-26 10:25:49 +00:00

WIP: SVG import & rasterization

Updated AntiGrain (agg) library to 2.5
Added agg_svg library from AntiGrain 2.5
added src/slic3r/Utils/SVGImport.cpp/hpp
This commit is contained in:
bubnikv
2019-02-07 12:06:51 +01:00
parent a2478b7faa
commit 9b15908a47
59 changed files with 10646 additions and 3586 deletions
+22
View File
@@ -0,0 +1,22 @@
project(agg_svg)
cmake_minimum_required(VERSION 2.6)
add_library(agg_svg STATIC
agg_svg_exception.h
agg_svg_parser.cpp
agg_svg_parser.h
agg_svg_path_renderer.cpp
agg_svg_path_renderer.h
agg_svg_path_tokenizer.cpp
agg_svg_path_tokenizer.h
# agg_bezier_arc.cpp
agg_curves.cpp
agg_trans_affine.cpp
agg_vcgen_contour.cpp
agg_vcgen_stroke.cpp
)
target_include_directories(agg_svg PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(agg_svg ${EXPAT_LIBRARIES})
+261
View File
@@ -0,0 +1,261 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://antigrain.com
//
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
//----------------------------------------------------------------------------
#include <math.h>
#include "agg_bezier_arc.h"
namespace agg
{
// This epsilon is used to prevent us from adding degenerate curves
// (converging to a single point).
// The value isn't very critical. Function arc_to_bezier() has a limit
// of the sweep_angle. If fabs(sweep_angle) exceeds pi/2 the curve
// becomes inaccurate. But slight exceeding is quite appropriate.
//-------------------------------------------------bezier_arc_angle_epsilon
const double bezier_arc_angle_epsilon = 0.01;
//------------------------------------------------------------arc_to_bezier
void arc_to_bezier(double cx, double cy, double rx, double ry,
double start_angle, double sweep_angle,
double* curve)
{
double x0 = cos(sweep_angle / 2.0);
double y0 = sin(sweep_angle / 2.0);
double tx = (1.0 - x0) * 4.0 / 3.0;
double ty = y0 - tx * x0 / y0;
double px[4];
double py[4];
px[0] = x0;
py[0] = -y0;
px[1] = x0 + tx;
py[1] = -ty;
px[2] = x0 + tx;
py[2] = ty;
px[3] = x0;
py[3] = y0;
double sn = sin(start_angle + sweep_angle / 2.0);
double cs = cos(start_angle + sweep_angle / 2.0);
unsigned i;
for(i = 0; i < 4; i++)
{
curve[i * 2] = cx + rx * (px[i] * cs - py[i] * sn);
curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs);
}
}
//------------------------------------------------------------------------
void bezier_arc::init(double x, double y,
double rx, double ry,
double start_angle,
double sweep_angle)
{
start_angle = fmod(start_angle, 2.0 * pi);
if(sweep_angle >= 2.0 * pi) sweep_angle = 2.0 * pi;
if(sweep_angle <= -2.0 * pi) sweep_angle = -2.0 * pi;
if(fabs(sweep_angle) < 1e-10)
{
m_num_vertices = 4;
m_cmd = path_cmd_line_to;
m_vertices[0] = x + rx * cos(start_angle);
m_vertices[1] = y + ry * sin(start_angle);
m_vertices[2] = x + rx * cos(start_angle + sweep_angle);
m_vertices[3] = y + ry * sin(start_angle + sweep_angle);
return;
}
double total_sweep = 0.0;
double local_sweep = 0.0;
double prev_sweep;
m_num_vertices = 2;
m_cmd = path_cmd_curve4;
bool done = false;
do
{
if(sweep_angle < 0.0)
{
prev_sweep = total_sweep;
local_sweep = -pi * 0.5;
total_sweep -= pi * 0.5;
if(total_sweep <= sweep_angle + bezier_arc_angle_epsilon)
{
local_sweep = sweep_angle - prev_sweep;
done = true;
}
}
else
{
prev_sweep = total_sweep;
local_sweep = pi * 0.5;
total_sweep += pi * 0.5;
if(total_sweep >= sweep_angle - bezier_arc_angle_epsilon)
{
local_sweep = sweep_angle - prev_sweep;
done = true;
}
}
arc_to_bezier(x, y, rx, ry,
start_angle,
local_sweep,
m_vertices + m_num_vertices - 2);
m_num_vertices += 6;
start_angle += local_sweep;
}
while(!done && m_num_vertices < 26);
}
//--------------------------------------------------------------------
void bezier_arc_svg::init(double x0, double y0,
double rx, double ry,
double angle,
bool large_arc_flag,
bool sweep_flag,
double x2, double y2)
{
m_radii_ok = true;
if(rx < 0.0) rx = -rx;
if(ry < 0.0) ry = -rx;
// Calculate the middle point between
// the current and the final points
//------------------------
double dx2 = (x0 - x2) / 2.0;
double dy2 = (y0 - y2) / 2.0;
double cos_a = cos(angle);
double sin_a = sin(angle);
// Calculate (x1, y1)
//------------------------
double x1 = cos_a * dx2 + sin_a * dy2;
double y1 = -sin_a * dx2 + cos_a * dy2;
// Ensure radii are large enough
//------------------------
double prx = rx * rx;
double pry = ry * ry;
double px1 = x1 * x1;
double py1 = y1 * y1;
// Check that radii are large enough
//------------------------
double radii_check = px1/prx + py1/pry;
if(radii_check > 1.0)
{
rx = sqrt(radii_check) * rx;
ry = sqrt(radii_check) * ry;
prx = rx * rx;
pry = ry * ry;
if(radii_check > 10.0) m_radii_ok = false;
}
// Calculate (cx1, cy1)
//------------------------
double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0;
double sq = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1);
double coef = sign * sqrt((sq < 0) ? 0 : sq);
double cx1 = coef * ((rx * y1) / ry);
double cy1 = coef * -((ry * x1) / rx);
//
// Calculate (cx, cy) from (cx1, cy1)
//------------------------
double sx2 = (x0 + x2) / 2.0;
double sy2 = (y0 + y2) / 2.0;
double cx = sx2 + (cos_a * cx1 - sin_a * cy1);
double cy = sy2 + (sin_a * cx1 + cos_a * cy1);
// Calculate the start_angle (angle1) and the sweep_angle (dangle)
//------------------------
double ux = (x1 - cx1) / rx;
double uy = (y1 - cy1) / ry;
double vx = (-x1 - cx1) / rx;
double vy = (-y1 - cy1) / ry;
double p, n;
// Calculate the angle start
//------------------------
n = sqrt(ux*ux + uy*uy);
p = ux; // (1 * ux) + (0 * uy)
sign = (uy < 0) ? -1.0 : 1.0;
double v = p / n;
if(v < -1.0) v = -1.0;
if(v > 1.0) v = 1.0;
double start_angle = sign * acos(v);
// Calculate the sweep angle
//------------------------
n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy));
p = ux * vx + uy * vy;
sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
v = p / n;
if(v < -1.0) v = -1.0;
if(v > 1.0) v = 1.0;
double sweep_angle = sign * acos(v);
if(!sweep_flag && sweep_angle > 0)
{
sweep_angle -= pi * 2.0;
}
else
if (sweep_flag && sweep_angle < 0)
{
sweep_angle += pi * 2.0;
}
// We can now build and transform the resulting arc
//------------------------
m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle);
trans_affine mtx = trans_affine_rotation(angle);
mtx *= trans_affine_translation(cx, cy);
for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2)
{
mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1);
}
// We must make sure that the starting and ending points
// exactly coincide with the initial (x0,y0) and (x2,y2)
m_arc.vertices()[0] = x0;
m_arc.vertices()[1] = y0;
if(m_arc.num_vertices() > 2)
{
m_arc.vertices()[m_arc.num_vertices() - 2] = x2;
m_arc.vertices()[m_arc.num_vertices() - 1] = y2;
}
}
}
+620
View File
@@ -0,0 +1,620 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://antigrain.com
//
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
//----------------------------------------------------------------------------
#include <math.h>
#include <agg/agg_curves.h>
#include <agg/agg_math.h>
namespace agg
{
//------------------------------------------------------------------------
const double curve_distance_epsilon = 1e-30;
const double curve_collinearity_epsilon = 1e-30;
const double curve_angle_tolerance_epsilon = 0.01;
enum curve_recursion_limit_e { curve_recursion_limit = 32 };
//------------------------------------------------------------------------
void curve3_inc::approximation_scale(double s)
{
m_scale = s;
}
//------------------------------------------------------------------------
double curve3_inc::approximation_scale() const
{
return m_scale;
}
//------------------------------------------------------------------------
void curve3_inc::init(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
m_start_x = x1;
m_start_y = y1;
m_end_x = x3;
m_end_y = y3;
double dx1 = x2 - x1;
double dy1 = y2 - y1;
double dx2 = x3 - x2;
double dy2 = y3 - y2;
double len = sqrt(dx1 * dx1 + dy1 * dy1) + sqrt(dx2 * dx2 + dy2 * dy2);
m_num_steps = uround(len * 0.25 * m_scale);
if(m_num_steps < 4)
{
m_num_steps = 4;
}
double subdivide_step = 1.0 / m_num_steps;
double subdivide_step2 = subdivide_step * subdivide_step;
double tmpx = (x1 - x2 * 2.0 + x3) * subdivide_step2;
double tmpy = (y1 - y2 * 2.0 + y3) * subdivide_step2;
m_saved_fx = m_fx = x1;
m_saved_fy = m_fy = y1;
m_saved_dfx = m_dfx = tmpx + (x2 - x1) * (2.0 * subdivide_step);
m_saved_dfy = m_dfy = tmpy + (y2 - y1) * (2.0 * subdivide_step);
m_ddfx = tmpx * 2.0;
m_ddfy = tmpy * 2.0;
m_step = m_num_steps;
}
//------------------------------------------------------------------------
void curve3_inc::rewind(unsigned)
{
if(m_num_steps == 0)
{
m_step = -1;
return;
}
m_step = m_num_steps;
m_fx = m_saved_fx;
m_fy = m_saved_fy;
m_dfx = m_saved_dfx;
m_dfy = m_saved_dfy;
}
//------------------------------------------------------------------------
unsigned curve3_inc::vertex(double* x, double* y)
{
if(m_step < 0) return path_cmd_stop;
if(m_step == m_num_steps)
{
*x = m_start_x;
*y = m_start_y;
--m_step;
return path_cmd_move_to;
}
if(m_step == 0)
{
*x = m_end_x;
*y = m_end_y;
--m_step;
return path_cmd_line_to;
}
m_fx += m_dfx;
m_fy += m_dfy;
m_dfx += m_ddfx;
m_dfy += m_ddfy;
*x = m_fx;
*y = m_fy;
--m_step;
return path_cmd_line_to;
}
//------------------------------------------------------------------------
void curve3_div::init(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
m_points.remove_all();
m_distance_tolerance_square = 0.5 / m_approximation_scale;
m_distance_tolerance_square *= m_distance_tolerance_square;
bezier(x1, y1, x2, y2, x3, y3);
m_count = 0;
}
//------------------------------------------------------------------------
void curve3_div::recursive_bezier(double x1, double y1,
double x2, double y2,
double x3, double y3,
unsigned level)
{
if(level > curve_recursion_limit)
{
return;
}
// Calculate all the mid-points of the line segments
//----------------------
double x12 = (x1 + x2) / 2;
double y12 = (y1 + y2) / 2;
double x23 = (x2 + x3) / 2;
double y23 = (y2 + y3) / 2;
double x123 = (x12 + x23) / 2;
double y123 = (y12 + y23) / 2;
double dx = x3-x1;
double dy = y3-y1;
double d = fabs(((x2 - x3) * dy - (y2 - y3) * dx));
double da;
if(d > curve_collinearity_epsilon)
{
// Regular case
//-----------------
if(d * d <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
// If the curvature doesn't exceed the distance_tolerance value
// we tend to finish subdivisions.
//----------------------
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
m_points.add(point_d(x123, y123));
return;
}
// Angle & Cusp Condition
//----------------------
da = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
if(da >= pi) da = 2*pi - da;
if(da < m_angle_tolerance)
{
// Finally we can stop the recursion
//----------------------
m_points.add(point_d(x123, y123));
return;
}
}
}
else
{
// Collinear case
//------------------
da = dx*dx + dy*dy;
if(da == 0)
{
d = calc_sq_distance(x1, y1, x2, y2);
}
else
{
d = ((x2 - x1)*dx + (y2 - y1)*dy) / da;
if(d > 0 && d < 1)
{
// Simple collinear case, 1---2---3
// We can leave just two endpoints
return;
}
if(d <= 0) d = calc_sq_distance(x2, y2, x1, y1);
else if(d >= 1) d = calc_sq_distance(x2, y2, x3, y3);
else d = calc_sq_distance(x2, y2, x1 + d*dx, y1 + d*dy);
}
if(d < m_distance_tolerance_square)
{
m_points.add(point_d(x2, y2));
return;
}
}
// Continue subdivision
//----------------------
recursive_bezier(x1, y1, x12, y12, x123, y123, level + 1);
recursive_bezier(x123, y123, x23, y23, x3, y3, level + 1);
}
//------------------------------------------------------------------------
void curve3_div::bezier(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
m_points.add(point_d(x1, y1));
recursive_bezier(x1, y1, x2, y2, x3, y3, 0);
m_points.add(point_d(x3, y3));
}
//------------------------------------------------------------------------
void curve4_inc::approximation_scale(double s)
{
m_scale = s;
}
//------------------------------------------------------------------------
double curve4_inc::approximation_scale() const
{
return m_scale;
}
//------------------------------------------------------------------------
static double MSC60_fix_ICE(double v) { return v; }
//------------------------------------------------------------------------
void curve4_inc::init(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4)
{
m_start_x = x1;
m_start_y = y1;
m_end_x = x4;
m_end_y = y4;
double dx1 = x2 - x1;
double dy1 = y2 - y1;
double dx2 = x3 - x2;
double dy2 = y3 - y2;
double dx3 = x4 - x3;
double dy3 = y4 - y3;
double len = (sqrt(dx1 * dx1 + dy1 * dy1) +
sqrt(dx2 * dx2 + dy2 * dy2) +
sqrt(dx3 * dx3 + dy3 * dy3)) * 0.25 * m_scale;
#if defined(_MSC_VER) && _MSC_VER <= 1200
m_num_steps = uround(MSC60_fix_ICE(len));
#else
m_num_steps = uround(len);
#endif
if(m_num_steps < 4)
{
m_num_steps = 4;
}
double subdivide_step = 1.0 / m_num_steps;
double subdivide_step2 = subdivide_step * subdivide_step;
double subdivide_step3 = subdivide_step * subdivide_step * subdivide_step;
double pre1 = 3.0 * subdivide_step;
double pre2 = 3.0 * subdivide_step2;
double pre4 = 6.0 * subdivide_step2;
double pre5 = 6.0 * subdivide_step3;
double tmp1x = x1 - x2 * 2.0 + x3;
double tmp1y = y1 - y2 * 2.0 + y3;
double tmp2x = (x2 - x3) * 3.0 - x1 + x4;
double tmp2y = (y2 - y3) * 3.0 - y1 + y4;
m_saved_fx = m_fx = x1;
m_saved_fy = m_fy = y1;
m_saved_dfx = m_dfx = (x2 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdivide_step3;
m_saved_dfy = m_dfy = (y2 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdivide_step3;
m_saved_ddfx = m_ddfx = tmp1x * pre4 + tmp2x * pre5;
m_saved_ddfy = m_ddfy = tmp1y * pre4 + tmp2y * pre5;
m_dddfx = tmp2x * pre5;
m_dddfy = tmp2y * pre5;
m_step = m_num_steps;
}
//------------------------------------------------------------------------
void curve4_inc::rewind(unsigned)
{
if(m_num_steps == 0)
{
m_step = -1;
return;
}
m_step = m_num_steps;
m_fx = m_saved_fx;
m_fy = m_saved_fy;
m_dfx = m_saved_dfx;
m_dfy = m_saved_dfy;
m_ddfx = m_saved_ddfx;
m_ddfy = m_saved_ddfy;
}
//------------------------------------------------------------------------
unsigned curve4_inc::vertex(double* x, double* y)
{
if(m_step < 0) return path_cmd_stop;
if(m_step == m_num_steps)
{
*x = m_start_x;
*y = m_start_y;
--m_step;
return path_cmd_move_to;
}
if(m_step == 0)
{
*x = m_end_x;
*y = m_end_y;
--m_step;
return path_cmd_line_to;
}
m_fx += m_dfx;
m_fy += m_dfy;
m_dfx += m_ddfx;
m_dfy += m_ddfy;
m_ddfx += m_dddfx;
m_ddfy += m_dddfy;
*x = m_fx;
*y = m_fy;
--m_step;
return path_cmd_line_to;
}
//------------------------------------------------------------------------
void curve4_div::init(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4)
{
m_points.remove_all();
m_distance_tolerance_square = 0.5 / m_approximation_scale;
m_distance_tolerance_square *= m_distance_tolerance_square;
bezier(x1, y1, x2, y2, x3, y3, x4, y4);
m_count = 0;
}
//------------------------------------------------------------------------
void curve4_div::recursive_bezier(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4,
unsigned level)
{
if(level > curve_recursion_limit)
{
return;
}
// Calculate all the mid-points of the line segments
//----------------------
double x12 = (x1 + x2) / 2;
double y12 = (y1 + y2) / 2;
double x23 = (x2 + x3) / 2;
double y23 = (y2 + y3) / 2;
double x34 = (x3 + x4) / 2;
double y34 = (y3 + y4) / 2;
double x123 = (x12 + x23) / 2;
double y123 = (y12 + y23) / 2;
double x234 = (x23 + x34) / 2;
double y234 = (y23 + y34) / 2;
double x1234 = (x123 + x234) / 2;
double y1234 = (y123 + y234) / 2;
// Try to approximate the full cubic curve by a single straight line
//------------------
double dx = x4-x1;
double dy = y4-y1;
double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx));
double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx));
double da1, da2, k;
switch((int(d2 > curve_collinearity_epsilon) << 1) +
int(d3 > curve_collinearity_epsilon))
{
case 0:
// All collinear OR p1==p4
//----------------------
k = dx*dx + dy*dy;
if(k == 0)
{
d2 = calc_sq_distance(x1, y1, x2, y2);
d3 = calc_sq_distance(x4, y4, x3, y3);
}
else
{
k = 1 / k;
da1 = x2 - x1;
da2 = y2 - y1;
d2 = k * (da1*dx + da2*dy);
da1 = x3 - x1;
da2 = y3 - y1;
d3 = k * (da1*dx + da2*dy);
if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1)
{
// Simple collinear case, 1---2---3---4
// We can leave just two endpoints
return;
}
if(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1);
else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4);
else d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);
if(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1);
else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4);
else d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);
}
if(d2 > d3)
{
if(d2 < m_distance_tolerance_square)
{
m_points.add(point_d(x2, y2));
return;
}
}
else
{
if(d3 < m_distance_tolerance_square)
{
m_points.add(point_d(x3, y3));
return;
}
}
break;
case 1:
// p1,p2,p4 are collinear, p3 is significant
//----------------------
if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
m_points.add(point_d(x23, y23));
return;
}
// Angle Condition
//----------------------
da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2));
if(da1 >= pi) da1 = 2*pi - da1;
if(da1 < m_angle_tolerance)
{
m_points.add(point_d(x2, y2));
m_points.add(point_d(x3, y3));
return;
}
if(m_cusp_limit != 0.0)
{
if(da1 > m_cusp_limit)
{
m_points.add(point_d(x3, y3));
return;
}
}
}
break;
case 2:
// p1,p3,p4 are collinear, p2 is significant
//----------------------
if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
m_points.add(point_d(x23, y23));
return;
}
// Angle Condition
//----------------------
da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
if(da1 >= pi) da1 = 2*pi - da1;
if(da1 < m_angle_tolerance)
{
m_points.add(point_d(x2, y2));
m_points.add(point_d(x3, y3));
return;
}
if(m_cusp_limit != 0.0)
{
if(da1 > m_cusp_limit)
{
m_points.add(point_d(x2, y2));
return;
}
}
}
break;
case 3:
// Regular case
//-----------------
if((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy))
{
// If the curvature doesn't exceed the distance_tolerance value
// we tend to finish subdivisions.
//----------------------
if(m_angle_tolerance < curve_angle_tolerance_epsilon)
{
m_points.add(point_d(x23, y23));
return;
}
// Angle & Cusp Condition
//----------------------
k = atan2(y3 - y2, x3 - x2);
da1 = fabs(k - atan2(y2 - y1, x2 - x1));
da2 = fabs(atan2(y4 - y3, x4 - x3) - k);
if(da1 >= pi) da1 = 2*pi - da1;
if(da2 >= pi) da2 = 2*pi - da2;
if(da1 + da2 < m_angle_tolerance)
{
// Finally we can stop the recursion
//----------------------
m_points.add(point_d(x23, y23));
return;
}
if(m_cusp_limit != 0.0)
{
if(da1 > m_cusp_limit)
{
m_points.add(point_d(x2, y2));
return;
}
if(da2 > m_cusp_limit)
{
m_points.add(point_d(x3, y3));
return;
}
}
}
break;
}
// Continue subdivision
//----------------------
recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1);
recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1);
}
//------------------------------------------------------------------------
void curve4_div::bezier(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4)
{
m_points.add(point_d(x1, y1));
recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0);
m_points.add(point_d(x4, y4));
}
}
+69
View File
@@ -0,0 +1,69 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG exception
//
//----------------------------------------------------------------------------
#ifndef AGG_SVG_EXCEPTION_INCLUDED
#define AGG_SVG_EXCEPTION_INCLUDED
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
namespace agg
{
namespace svg
{
class exception
{
public:
~exception()
{
delete [] m_msg;
}
exception() : m_msg(0) {}
exception(const char* fmt, ...) :
m_msg(0)
{
if(fmt)
{
m_msg = new char [4096];
va_list arg;
va_start(arg, fmt);
vsprintf(m_msg, fmt, arg);
va_end(arg);
}
}
exception(const exception& exc) :
m_msg(exc.m_msg ? new char[strlen(exc.m_msg) + 1] : 0)
{
if(m_msg) strcpy(m_msg, exc.m_msg);
}
const char* msg() const { return m_msg; }
private:
char* m_msg;
};
}
}
#endif
+886
View File
@@ -0,0 +1,886 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG parser.
//
//----------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "agg_svg_parser.h"
#include "expat.h"
namespace agg
{
namespace svg
{
struct named_color
{
char name[22];
int8u r, g, b, a;
};
named_color colors[] =
{
{ "aliceblue",240,248,255, 255 },
{ "antiquewhite",250,235,215, 255 },
{ "aqua",0,255,255, 255 },
{ "aquamarine",127,255,212, 255 },
{ "azure",240,255,255, 255 },
{ "beige",245,245,220, 255 },
{ "bisque",255,228,196, 255 },
{ "black",0,0,0, 255 },
{ "blanchedalmond",255,235,205, 255 },
{ "blue",0,0,255, 255 },
{ "blueviolet",138,43,226, 255 },
{ "brown",165,42,42, 255 },
{ "burlywood",222,184,135, 255 },
{ "cadetblue",95,158,160, 255 },
{ "chartreuse",127,255,0, 255 },
{ "chocolate",210,105,30, 255 },
{ "coral",255,127,80, 255 },
{ "cornflowerblue",100,149,237, 255 },
{ "cornsilk",255,248,220, 255 },
{ "crimson",220,20,60, 255 },
{ "cyan",0,255,255, 255 },
{ "darkblue",0,0,139, 255 },
{ "darkcyan",0,139,139, 255 },
{ "darkgoldenrod",184,134,11, 255 },
{ "darkgray",169,169,169, 255 },
{ "darkgreen",0,100,0, 255 },
{ "darkgrey",169,169,169, 255 },
{ "darkkhaki",189,183,107, 255 },
{ "darkmagenta",139,0,139, 255 },
{ "darkolivegreen",85,107,47, 255 },
{ "darkorange",255,140,0, 255 },
{ "darkorchid",153,50,204, 255 },
{ "darkred",139,0,0, 255 },
{ "darksalmon",233,150,122, 255 },
{ "darkseagreen",143,188,143, 255 },
{ "darkslateblue",72,61,139, 255 },
{ "darkslategray",47,79,79, 255 },
{ "darkslategrey",47,79,79, 255 },
{ "darkturquoise",0,206,209, 255 },
{ "darkviolet",148,0,211, 255 },
{ "deeppink",255,20,147, 255 },
{ "deepskyblue",0,191,255, 255 },
{ "dimgray",105,105,105, 255 },
{ "dimgrey",105,105,105, 255 },
{ "dodgerblue",30,144,255, 255 },
{ "firebrick",178,34,34, 255 },
{ "floralwhite",255,250,240, 255 },
{ "forestgreen",34,139,34, 255 },
{ "fuchsia",255,0,255, 255 },
{ "gainsboro",220,220,220, 255 },
{ "ghostwhite",248,248,255, 255 },
{ "gold",255,215,0, 255 },
{ "goldenrod",218,165,32, 255 },
{ "gray",128,128,128, 255 },
{ "green",0,128,0, 255 },
{ "greenyellow",173,255,47, 255 },
{ "grey",128,128,128, 255 },
{ "honeydew",240,255,240, 255 },
{ "hotpink",255,105,180, 255 },
{ "indianred",205,92,92, 255 },
{ "indigo",75,0,130, 255 },
{ "ivory",255,255,240, 255 },
{ "khaki",240,230,140, 255 },
{ "lavender",230,230,250, 255 },
{ "lavenderblush",255,240,245, 255 },
{ "lawngreen",124,252,0, 255 },
{ "lemonchiffon",255,250,205, 255 },
{ "lightblue",173,216,230, 255 },
{ "lightcoral",240,128,128, 255 },
{ "lightcyan",224,255,255, 255 },
{ "lightgoldenrodyellow",250,250,210, 255 },
{ "lightgray",211,211,211, 255 },
{ "lightgreen",144,238,144, 255 },
{ "lightgrey",211,211,211, 255 },
{ "lightpink",255,182,193, 255 },
{ "lightsalmon",255,160,122, 255 },
{ "lightseagreen",32,178,170, 255 },
{ "lightskyblue",135,206,250, 255 },
{ "lightslategray",119,136,153, 255 },
{ "lightslategrey",119,136,153, 255 },
{ "lightsteelblue",176,196,222, 255 },
{ "lightyellow",255,255,224, 255 },
{ "lime",0,255,0, 255 },
{ "limegreen",50,205,50, 255 },
{ "linen",250,240,230, 255 },
{ "magenta",255,0,255, 255 },
{ "maroon",128,0,0, 255 },
{ "mediumaquamarine",102,205,170, 255 },
{ "mediumblue",0,0,205, 255 },
{ "mediumorchid",186,85,211, 255 },
{ "mediumpurple",147,112,219, 255 },
{ "mediumseagreen",60,179,113, 255 },
{ "mediumslateblue",123,104,238, 255 },
{ "mediumspringgreen",0,250,154, 255 },
{ "mediumturquoise",72,209,204, 255 },
{ "mediumvioletred",199,21,133, 255 },
{ "midnightblue",25,25,112, 255 },
{ "mintcream",245,255,250, 255 },
{ "mistyrose",255,228,225, 255 },
{ "moccasin",255,228,181, 255 },
{ "navajowhite",255,222,173, 255 },
{ "navy",0,0,128, 255 },
{ "oldlace",253,245,230, 255 },
{ "olive",128,128,0, 255 },
{ "olivedrab",107,142,35, 255 },
{ "orange",255,165,0, 255 },
{ "orangered",255,69,0, 255 },
{ "orchid",218,112,214, 255 },
{ "palegoldenrod",238,232,170, 255 },
{ "palegreen",152,251,152, 255 },
{ "paleturquoise",175,238,238, 255 },
{ "palevioletred",219,112,147, 255 },
{ "papayawhip",255,239,213, 255 },
{ "peachpuff",255,218,185, 255 },
{ "peru",205,133,63, 255 },
{ "pink",255,192,203, 255 },
{ "plum",221,160,221, 255 },
{ "powderblue",176,224,230, 255 },
{ "purple",128,0,128, 255 },
{ "red",255,0,0, 255 },
{ "rosybrown",188,143,143, 255 },
{ "royalblue",65,105,225, 255 },
{ "saddlebrown",139,69,19, 255 },
{ "salmon",250,128,114, 255 },
{ "sandybrown",244,164,96, 255 },
{ "seagreen",46,139,87, 255 },
{ "seashell",255,245,238, 255 },
{ "sienna",160,82,45, 255 },
{ "silver",192,192,192, 255 },
{ "skyblue",135,206,235, 255 },
{ "slateblue",106,90,205, 255 },
{ "slategray",112,128,144, 255 },
{ "slategrey",112,128,144, 255 },
{ "snow",255,250,250, 255 },
{ "springgreen",0,255,127, 255 },
{ "steelblue",70,130,180, 255 },
{ "tan",210,180,140, 255 },
{ "teal",0,128,128, 255 },
{ "thistle",216,191,216, 255 },
{ "tomato",255,99,71, 255 },
{ "turquoise",64,224,208, 255 },
{ "violet",238,130,238, 255 },
{ "wheat",245,222,179, 255 },
{ "white",255,255,255, 255 },
{ "whitesmoke",245,245,245, 255 },
{ "yellow",255,255,0, 255 },
{ "yellowgreen",154,205,50, 255 },
{ "zzzzzzzzzzz",0,0,0, 0 }
};
//------------------------------------------------------------------------
parser::~parser()
{
delete [] m_attr_value;
delete [] m_attr_name;
delete [] m_buf;
delete [] m_title;
}
//------------------------------------------------------------------------
parser::parser(path_renderer& path) :
m_path(path),
m_tokenizer(),
m_buf(new char[buf_size]),
m_title(new char[256]),
m_title_len(0),
m_title_flag(false),
m_path_flag(false),
m_attr_name(new char[128]),
m_attr_value(new char[1024]),
m_attr_name_len(127),
m_attr_value_len(1023)
{
m_title[0] = 0;
}
//------------------------------------------------------------------------
void parser::parse(const char* fname)
{
char msg[1024];
XML_Parser p = XML_ParserCreate(NULL);
if(p == 0)
{
throw exception("Couldn't allocate memory for parser");
}
XML_SetUserData(p, this);
XML_SetElementHandler(p, start_element, end_element);
XML_SetCharacterDataHandler(p, content);
FILE* fd = fopen(fname, "r");
if(fd == 0)
{
sprintf(msg, "Couldn't open file %s", fname);
throw exception(msg);
}
bool done = false;
do
{
size_t len = fread(m_buf, 1, buf_size, fd);
done = len < buf_size;
if(!XML_Parse(p, m_buf, len, done))
{
sprintf(msg,
"%s at line %d\n",
XML_ErrorString(XML_GetErrorCode(p)),
XML_GetCurrentLineNumber(p));
throw exception(msg);
}
}
while(!done);
fclose(fd);
XML_ParserFree(p);
char* ts = m_title;
while(*ts)
{
if(*ts < ' ') *ts = ' ';
++ts;
}
}
//------------------------------------------------------------------------
void parser::start_element(void* data, const char* el, const char** attr)
{
parser& self = *(parser*)data;
if(strcmp(el, "title") == 0)
{
self.m_title_flag = true;
}
else
if(strcmp(el, "g") == 0)
{
self.m_path.push_attr();
self.parse_attr(attr);
}
else
if(strcmp(el, "path") == 0)
{
if(self.m_path_flag)
{
throw exception("start_element: Nested path");
}
self.m_path.begin_path();
self.parse_path(attr);
self.m_path.end_path();
self.m_path_flag = true;
}
else
if(strcmp(el, "rect") == 0)
{
self.parse_rect(attr);
}
else
if(strcmp(el, "line") == 0)
{
self.parse_line(attr);
}
else
if(strcmp(el, "polyline") == 0)
{
self.parse_poly(attr, false);
}
else
if(strcmp(el, "polygon") == 0)
{
self.parse_poly(attr, true);
}
//else
//if(strcmp(el, "<OTHER_ELEMENTS>") == 0)
//{
//}
// . . .
}
//------------------------------------------------------------------------
void parser::end_element(void* data, const char* el)
{
parser& self = *(parser*)data;
if(strcmp(el, "title") == 0)
{
self.m_title_flag = false;
}
else
if(strcmp(el, "g") == 0)
{
self.m_path.pop_attr();
}
else
if(strcmp(el, "path") == 0)
{
self.m_path_flag = false;
}
//else
//if(strcmp(el, "<OTHER_ELEMENTS>") == 0)
//{
//}
// . . .
}
//------------------------------------------------------------------------
void parser::content(void* data, const char* s, int len)
{
parser& self = *(parser*)data;
// m_title_flag signals that the <title> tag is being parsed now.
// The following code concatenates the pieces of content of the <title> tag.
if(self.m_title_flag)
{
if(len + self.m_title_len > 255) len = 255 - self.m_title_len;
if(len > 0)
{
memcpy(self.m_title + self.m_title_len, s, len);
self.m_title_len += len;
self.m_title[self.m_title_len] = 0;
}
}
}
//------------------------------------------------------------------------
void parser::parse_attr(const char** attr)
{
int i;
for(i = 0; attr[i]; i += 2)
{
if(strcmp(attr[i], "style") == 0)
{
parse_style(attr[i + 1]);
}
else
{
parse_attr(attr[i], attr[i + 1]);
}
}
}
//-------------------------------------------------------------
void parser::parse_path(const char** attr)
{
int i;
for(i = 0; attr[i]; i += 2)
{
// The <path> tag can consist of the path itself ("d=")
// as well as of other parameters like "style=", "transform=", etc.
// In the last case we simply rely on the function of parsing
// attributes (see 'else' branch).
if(strcmp(attr[i], "d") == 0)
{
m_tokenizer.set_path_str(attr[i + 1]);
m_path.parse_path(m_tokenizer);
}
else
{
// Create a temporary single pair "name-value" in order
// to avoid multiple calls for the same attribute.
const char* tmp[4];
tmp[0] = attr[i];
tmp[1] = attr[i + 1];
tmp[2] = 0;
tmp[3] = 0;
parse_attr(tmp);
}
}
}
//-------------------------------------------------------------
int cmp_color(const void* p1, const void* p2)
{
return strcmp(((named_color*)p1)->name, ((named_color*)p2)->name);
}
//-------------------------------------------------------------
rgba8 parse_color(const char* str)
{
while(*str == ' ') ++str;
unsigned c = 0;
if(*str == '#')
{
sscanf(str + 1, "%x", &c);
return rgb8_packed(c);
}
else
{
named_color c;
unsigned len = strlen(str);
if(len > sizeof(c.name) - 1)
{
throw exception("parse_color: Invalid color name '%s'", str);
}
strcpy(c.name, str);
const void* p = bsearch(&c,
colors,
sizeof(colors) / sizeof(colors[0]),
sizeof(colors[0]),
cmp_color);
if(p == 0)
{
throw exception("parse_color: Invalid color name '%s'", str);
}
const named_color* pc = (const named_color*)p;
return rgba8(pc->r, pc->g, pc->b, pc->a);
}
}
double parse_double(const char* str)
{
while(*str == ' ') ++str;
return atof(str);
}
//-------------------------------------------------------------
bool parser::parse_attr(const char* name, const char* value)
{
if(strcmp(name, "style") == 0)
{
parse_style(value);
}
else
if(strcmp(name, "fill") == 0)
{
if(strcmp(value, "none") == 0)
{
m_path.fill_none();
}
else
{
m_path.fill(parse_color(value));
}
}
else
if(strcmp(name, "fill-opacity") == 0)
{
m_path.fill_opacity(parse_double(value));
}
else
if(strcmp(name, "stroke") == 0)
{
if(strcmp(value, "none") == 0)
{
m_path.stroke_none();
}
else
{
m_path.stroke(parse_color(value));
}
}
else
if(strcmp(name, "stroke-width") == 0)
{
m_path.stroke_width(parse_double(value));
}
else
if(strcmp(name, "stroke-linecap") == 0)
{
if(strcmp(value, "butt") == 0) m_path.line_cap(butt_cap);
else if(strcmp(value, "round") == 0) m_path.line_cap(round_cap);
else if(strcmp(value, "square") == 0) m_path.line_cap(square_cap);
}
else
if(strcmp(name, "stroke-linejoin") == 0)
{
if(strcmp(value, "miter") == 0) m_path.line_join(miter_join);
else if(strcmp(value, "round") == 0) m_path.line_join(round_join);
else if(strcmp(value, "bevel") == 0) m_path.line_join(bevel_join);
}
else
if(strcmp(name, "stroke-miterlimit") == 0)
{
m_path.miter_limit(parse_double(value));
}
else
if(strcmp(name, "stroke-opacity") == 0)
{
m_path.stroke_opacity(parse_double(value));
}
else
if(strcmp(name, "transform") == 0)
{
parse_transform(value);
}
//else
//if(strcmp(el, "<OTHER_ATTRIBUTES>") == 0)
//{
//}
// . . .
else
{
return false;
}
return true;
}
//-------------------------------------------------------------
void parser::copy_name(const char* start, const char* end)
{
unsigned len = unsigned(end - start);
if(m_attr_name_len == 0 || len > m_attr_name_len)
{
delete [] m_attr_name;
m_attr_name = new char[len + 1];
m_attr_name_len = len;
}
if(len) memcpy(m_attr_name, start, len);
m_attr_name[len] = 0;
}
//-------------------------------------------------------------
void parser::copy_value(const char* start, const char* end)
{
unsigned len = unsigned(end - start);
if(m_attr_value_len == 0 || len > m_attr_value_len)
{
delete [] m_attr_value;
m_attr_value = new char[len + 1];
m_attr_value_len = len;
}
if(len) memcpy(m_attr_value, start, len);
m_attr_value[len] = 0;
}
//-------------------------------------------------------------
bool parser::parse_name_value(const char* nv_start, const char* nv_end)
{
const char* str = nv_start;
while(str < nv_end && *str != ':') ++str;
const char* val = str;
// Right Trim
while(str > nv_start &&
(*str == ':' || isspace(*str))) --str;
++str;
copy_name(nv_start, str);
while(val < nv_end && (*val == ':' || isspace(*val))) ++val;
copy_value(val, nv_end);
return parse_attr(m_attr_name, m_attr_value);
}
//-------------------------------------------------------------
void parser::parse_style(const char* str)
{
while(*str)
{
// Left Trim
while(*str && isspace(*str)) ++str;
const char* nv_start = str;
while(*str && *str != ';') ++str;
const char* nv_end = str;
// Right Trim
while(nv_end > nv_start &&
(*nv_end == ';' || isspace(*nv_end))) --nv_end;
++nv_end;
parse_name_value(nv_start, nv_end);
if(*str) ++str;
}
}
//-------------------------------------------------------------
void parser::parse_rect(const char** attr)
{
int i;
double x = 0.0;
double y = 0.0;
double w = 0.0;
double h = 0.0;
m_path.begin_path();
for(i = 0; attr[i]; i += 2)
{
if(!parse_attr(attr[i], attr[i + 1]))
{
if(strcmp(attr[i], "x") == 0) x = parse_double(attr[i + 1]);
if(strcmp(attr[i], "y") == 0) y = parse_double(attr[i + 1]);
if(strcmp(attr[i], "width") == 0) w = parse_double(attr[i + 1]);
if(strcmp(attr[i], "height") == 0) h = parse_double(attr[i + 1]);
// rx - to be implemented
// ry - to be implemented
}
}
if(w != 0.0 && h != 0.0)
{
if(w < 0.0) throw exception("parse_rect: Invalid width: %f", w);
if(h < 0.0) throw exception("parse_rect: Invalid height: %f", h);
m_path.move_to(x, y);
m_path.line_to(x + w, y);
m_path.line_to(x + w, y + h);
m_path.line_to(x, y + h);
m_path.close_subpath();
}
m_path.end_path();
}
//-------------------------------------------------------------
void parser::parse_line(const char** attr)
{
int i;
double x1 = 0.0;
double y1 = 0.0;
double x2 = 0.0;
double y2 = 0.0;
m_path.begin_path();
for(i = 0; attr[i]; i += 2)
{
if(!parse_attr(attr[i], attr[i + 1]))
{
if(strcmp(attr[i], "x1") == 0) x1 = parse_double(attr[i + 1]);
if(strcmp(attr[i], "y1") == 0) y1 = parse_double(attr[i + 1]);
if(strcmp(attr[i], "x2") == 0) x2 = parse_double(attr[i + 1]);
if(strcmp(attr[i], "y2") == 0) y2 = parse_double(attr[i + 1]);
}
}
m_path.move_to(x1, y1);
m_path.line_to(x2, y2);
m_path.end_path();
}
//-------------------------------------------------------------
void parser::parse_poly(const char** attr, bool close_flag)
{
int i;
double x = 0.0;
double y = 0.0;
m_path.begin_path();
for(i = 0; attr[i]; i += 2)
{
if(!parse_attr(attr[i], attr[i + 1]))
{
if(strcmp(attr[i], "points") == 0)
{
m_tokenizer.set_path_str(attr[i + 1]);
if(!m_tokenizer.next())
{
throw exception("parse_poly: Too few coordinates");
}
x = m_tokenizer.last_number();
if(!m_tokenizer.next())
{
throw exception("parse_poly: Too few coordinates");
}
y = m_tokenizer.last_number();
m_path.move_to(x, y);
while(m_tokenizer.next())
{
x = m_tokenizer.last_number();
if(!m_tokenizer.next())
{
throw exception("parse_poly: Odd number of coordinates");
}
y = m_tokenizer.last_number();
m_path.line_to(x, y);
}
}
}
}
if(close_flag)
{
m_path.close_subpath();
}
m_path.end_path();
}
//-------------------------------------------------------------
void parser::parse_transform(const char* str)
{
while(*str)
{
if(islower(*str))
{
if(strncmp(str, "matrix", 6) == 0) str += parse_matrix(str); else
if(strncmp(str, "translate", 9) == 0) str += parse_translate(str); else
if(strncmp(str, "rotate", 6) == 0) str += parse_rotate(str); else
if(strncmp(str, "scale", 5) == 0) str += parse_scale(str); else
if(strncmp(str, "skewX", 5) == 0) str += parse_skew_x(str); else
if(strncmp(str, "skewY", 5) == 0) str += parse_skew_y(str); else
{
++str;
}
}
else
{
++str;
}
}
}
//-------------------------------------------------------------
static bool is_numeric(char c)
{
return strchr("0123456789+-.eE", c) != 0;
}
//-------------------------------------------------------------
static unsigned parse_transform_args(const char* str,
double* args,
unsigned max_na,
unsigned* na)
{
*na = 0;
const char* ptr = str;
while(*ptr && *ptr != '(') ++ptr;
if(*ptr == 0)
{
throw exception("parse_transform_args: Invalid syntax");
}
const char* end = ptr;
while(*end && *end != ')') ++end;
if(*end == 0)
{
throw exception("parse_transform_args: Invalid syntax");
}
while(ptr < end)
{
if(is_numeric(*ptr))
{
if(*na >= max_na)
{
throw exception("parse_transform_args: Too many arguments");
}
args[(*na)++] = atof(ptr);
while(ptr < end && is_numeric(*ptr)) ++ptr;
}
else
{
++ptr;
}
}
return unsigned(end - str);
}
//-------------------------------------------------------------
unsigned parser::parse_matrix(const char* str)
{
double args[6];
unsigned na = 0;
unsigned len = parse_transform_args(str, args, 6, &na);
if(na != 6)
{
throw exception("parse_matrix: Invalid number of arguments");
}
m_path.transform().premultiply(trans_affine(args[0], args[1], args[2], args[3], args[4], args[5]));
return len;
}
//-------------------------------------------------------------
unsigned parser::parse_translate(const char* str)
{
double args[2];
unsigned na = 0;
unsigned len = parse_transform_args(str, args, 2, &na);
if(na == 1) args[1] = 0.0;
m_path.transform().premultiply(trans_affine_translation(args[0], args[1]));
return len;
}
//-------------------------------------------------------------
unsigned parser::parse_rotate(const char* str)
{
double args[3];
unsigned na = 0;
unsigned len = parse_transform_args(str, args, 3, &na);
if(na == 1)
{
m_path.transform().premultiply(trans_affine_rotation(deg2rad(args[0])));
}
else if(na == 3)
{
trans_affine t = trans_affine_translation(-args[1], -args[2]);
t *= trans_affine_rotation(deg2rad(args[0]));
t *= trans_affine_translation(args[1], args[2]);
m_path.transform().premultiply(t);
}
else
{
throw exception("parse_rotate: Invalid number of arguments");
}
return len;
}
//-------------------------------------------------------------
unsigned parser::parse_scale(const char* str)
{
double args[2];
unsigned na = 0;
unsigned len = parse_transform_args(str, args, 2, &na);
if(na == 1) args[1] = args[0];
m_path.transform().premultiply(trans_affine_scaling(args[0], args[1]));
return len;
}
//-------------------------------------------------------------
unsigned parser::parse_skew_x(const char* str)
{
double arg;
unsigned na = 0;
unsigned len = parse_transform_args(str, &arg, 1, &na);
m_path.transform().premultiply(trans_affine_skewing(deg2rad(arg), 0.0));
return len;
}
//-------------------------------------------------------------
unsigned parser::parse_skew_y(const char* str)
{
double arg;
unsigned na = 0;
unsigned len = parse_transform_args(str, &arg, 1, &na);
m_path.transform().premultiply(trans_affine_skewing(0.0, deg2rad(arg)));
return len;
}
}
}
+85
View File
@@ -0,0 +1,85 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG parser.
//
//----------------------------------------------------------------------------
#ifndef AGG_SVG_PARSER_INCLUDED
#define AGG_SVG_PARSER_INCLUDED
#include "agg_svg_path_tokenizer.h"
#include "agg_svg_path_renderer.h"
namespace agg
{
namespace svg
{
class parser
{
enum buf_size_e { buf_size = BUFSIZ };
public:
~parser();
parser(path_renderer& path);
void parse(const char* fname);
const char* title() const { return m_title; }
private:
// XML event handlers
static void start_element(void* data, const char* el, const char** attr);
static void end_element(void* data, const char* el);
static void content(void* data, const char* s, int len);
void parse_attr(const char** attr);
void parse_path(const char** attr);
void parse_poly(const char** attr, bool close_flag);
void parse_rect(const char** attr);
void parse_line(const char** attr);
void parse_style(const char* str);
void parse_transform(const char* str);
unsigned parse_matrix(const char* str);
unsigned parse_translate(const char* str);
unsigned parse_rotate(const char* str);
unsigned parse_scale(const char* str);
unsigned parse_skew_x(const char* str);
unsigned parse_skew_y(const char* str);
bool parse_attr(const char* name, const char* value);
bool parse_name_value(const char* nv_start, const char* nv_end);
void copy_name(const char* start, const char* end);
void copy_value(const char* start, const char* end);
private:
path_renderer& m_path;
path_tokenizer m_tokenizer;
char* m_buf;
char* m_title;
unsigned m_title_len;
bool m_title_flag;
bool m_path_flag;
char* m_attr_name;
char* m_attr_value;
unsigned m_attr_name_len;
unsigned m_attr_value_len;
};
}
}
#endif
+366
View File
@@ -0,0 +1,366 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG path renderer.
//
//----------------------------------------------------------------------------
#include <stdio.h>
#include "agg_svg_path_renderer.h"
namespace agg
{
namespace svg
{
//------------------------------------------------------------------------
path_renderer::path_renderer() :
m_curved(m_storage),
m_curved_count(m_curved),
m_curved_stroked(m_curved_count),
m_curved_stroked_trans(m_curved_stroked, m_transform),
m_curved_trans(m_curved_count, m_transform),
m_curved_trans_contour(m_curved_trans)
{
m_curved_trans_contour.auto_detect_orientation(false);
}
//------------------------------------------------------------------------
void path_renderer::remove_all()
{
m_storage.remove_all();
m_attr_storage.remove_all();
m_attr_stack.remove_all();
m_transform.reset();
}
//------------------------------------------------------------------------
void path_renderer::begin_path()
{
push_attr();
unsigned idx = m_storage.start_new_path();
m_attr_storage.add(path_attributes(cur_attr(), idx));
}
//------------------------------------------------------------------------
void path_renderer::end_path()
{
if(m_attr_storage.size() == 0)
{
throw exception("end_path : The path was not begun");
}
path_attributes attr = cur_attr();
unsigned idx = m_attr_storage[m_attr_storage.size() - 1].index;
attr.index = idx;
m_attr_storage[m_attr_storage.size() - 1] = attr;
pop_attr();
}
//------------------------------------------------------------------------
void path_renderer::move_to(double x, double y, bool rel) // M, m
{
if(rel) m_storage.rel_to_abs(&x, &y);
m_storage.move_to(x, y);
}
//------------------------------------------------------------------------
void path_renderer::line_to(double x, double y, bool rel) // L, l
{
if(rel) m_storage.rel_to_abs(&x, &y);
m_storage.line_to(x, y);
}
//------------------------------------------------------------------------
void path_renderer::hline_to(double x, bool rel) // H, h
{
double x2 = 0.0;
double y2 = 0.0;
if(m_storage.total_vertices())
{
m_storage.vertex(m_storage.total_vertices() - 1, &x2, &y2);
if(rel) x += x2;
m_storage.line_to(x, y2);
}
}
//------------------------------------------------------------------------
void path_renderer::vline_to(double y, bool rel) // V, v
{
double x2 = 0.0;
double y2 = 0.0;
if(m_storage.total_vertices())
{
m_storage.vertex(m_storage.total_vertices() - 1, &x2, &y2);
if(rel) y += y2;
m_storage.line_to(x2, y);
}
}
//------------------------------------------------------------------------
void path_renderer::curve3(double x1, double y1, // Q, q
double x, double y, bool rel)
{
if(rel)
{
m_storage.rel_to_abs(&x1, &y1);
m_storage.rel_to_abs(&x, &y);
}
m_storage.curve3(x1, y1, x, y);
}
//------------------------------------------------------------------------
void path_renderer::curve3(double x, double y, bool rel) // T, t
{
// throw exception("curve3(x, y) : NOT IMPLEMENTED YET");
if(rel)
{
m_storage.curve3_rel(x, y);
} else
{
m_storage.curve3(x, y);
}
}
//------------------------------------------------------------------------
void path_renderer::curve4(double x1, double y1, // C, c
double x2, double y2,
double x, double y, bool rel)
{
if(rel)
{
m_storage.rel_to_abs(&x1, &y1);
m_storage.rel_to_abs(&x2, &y2);
m_storage.rel_to_abs(&x, &y);
}
m_storage.curve4(x1, y1, x2, y2, x, y);
}
//------------------------------------------------------------------------
void path_renderer::curve4(double x2, double y2, // S, s
double x, double y, bool rel)
{
//throw exception("curve4(x2, y2, x, y) : NOT IMPLEMENTED YET");
if(rel)
{
m_storage.curve4_rel(x2, y2, x, y);
} else
{
m_storage.curve4(x2, y2, x, y);
}
}
//------------------------------------------------------------------------
void path_renderer::close_subpath()
{
m_storage.end_poly(path_flags_close);
}
//------------------------------------------------------------------------
path_attributes& path_renderer::cur_attr()
{
if(m_attr_stack.size() == 0)
{
throw exception("cur_attr : Attribute stack is empty");
}
return m_attr_stack[m_attr_stack.size() - 1];
}
//------------------------------------------------------------------------
void path_renderer::push_attr()
{
m_attr_stack.add(m_attr_stack.size() ?
m_attr_stack[m_attr_stack.size() - 1] :
path_attributes());
}
//------------------------------------------------------------------------
void path_renderer::pop_attr()
{
if(m_attr_stack.size() == 0)
{
throw exception("pop_attr : Attribute stack is empty");
}
m_attr_stack.remove_last();
}
//------------------------------------------------------------------------
void path_renderer::fill(const rgba8& f)
{
path_attributes& attr = cur_attr();
attr.fill_color = f;
attr.fill_flag = true;
}
//------------------------------------------------------------------------
void path_renderer::stroke(const rgba8& s)
{
path_attributes& attr = cur_attr();
attr.stroke_color = s;
attr.stroke_flag = true;
}
//------------------------------------------------------------------------
void path_renderer::even_odd(bool flag)
{
cur_attr().even_odd_flag = flag;
}
//------------------------------------------------------------------------
void path_renderer::stroke_width(double w)
{
cur_attr().stroke_width = w;
}
//------------------------------------------------------------------------
void path_renderer::fill_none()
{
cur_attr().fill_flag = false;
}
//------------------------------------------------------------------------
void path_renderer::stroke_none()
{
cur_attr().stroke_flag = false;
}
//------------------------------------------------------------------------
void path_renderer::fill_opacity(double op)
{
cur_attr().fill_color.opacity(op);
}
//------------------------------------------------------------------------
void path_renderer::stroke_opacity(double op)
{
cur_attr().stroke_color.opacity(op);
}
//------------------------------------------------------------------------
void path_renderer::line_join(line_join_e join)
{
cur_attr().line_join = join;
}
//------------------------------------------------------------------------
void path_renderer::line_cap(line_cap_e cap)
{
cur_attr().line_cap = cap;
}
//------------------------------------------------------------------------
void path_renderer::miter_limit(double ml)
{
cur_attr().miter_limit = ml;
}
//------------------------------------------------------------------------
trans_affine& path_renderer::transform()
{
return cur_attr().transform;
}
//------------------------------------------------------------------------
void path_renderer::parse_path(path_tokenizer& tok)
{
while(tok.next())
{
double arg[10];
char cmd = tok.last_command();
unsigned i;
switch(cmd)
{
case 'M': case 'm':
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
move_to(arg[0], arg[1], cmd == 'm');
break;
case 'L': case 'l':
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
line_to(arg[0], arg[1], cmd == 'l');
break;
case 'V': case 'v':
vline_to(tok.last_number(), cmd == 'v');
break;
case 'H': case 'h':
hline_to(tok.last_number(), cmd == 'h');
break;
case 'Q': case 'q':
arg[0] = tok.last_number();
for(i = 1; i < 4; i++)
{
arg[i] = tok.next(cmd);
}
curve3(arg[0], arg[1], arg[2], arg[3], cmd == 'q');
break;
case 'T': case 't':
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
curve3(arg[0], arg[1], cmd == 't');
break;
case 'C': case 'c':
arg[0] = tok.last_number();
for(i = 1; i < 6; i++)
{
arg[i] = tok.next(cmd);
}
curve4(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], cmd == 'c');
break;
case 'S': case 's':
arg[0] = tok.last_number();
for(i = 1; i < 4; i++)
{
arg[i] = tok.next(cmd);
}
curve4(arg[0], arg[1], arg[2], arg[3], cmd == 's');
break;
case 'A': case 'a':
arg[0] = tok.last_number();
for (i = 1; i < 7; i++)
{
arg[i] = tok.next(cmd);
}
// curve3(arg[0], arg[1], arg[2], arg[3], cmd == 'q');
// throw exception("parse_path: Command A: NOT IMPLEMENTED YET");
break;
case 'Z': case 'z':
close_subpath();
break;
default:
{
char buf[100];
sprintf(buf, "parse_path: Invalid Command %c", cmd);
throw exception(buf);
}
}
}
}
}
}
+321
View File
@@ -0,0 +1,321 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG path renderer.
//
//----------------------------------------------------------------------------
#ifndef AGG_SVG_PATH_RENDERER_INCLUDED
#define AGG_SVG_PATH_RENDERER_INCLUDED
#include <agg/agg_path_storage.h>
#include <agg/agg_conv_transform.h>
#include <agg/agg_conv_stroke.h>
#include <agg/agg_conv_contour.h>
#include <agg/agg_conv_curve.h>
#include <agg/agg_color_rgba.h>
#include <agg/agg_renderer_scanline.h>
#include <agg/agg_bounding_rect.h>
#include <agg/agg_rasterizer_scanline_aa.h>
#include "agg_svg_path_tokenizer.h"
namespace agg
{
namespace svg
{
template<class VertexSource> class conv_count
{
public:
conv_count(VertexSource& vs) : m_source(&vs), m_count(0) {}
void count(unsigned n) { m_count = n; }
unsigned count() const { return m_count; }
void rewind(unsigned path_id) { m_source->rewind(path_id); }
unsigned vertex(double* x, double* y)
{
++m_count;
return m_source->vertex(x, y);
}
private:
VertexSource* m_source;
unsigned m_count;
};
//============================================================================
// Basic path attributes
struct path_attributes
{
unsigned index;
rgba8 fill_color;
rgba8 stroke_color;
bool fill_flag;
bool stroke_flag;
bool even_odd_flag;
line_join_e line_join;
line_cap_e line_cap;
double miter_limit;
double stroke_width;
trans_affine transform;
// Empty constructor
path_attributes() :
index(0),
fill_color(rgba(0,0,0)),
stroke_color(rgba(0,0,0)),
fill_flag(true),
stroke_flag(false),
even_odd_flag(false),
line_join(miter_join),
line_cap(butt_cap),
miter_limit(4.0),
stroke_width(1.0),
transform()
{
}
// Copy constructor
path_attributes(const path_attributes& attr) :
index(attr.index),
fill_color(attr.fill_color),
stroke_color(attr.stroke_color),
fill_flag(attr.fill_flag),
stroke_flag(attr.stroke_flag),
even_odd_flag(attr.even_odd_flag),
line_join(attr.line_join),
line_cap(attr.line_cap),
miter_limit(attr.miter_limit),
stroke_width(attr.stroke_width),
transform(attr.transform)
{
}
// Copy constructor with new index value
path_attributes(const path_attributes& attr, unsigned idx) :
index(idx),
fill_color(attr.fill_color),
stroke_color(attr.stroke_color),
fill_flag(attr.fill_flag),
stroke_flag(attr.stroke_flag),
even_odd_flag(attr.even_odd_flag),
line_join(attr.line_join),
line_cap(attr.line_cap),
miter_limit(attr.miter_limit),
stroke_width(attr.stroke_width),
transform(attr.transform)
{
}
};
//============================================================================
// Path container and renderer.
class path_renderer
{
public:
typedef pod_bvector<path_attributes> attr_storage;
typedef conv_curve<path_storage> curved;
typedef conv_count<curved> curved_count;
typedef conv_stroke<curved_count> curved_stroked;
typedef conv_transform<curved_stroked> curved_stroked_trans;
typedef conv_transform<curved_count> curved_trans;
typedef conv_contour<curved_trans> curved_trans_contour;
path_renderer();
void remove_all();
// Use these functions as follows:
// begin_path() when the XML tag <path> comes ("start_element" handler)
// parse_path() on "d=" tag attribute
// end_path() when parsing of the entire tag is done.
void begin_path();
void parse_path(path_tokenizer& tok);
void end_path();
// The following functions are essentially a "reflection" of
// the respective SVG path commands.
void move_to(double x, double y, bool rel=false); // M, m
void line_to(double x, double y, bool rel=false); // L, l
void hline_to(double x, bool rel=false); // H, h
void vline_to(double y, bool rel=false); // V, v
void curve3(double x1, double y1, // Q, q
double x, double y, bool rel=false);
void curve3(double x, double y, bool rel=false); // T, t
void curve4(double x1, double y1, // C, c
double x2, double y2,
double x, double y, bool rel=false);
void curve4(double x2, double y2, // S, s
double x, double y, bool rel=false);
void close_subpath(); // Z, z
// template<class VertexSource>
// void add_path(VertexSource& vs,
// unsigned path_id = 0,
// bool solid_path = true)
// {
// m_storage.add_path(vs, path_id, solid_path);
// }
unsigned vertex_count() const { return m_curved_count.count(); }
// Call these functions on <g> tag (start_element, end_element respectively)
void push_attr();
void pop_attr();
// Attribute setting functions.
void fill(const rgba8& f);
void stroke(const rgba8& s);
void even_odd(bool flag);
void stroke_width(double w);
void fill_none();
void stroke_none();
void fill_opacity(double op);
void stroke_opacity(double op);
void line_join(line_join_e join);
void line_cap(line_cap_e cap);
void miter_limit(double ml);
trans_affine& transform();
// Make all polygons CCW-oriented
void arrange_orientations()
{
m_storage.arrange_orientations_all_paths(path_flags_ccw);
}
// Expand all polygons
void expand(double value)
{
m_curved_trans_contour.width(value);
}
unsigned operator [](unsigned idx)
{
m_transform = m_attr_storage[idx].transform;
return m_attr_storage[idx].index;
}
void bounding_rect(double* x1, double* y1, double* x2, double* y2)
{
agg::conv_transform<agg::path_storage> trans(m_storage, m_transform);
agg::bounding_rect(trans, *this, 0, m_attr_storage.size(), x1, y1, x2, y2);
}
// Rendering. One can specify two additional parameters:
// trans_affine and opacity. They can be used to transform the whole
// image and/or to make it translucent.
template<class Rasterizer, class Scanline, class Renderer>
void render(Rasterizer& ras,
Scanline& sl,
Renderer& ren,
const trans_affine& mtx,
const rect_i& cb,
double opacity=1.0)
{
unsigned i;
ras.clip_box(cb.x1, cb.y1, cb.x2, cb.y2);
m_curved_count.count(0);
for(i = 0; i < m_attr_storage.size(); i++)
{
const path_attributes& attr = m_attr_storage[i];
m_transform = attr.transform;
m_transform *= mtx;
double scl = m_transform.scale();
//m_curved.approximation_method(curve_inc);
m_curved.approximation_scale(scl);
m_curved.angle_tolerance(0.0);
rgba8 color;
if(attr.fill_flag)
{
ras.reset();
ras.filling_rule(attr.even_odd_flag ? fill_even_odd : fill_non_zero);
if(fabs(m_curved_trans_contour.width()) < 0.0001)
{
ras.add_path(m_curved_trans, attr.index);
}
else
{
m_curved_trans_contour.miter_limit(attr.miter_limit);
ras.add_path(m_curved_trans_contour, attr.index);
}
color = attr.fill_color;
color.opacity(color.opacity() * opacity);
ren.color(color);
agg::render_scanlines(ras, sl, ren);
}
if(attr.stroke_flag)
{
m_curved_stroked.width(attr.stroke_width);
//m_curved_stroked.line_join((attr.line_join == miter_join) ? miter_join_round : attr.line_join);
m_curved_stroked.line_join(attr.line_join);
m_curved_stroked.line_cap(attr.line_cap);
m_curved_stroked.miter_limit(attr.miter_limit);
m_curved_stroked.inner_join(inner_round);
m_curved_stroked.approximation_scale(scl);
// If the *visual* line width is considerable we
// turn on processing of curve cusps.
//---------------------
if(attr.stroke_width * scl > 1.0)
{
m_curved.angle_tolerance(0.2);
}
ras.reset();
ras.filling_rule(fill_non_zero);
ras.add_path(m_curved_stroked_trans, attr.index);
color = attr.stroke_color;
color.opacity(color.opacity() * opacity);
ren.color(color);
agg::render_scanlines(ras, sl, ren);
}
}
}
private:
path_attributes& cur_attr();
path_storage m_storage;
attr_storage m_attr_storage;
attr_storage m_attr_stack;
trans_affine m_transform;
curved m_curved;
curved_count m_curved_count;
curved_stroked m_curved_stroked;
curved_stroked_trans m_curved_stroked_trans;
curved_trans m_curved_trans;
curved_trans_contour m_curved_trans_contour;
};
}
}
#endif
+151
View File
@@ -0,0 +1,151 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG path tokenizer.
//
//----------------------------------------------------------------------------
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "agg_svg_exception.h"
#include "agg_svg_path_tokenizer.h"
namespace agg
{
namespace svg
{
//------------------------------------------------------------------------
const char path_tokenizer::s_commands[] = "+-MmZzLlHhVvCcSsQqTtAaFfPp";
const char path_tokenizer::s_numeric[] = ".Ee0123456789";
const char path_tokenizer::s_separators[] = " ,\t\n\r";
//------------------------------------------------------------------------
path_tokenizer::path_tokenizer()
: m_path(0), m_last_command(0), m_last_number(0.0)
{
init_char_mask(m_commands_mask, s_commands);
init_char_mask(m_numeric_mask, s_numeric);
init_char_mask(m_separators_mask, s_separators);
}
//------------------------------------------------------------------------
void path_tokenizer::set_path_str(const char* str)
{
m_path = str;
m_last_command = 0;
m_last_number = 0.0;
}
//------------------------------------------------------------------------
void path_tokenizer::init_char_mask(char* mask, const char* char_set)
{
memset(mask, 0, 256/8);
while(*char_set)
{
unsigned c = unsigned(*char_set++) & 0xFF;
mask[c >> 3] |= 1 << (c & 7);
}
}
//------------------------------------------------------------------------
bool path_tokenizer::next()
{
if(m_path == 0) return false;
// Skip all white spaces and other garbage
while(*m_path && !is_command(*m_path) && !is_numeric(*m_path))
{
if(!is_separator(*m_path))
{
char buf[100];
sprintf(buf, "path_tokenizer::next : Invalid Character %c", *m_path);
throw exception(buf);
}
m_path++;
}
if(*m_path == 0) return false;
if(is_command(*m_path))
{
// Check if the command is a numeric sign character
if(*m_path == '-' || *m_path == '+')
{
return parse_number();
}
m_last_command = *m_path++;
while(*m_path && is_separator(*m_path)) m_path++;
if(*m_path == 0) return true;
}
return parse_number();
}
//------------------------------------------------------------------------
double path_tokenizer::next(char cmd)
{
if(!next()) throw exception("parse_path: Unexpected end of path");
if(last_command() != cmd)
{
char buf[100];
sprintf(buf, "parse_path: Command %c: bad or missing parameters", cmd);
throw exception(buf);
}
return last_number();
}
//------------------------------------------------------------------------
bool path_tokenizer::parse_number()
{
char buf[256]; // Should be enough for any number
char* buf_ptr = buf;
// Copy all sign characters
while(buf_ptr < buf+255 && *m_path == '-' || *m_path == '+')
{
*buf_ptr++ = *m_path++;
}
// Copy all numeric characters
bool dot_seen = false;
while(buf_ptr < buf+255 && is_numeric(*m_path))
{
char c = *m_path;
if (c == '.') {
if (dot_seen)
break;
dot_seen = true;
}
*buf_ptr++ = *m_path++;
}
*buf_ptr = 0;
m_last_number = atof(buf);
return true;
}
} //namespace svg
} //namespace agg
+114
View File
@@ -0,0 +1,114 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//----------------------------------------------------------------------------
//
// SVG path tokenizer.
//
//----------------------------------------------------------------------------
#ifndef AGG_SVG_PATH_TOKENIZER_INCLUDED
#define AGG_SVG_PATH_TOKENIZER_INCLUDED
#include "agg_svg_exception.h"
namespace agg
{
namespace svg
{
// SVG path tokenizer.
// Example:
//
// agg::svg::path_tokenizer tok;
//
// tok.set_str("M-122.304 84.285L-122.304 84.285 122.203 86.179 ");
// while(tok.next())
// {
// printf("command='%c' number=%f\n",
// tok.last_command(),
// tok.last_number());
// }
//
// The tokenizer does all the routine job of parsing the SVG paths.
// It doesn't recognize any graphical primitives, it even doesn't know
// anything about pairs of coordinates (X,Y). The purpose of this class
// is to tokenize the numeric values and commands. SVG paths can
// have single numeric values for Horizontal or Vertical line_to commands
// as well as more than two coordinates (4 or 6) for Bezier curves
// depending on the semantics of the command.
// The behaviour is as follows:
//
// Each call to next() returns true if there's new command or new numeric
// value or false when the path ends. How to interpret the result
// depends on the sematics of the command. For example, command "C"
// (cubic Bezier curve) implies 6 floating point numbers preceded by this
// command. If the command assumes no arguments (like z or Z) the
// the last_number() values won't change, that is, last_number() always
// returns the last recognized numeric value, so does last_command().
//===============================================================
class path_tokenizer
{
public:
path_tokenizer();
void set_path_str(const char* str);
bool next();
double next(char cmd);
char last_command() const { return m_last_command; }
double last_number() const { return m_last_number; }
private:
static void init_char_mask(char* mask, const char* char_set);
bool contains(const char* mask, unsigned c) const
{
return (mask[(c >> 3) & (256/8-1)] & (1 << (c & 7))) != 0;
}
bool is_command(unsigned c) const
{
return contains(m_commands_mask, c);
}
bool is_numeric(unsigned c) const
{
return contains(m_numeric_mask, c);
}
bool is_separator(unsigned c) const
{
return contains(m_separators_mask, c);
}
bool parse_number();
char m_separators_mask[256/8];
char m_commands_mask[256/8];
char m_numeric_mask[256/8];
const char* m_path;
double m_last_number;
char m_last_command;
static const char s_commands[];
static const char s_numeric[];
static const char s_separators[];
};
} //namespace svg
} //namespace agg
#endif
+200
View File
@@ -0,0 +1,200 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://antigrain.com
//
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
//----------------------------------------------------------------------------
#include <agg/agg_trans_affine.h>
namespace agg
{
//------------------------------------------------------------------------
const trans_affine& trans_affine::parl_to_parl(const double* src,
const double* dst)
{
sx = src[2] - src[0];
shy = src[3] - src[1];
shx = src[4] - src[0];
sy = src[5] - src[1];
tx = src[0];
ty = src[1];
invert();
multiply(trans_affine(dst[2] - dst[0], dst[3] - dst[1],
dst[4] - dst[0], dst[5] - dst[1],
dst[0], dst[1]));
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::rect_to_parl(double x1, double y1,
double x2, double y2,
const double* parl)
{
double src[6];
src[0] = x1; src[1] = y1;
src[2] = x2; src[3] = y1;
src[4] = x2; src[5] = y2;
parl_to_parl(src, parl);
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::parl_to_rect(const double* parl,
double x1, double y1,
double x2, double y2)
{
double dst[6];
dst[0] = x1; dst[1] = y1;
dst[2] = x2; dst[3] = y1;
dst[4] = x2; dst[5] = y2;
parl_to_parl(parl, dst);
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::multiply(const trans_affine& m)
{
double t0 = sx * m.sx + shy * m.shx;
double t2 = shx * m.sx + sy * m.shx;
double t4 = tx * m.sx + ty * m.shx + m.tx;
shy = sx * m.shy + shy * m.sy;
sy = shx * m.shy + sy * m.sy;
ty = tx * m.shy + ty * m.sy + m.ty;
sx = t0;
shx = t2;
tx = t4;
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::invert()
{
double d = determinant_reciprocal();
double t0 = sy * d;
sy = sx * d;
shy = -shy * d;
shx = -shx * d;
double t4 = -tx * t0 - ty * shx;
ty = -tx * shy - ty * sy;
sx = t0;
tx = t4;
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::flip_x()
{
sx = -sx;
shy = -shy;
tx = -tx;
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::flip_y()
{
shx = -shx;
sy = -sy;
ty = -ty;
return *this;
}
//------------------------------------------------------------------------
const trans_affine& trans_affine::reset()
{
sx = sy = 1.0;
shy = shx = tx = ty = 0.0;
return *this;
}
//------------------------------------------------------------------------
bool trans_affine::is_identity(double epsilon) const
{
return is_equal_eps(sx, 1.0, epsilon) &&
is_equal_eps(shy, 0.0, epsilon) &&
is_equal_eps(shx, 0.0, epsilon) &&
is_equal_eps(sy, 1.0, epsilon) &&
is_equal_eps(tx, 0.0, epsilon) &&
is_equal_eps(ty, 0.0, epsilon);
}
//------------------------------------------------------------------------
bool trans_affine::is_valid(double epsilon) const
{
return fabs(sx) > epsilon && fabs(sy) > epsilon;
}
//------------------------------------------------------------------------
bool trans_affine::is_equal(const trans_affine& m, double epsilon) const
{
return is_equal_eps(sx, m.sx, epsilon) &&
is_equal_eps(shy, m.shy, epsilon) &&
is_equal_eps(shx, m.shx, epsilon) &&
is_equal_eps(sy, m.sy, epsilon) &&
is_equal_eps(tx, m.tx, epsilon) &&
is_equal_eps(ty, m.ty, epsilon);
}
//------------------------------------------------------------------------
double trans_affine::rotation() const
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 0.0;
transform(&x1, &y1);
transform(&x2, &y2);
return atan2(y2-y1, x2-x1);
}
//------------------------------------------------------------------------
void trans_affine::translation(double* dx, double* dy) const
{
*dx = tx;
*dy = ty;
}
//------------------------------------------------------------------------
void trans_affine::scaling(double* x, double* y) const
{
double x1 = 0.0;
double y1 = 0.0;
double x2 = 1.0;
double y2 = 1.0;
trans_affine t(*this);
t *= trans_affine_rotation(-rotation());
t.transform(&x1, &y1);
t.transform(&x2, &y2);
*x = x2 - x1;
*y = y2 - y1;
}
}
+170
View File
@@ -0,0 +1,170 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://antigrain.com
//
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
//----------------------------------------------------------------------------
#include <math.h>
#include <agg/agg_vcgen_contour.h>
namespace agg
{
//------------------------------------------------------------------------
vcgen_contour::vcgen_contour() :
m_stroker(),
m_width(1),
m_src_vertices(),
m_out_vertices(),
m_status(initial),
m_src_vertex(0),
m_closed(0),
m_orientation(0),
m_auto_detect(false)
{
}
//------------------------------------------------------------------------
void vcgen_contour::remove_all()
{
m_src_vertices.remove_all();
m_closed = 0;
m_orientation = 0;
m_status = initial;
}
//------------------------------------------------------------------------
void vcgen_contour::add_vertex(double x, double y, unsigned cmd)
{
m_status = initial;
if(is_move_to(cmd))
{
m_src_vertices.modify_last(vertex_dist(x, y));
}
else
{
if(is_vertex(cmd))
{
m_src_vertices.add(vertex_dist(x, y));
}
else
{
if(is_end_poly(cmd))
{
m_closed = get_close_flag(cmd);
if(m_orientation == path_flags_none)
{
m_orientation = get_orientation(cmd);
}
}
}
}
}
//------------------------------------------------------------------------
void vcgen_contour::rewind(unsigned)
{
if(m_status == initial)
{
m_src_vertices.close(true);
if(m_auto_detect)
{
if(!is_oriented(m_orientation))
{
m_orientation = (calc_polygon_area(m_src_vertices) > 0.0) ?
path_flags_ccw :
path_flags_cw;
}
}
if(is_oriented(m_orientation))
{
m_stroker.width(is_ccw(m_orientation) ? m_width : -m_width);
}
}
m_status = ready;
m_src_vertex = 0;
}
//------------------------------------------------------------------------
unsigned vcgen_contour::vertex(double* x, double* y)
{
unsigned cmd = path_cmd_line_to;
while(!is_stop(cmd))
{
switch(m_status)
{
case initial:
rewind(0);
case ready:
if(m_src_vertices.size() < 2 + unsigned(m_closed != 0))
{
cmd = path_cmd_stop;
break;
}
m_status = outline;
cmd = path_cmd_move_to;
m_src_vertex = 0;
m_out_vertex = 0;
case outline:
if(m_src_vertex >= m_src_vertices.size())
{
m_status = end_poly;
break;
}
m_stroker.calc_join(m_out_vertices,
m_src_vertices.prev(m_src_vertex),
m_src_vertices.curr(m_src_vertex),
m_src_vertices.next(m_src_vertex),
m_src_vertices.prev(m_src_vertex).dist,
m_src_vertices.curr(m_src_vertex).dist);
++m_src_vertex;
m_status = out_vertices;
m_out_vertex = 0;
case out_vertices:
if(m_out_vertex >= m_out_vertices.size())
{
m_status = outline;
}
else
{
const point_d& c = m_out_vertices[m_out_vertex++];
*x = c.x;
*y = c.y;
return cmd;
}
break;
case end_poly:
if(!m_closed) return path_cmd_stop;
m_status = stop;
return path_cmd_end_poly | path_flags_close | path_flags_ccw;
case stop:
return path_cmd_stop;
}
}
return cmd;
}
}
+219
View File
@@ -0,0 +1,219 @@
//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://antigrain.com
//
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
//----------------------------------------------------------------------------
#include <math.h>
#include <agg/agg_vcgen_stroke.h>
#include <agg/agg_shorten_path.h>
namespace agg
{
//------------------------------------------------------------------------
vcgen_stroke::vcgen_stroke() :
m_stroker(),
m_src_vertices(),
m_out_vertices(),
m_shorten(0.0),
m_closed(0),
m_status(initial),
m_src_vertex(0),
m_out_vertex(0)
{
}
//------------------------------------------------------------------------
void vcgen_stroke::remove_all()
{
m_src_vertices.remove_all();
m_closed = 0;
m_status = initial;
}
//------------------------------------------------------------------------
void vcgen_stroke::add_vertex(double x, double y, unsigned cmd)
{
m_status = initial;
if(is_move_to(cmd))
{
m_src_vertices.modify_last(vertex_dist(x, y));
}
else
{
if(is_vertex(cmd))
{
m_src_vertices.add(vertex_dist(x, y));
}
else
{
m_closed = get_close_flag(cmd);
}
}
}
//------------------------------------------------------------------------
void vcgen_stroke::rewind(unsigned)
{
if(m_status == initial)
{
m_src_vertices.close(m_closed != 0);
shorten_path(m_src_vertices, m_shorten, m_closed);
if(m_src_vertices.size() < 3) m_closed = 0;
}
m_status = ready;
m_src_vertex = 0;
m_out_vertex = 0;
}
//------------------------------------------------------------------------
unsigned vcgen_stroke::vertex(double* x, double* y)
{
unsigned cmd = path_cmd_line_to;
while(!is_stop(cmd))
{
switch(m_status)
{
case initial:
rewind(0);
case ready:
if(m_src_vertices.size() < 2 + unsigned(m_closed != 0))
{
cmd = path_cmd_stop;
break;
}
m_status = m_closed ? outline1 : cap1;
cmd = path_cmd_move_to;
m_src_vertex = 0;
m_out_vertex = 0;
break;
case cap1:
m_stroker.calc_cap(m_out_vertices,
m_src_vertices[0],
m_src_vertices[1],
m_src_vertices[0].dist);
m_src_vertex = 1;
m_prev_status = outline1;
m_status = out_vertices;
m_out_vertex = 0;
break;
case cap2:
m_stroker.calc_cap(m_out_vertices,
m_src_vertices[m_src_vertices.size() - 1],
m_src_vertices[m_src_vertices.size() - 2],
m_src_vertices[m_src_vertices.size() - 2].dist);
m_prev_status = outline2;
m_status = out_vertices;
m_out_vertex = 0;
break;
case outline1:
if(m_closed)
{
if(m_src_vertex >= m_src_vertices.size())
{
m_prev_status = close_first;
m_status = end_poly1;
break;
}
}
else
{
if(m_src_vertex >= m_src_vertices.size() - 1)
{
m_status = cap2;
break;
}
}
m_stroker.calc_join(m_out_vertices,
m_src_vertices.prev(m_src_vertex),
m_src_vertices.curr(m_src_vertex),
m_src_vertices.next(m_src_vertex),
m_src_vertices.prev(m_src_vertex).dist,
m_src_vertices.curr(m_src_vertex).dist);
++m_src_vertex;
m_prev_status = m_status;
m_status = out_vertices;
m_out_vertex = 0;
break;
case close_first:
m_status = outline2;
cmd = path_cmd_move_to;
case outline2:
if(m_src_vertex <= unsigned(m_closed == 0))
{
m_status = end_poly2;
m_prev_status = stop;
break;
}
--m_src_vertex;
m_stroker.calc_join(m_out_vertices,
m_src_vertices.next(m_src_vertex),
m_src_vertices.curr(m_src_vertex),
m_src_vertices.prev(m_src_vertex),
m_src_vertices.curr(m_src_vertex).dist,
m_src_vertices.prev(m_src_vertex).dist);
m_prev_status = m_status;
m_status = out_vertices;
m_out_vertex = 0;
break;
case out_vertices:
if(m_out_vertex >= m_out_vertices.size())
{
m_status = m_prev_status;
}
else
{
const point_d& c = m_out_vertices[m_out_vertex++];
*x = c.x;
*y = c.y;
return cmd;
}
break;
case end_poly1:
m_status = m_prev_status;
return path_cmd_end_poly | path_flags_close | path_flags_ccw;
case end_poly2:
m_status = m_prev_status;
return path_cmd_end_poly | path_flags_close | path_flags_cw;
case stop:
cmd = path_cmd_stop;
break;
}
}
return cmd;
}
}