//---------------------------------------------------------------------------- // Anti-Grain Geometry - Version 2.3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) // Copyright (c) 2008 Rene Rebe [arc parser code] // Copyright (c) 2017 John Horigan [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. // //---------------------------------------------------------------------------- #ifndef AGG_SVG_PATH_RENDERER_INCLUDED #define AGG_SVG_PATH_RENDERER_INCLUDED #include "agg_path_storage.h" #include "agg_conv_transform.h" #include "agg_conv_stroke.h" #include "agg_conv_contour.h" #include "agg_conv_curve.h" #include "agg_color_rgba.h" #include "agg_renderer_scanline.h" #include "agg_bounding_rect.h" #include "agg_rasterizer_scanline_aa.h" #include "agg_svg_path_tokenizer.h" namespace agg { namespace svg { template 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 attr_storage; typedef conv_curve curved; typedef conv_count curved_count; typedef conv_stroke curved_stroked; typedef conv_transform curved_stroked_trans; typedef conv_transform curved_trans; typedef conv_contour curved_trans_contour; path_renderer(); void remove_all(); // Use these functions as follows: // begin_path() when the XML tag 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 arc(double rx, double ry, double angle, // A, a bool large_arc_flag, bool sweep_flag, double x, double y, bool rel=false); void close_subpath(); // Z, z void align_subpath(unsigned start_idx); // template // 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 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 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 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