agg/examples/svg_viewer/agg_svg_path_renderer.cpp

404 lines
13 KiB
C++

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
// Copyright (c) 2008 Rene Rebe <rene@exactcode.de> [arc parser code]
// Copyright (c) 2017 John Horigan <john@glyphic.com> [align_subpath()]
//
// 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 <cmath>
#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
{
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)
{
if(rel)
{
m_storage.curve4_rel(x2, y2, x, y);
} else
{
m_storage.curve4(x2, y2, x, y);
}
}
//------------------------------------------------------------------------
void path_renderer::arc(double rx, double ry,
double angle,
bool large_arc_flag,
bool sweep_flag,
double x, double y, bool rel)
{
angle = deg2rad (angle);
if(rel)
{
m_storage.arc_rel(rx, ry, angle, large_arc_flag, sweep_flag, x, y);
} else
{
m_storage.arc_to(rx, ry, angle, large_arc_flag, sweep_flag, x, y);
}
}
//------------------------------------------------------------------------
void path_renderer::close_subpath()
{
m_storage.end_poly(path_flags_close);
}
//------------------------------------------------------------------------
// If the end points of a subpath are very, very close then make them
// exactly equal so that AGG is not confused.
//------------------------------------------------------------------------
void path_renderer::align_subpath(unsigned start_idx)
{
m_storage.align_path(start_idx);
}
//------------------------------------------------------------------------
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)
{
unsigned move_idx = 0;
while(tok.next())
{
double arg[10];
char cmd = tok.last_command();
if (m_storage.total_vertices() == 0 && !(cmd == 'M' || cmd == 'm'))
{
throw exception("path element data attributes must begin with a 'move to'");
}
unsigned i;
switch(cmd)
{
case 'M': case 'm':
align_subpath(move_idx);
arg[0] = tok.last_number();
arg[1] = tok.next(cmd);
move_to(arg[0], arg[1], cmd == 'm');
move_idx = m_storage.total_vertices() - 1;
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);
}
arc(arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6],
cmd == 'a');
break;
case 'Z': case 'z':
align_subpath(move_idx);
close_subpath();
break;
default:
{
char buf[100];
sprintf(buf, "parse_path: Invalid Command %c", cmd);
throw exception(buf);
}
}
}
align_subpath(move_idx);
m_storage.start_new_path();
}
}
}