Created
November 22, 2012 03:50
-
-
Save springmeyer/4129366 to your computer and use it in GitHub Desktop.
svg patch against agg from http://pastebin.com/CtysNL14
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: CMakeLists.txt | |
=================================================================== | |
--- CMakeLists.txt (revision 50) | |
+++ CMakeLists.txt (working copy) | |
@@ -302,6 +302,8 @@ | |
./svg_viewer/agg_svg_path_renderer.h | |
./svg_viewer/agg_svg_path_tokenizer.cpp | |
./svg_viewer/agg_svg_path_tokenizer.h | |
+ ./svg_viewer/agg_svg_gradient.cpp | |
+ ./svg_viewer/agg_svg_gradient.h | |
) | |
ENDIF ( agg_USE_EXPAT ) | |
Index: svg_viewer/agg_svg_gradient.cpp | |
=================================================================== | |
--- svg_viewer/agg_svg_gradient.cpp (revision 0) | |
+++ svg_viewer/agg_svg_gradient.cpp (working copy) | |
@@ -0,0 +1,197 @@ | |
+//---------------------------------------------------------------------------- | |
+// 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 | |
+//---------------------------------------------------------------------------- | |
+// | |
+// Gunnar Roth: add support for linear and radial gradients(support xlink attr), | |
+// shape gradient opaqueness, rounded rects, circles,ellipses. support a command (arc) in pathes. | |
+// set new origin correctly to last postion on z command in a path( was set to 0,0 before). | |
+// enable parsing of colors written as rgb() | |
+// some code was inspired by code from Haiku OS | |
+/* | |
+* Copyright 2006-2007, Haiku. All rights reserved. | |
+* Distributed under the terms of the MIT License. | |
+* | |
+* Authors: | |
+* Stephan Aßmus <superstippi@gmx.de> | |
+*/ | |
+// | |
+ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+ | |
+ | |
+ | |
+#include "agg_svg_gradient.h" | |
+#include <vector> | |
+#include "agg_trans_affine.h" | |
+#include "agg_gradient_lut.h" | |
+ | |
+ | |
+ | |
+ | |
+ | |
+ | |
+namespace agg { | |
+ namespace svg { | |
+ | |
+ | |
+ | |
+ // constructor | |
+ gradient::gradient() | |
+ : | |
+ m_type(GRADIENT_LINEAR), | |
+ m_id(""), | |
+ m_colcnt(0), | |
+ m_opaque(1.0) | |
+ { | |
+ } | |
+ | |
+ // destructor | |
+ gradient::~gradient() | |
+ { | |
+ | |
+ } | |
+ | |
+ // SetID | |
+ void | |
+ gradient::set_id(const char* id) | |
+ { | |
+ m_id = id; | |
+ } | |
+ | |
+ // ID | |
+ const char* | |
+ gradient::id() const | |
+ { | |
+ return m_id.c_str(); | |
+ } | |
+ | |
+ // AddStop | |
+ void | |
+ gradient::add_stop(double offset, rgba8 color) | |
+ { | |
+ m_colcnt++; | |
+ m_gradient_lut.add_color(offset,color); | |
+ } | |
+ | |
+ // SetTransformation | |
+ void | |
+ gradient::set_transformation(const trans_affine& transform) | |
+ { | |
+ m_transform.multiply(transform); | |
+ } | |
+ // constructor | |
+ linear_gradient::linear_gradient() | |
+ : gradient() | |
+ { | |
+ } | |
+ | |
+ // destructor | |
+ linear_gradient::~linear_gradient() | |
+ { | |
+ } | |
+ | |
+ // MakeGradient | |
+ | |
+ void linear_gradient::realize() | |
+ { | |
+ | |
+ set_type(GRADIENT_LINEAR); | |
+ // setup the gradient transform | |
+ point_d start(-lut_range, -lut_range); | |
+ point_d end(lut_range, -lut_range); | |
+ std::string coordinate; | |
+ if (find_string("x1", &coordinate) ) | |
+ start.x = atof(coordinate.c_str()); | |
+ if (find_string("y1", &coordinate) ) | |
+ start.y = atof(coordinate.c_str()); | |
+ if (find_string("x2", &coordinate) ) | |
+ end.x = atof(coordinate.c_str()); | |
+ if (find_string("y2", &coordinate) ) | |
+ end.y = atof(coordinate.c_str()); | |
+ | |
+ | |
+ // the transformed parallelogram | |
+ double parl[6]; | |
+ parl[0] = start.x; | |
+ parl[1] = start.y; | |
+ parl[2] = end.x; | |
+ parl[3] = end.y; | |
+ parl[4] = end.x - (end.y - start.y); | |
+ parl[5] = end.y + (end.x - start.x); | |
+ trans_affine transform(-lut_range, -lut_range, lut_range, lut_range, parl); | |
+ m_transform.premultiply(transform); | |
+ m_gradient_lut.build_lut(1.0); | |
+ } | |
+ | |
+ // constructor | |
+ radial_gradient::radial_gradient() | |
+ : gradient() | |
+ { | |
+ } | |
+ | |
+ // destructor | |
+ radial_gradient::~radial_gradient() | |
+ { | |
+ } | |
+ | |
+ // MakeGradient | |
+ | |
+ void radial_gradient::realize() | |
+ { | |
+ //printf("SVGRadialGradient::MakeGradient()\n"); | |
+ // TODO: handle userSpaceOnUse/objectBoundingBox | |
+ | |
+ set_type(GRADIENT_CIRCULAR); | |
+ | |
+ double cx = 0.0; | |
+ double cy = 0.0; | |
+ double r = 100.0; | |
+ double fx = 0.0; | |
+ double fy = 0.0; | |
+ | |
+ std::string value; | |
+ if (find_string("cx", &value) ) | |
+ cx = atof(value.c_str()); | |
+ if (find_string("cy", &value) ) | |
+ cy = atof(value.c_str()); | |
+ if (find_string("r", &value) ) | |
+ r = atof(value.c_str()); | |
+/* | |
+ if (FindString("fx", &value) ) | |
+ { | |
+ fx = atof(value.c_str()); | |
+ if (FindString("fy", &value) ) | |
+ fy = atof(value.c_str()); | |
+ SetType(GRADIENT_CIRCULAR_FOCAL); | |
+ } | |
+*/ | |
+ | |
+ | |
+ // the transformed parallelogram | |
+ double parl[6]; | |
+ parl[0] = cx - r; | |
+ parl[1] = cy - r; | |
+ parl[2] = cx + r; | |
+ parl[3] = cy - r; | |
+ parl[4] = cx + r; | |
+ parl[5] = cy + r; | |
+ | |
+ trans_affine transform(-lut_range, -lut_range, lut_range, lut_range, parl); | |
+ m_transform.premultiply(transform); | |
+ m_gradient_lut.build_lut(1.0); | |
+ } | |
+ | |
+ } // namespace svg | |
+} // namespace agg | |
\ No newline at end of file | |
Index: svg_viewer/agg_svg_gradient.h | |
=================================================================== | |
--- svg_viewer/agg_svg_gradient.h (revision 0) | |
+++ svg_viewer/agg_svg_gradient.h (working copy) | |
@@ -0,0 +1,274 @@ | |
+//---------------------------------------------------------------------------- | |
+// 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 | |
+//---------------------------------------------------------------------------- | |
+// | |
+// Gunnar Roth: add support for linear and radial gradients(support xlink attr), | |
+// shape gradient opaqueness, rounded rects, circles,ellipses. support a command (arc) in pathes. | |
+// set new origin correctly to last postion on z command in a path( was set to 0,0 before). | |
+// enable parsing of colors written as rgb() | |
+// some code was inspired by code from Haiku OS | |
+/* | |
+* Copyright 2006-2007, Haiku. All rights reserved. | |
+* Distributed under the terms of the MIT License. | |
+* | |
+* Authors: | |
+* Stephan Aßmus <superstippi@gmx.de> | |
+*/ | |
+// | |
+ | |
+#ifndef SVG_GRADIENTS_H | |
+#define SVG_GRADIENTS_H | |
+ | |
+ | |
+#include <agg_color_rgba.h> | |
+#include <agg_trans_affine.h> | |
+ | |
+ | |
+#include <String.h> | |
+#include <string> | |
+#include <map> | |
+#include "agg_gradient_lut.h" | |
+ | |
+ | |
+namespace agg { | |
+ namespace svg { | |
+ | |
+ template<class ColorInterpolator, | |
+ unsigned ColorLutSize=256> class gradient_lut_opaque | |
+ { | |
+ public: | |
+ typedef ColorInterpolator interpolator_type; | |
+ typedef typename interpolator_type::color_type color_type; | |
+ enum { color_lut_size = ColorLutSize }; | |
+ | |
+ //-------------------------------------------------------------------- | |
+ gradient_lut_opaque() : m_color_lut(color_lut_size) {} | |
+ | |
+ // Build Gradient Lut | |
+ // First, call remove_all(), then add_color() at least twice, | |
+ // then build_lut(). Argument "offset" in add_color must be | |
+ // in range [0...1] and defines a color stop as it is described | |
+ // in SVG specification, section Gradients and Patterns. | |
+ // The simplest linear gradient is: | |
+ // gradient_lut.add_color(0.0, start_color); | |
+ // gradient_lut.add_color(1.0, end_color); | |
+ //-------------------------------------------------------------------- | |
+ void remove_all(); | |
+ void add_color(double offset, const color_type& color); | |
+ void build_lut(double opaque ); | |
+ | |
+ // Size-index Interface. This class can be used directly as the | |
+ // ColorF in span_gradient. All it needs is two access methods | |
+ // size() and operator []. | |
+ //-------------------------------------------------------------------- | |
+ static unsigned size() | |
+ { | |
+ return color_lut_size; | |
+ } | |
+ const color_type& operator [] (unsigned i) const | |
+ { | |
+ return m_color_lut[i]; | |
+ } | |
+ | |
+ private: | |
+ //-------------------------------------------------------------------- | |
+ struct color_point | |
+ { | |
+ double offset; | |
+ color_type color; | |
+ | |
+ color_point() {} | |
+ color_point(double off, const color_type& c) : | |
+ offset(off), color(c) | |
+ { | |
+ if(offset < 0.0) offset = 0.0; | |
+ if(offset > 1.0) offset = 1.0; | |
+ } | |
+ }; | |
+ typedef agg::pod_bvector<color_point, 4> color_profile_type; | |
+ typedef agg::pod_array<color_type> color_lut_type; | |
+ | |
+ static bool offset_less(const color_point& a, const color_point& b) | |
+ { | |
+ return a.offset < b.offset; | |
+ } | |
+ static bool offset_equal(const color_point& a, const color_point& b) | |
+ { | |
+ return a.offset == b.offset; | |
+ } | |
+ | |
+ //-------------------------------------------------------------------- | |
+ color_profile_type m_color_profile; | |
+ color_lut_type m_color_lut; | |
+ }; | |
+ | |
+ | |
+ | |
+ //------------------------------------------------------------------------ | |
+ template<class T, unsigned S> | |
+ void gradient_lut_opaque<T,S>::remove_all() | |
+ { | |
+ m_color_profile.remove_all(); | |
+ } | |
+ | |
+ //------------------------------------------------------------------------ | |
+ template<class T, unsigned S> | |
+ void gradient_lut_opaque<T,S>::add_color(double offset, const color_type& color) | |
+ { | |
+ m_color_profile.add(color_point(offset, color)); | |
+ } | |
+ | |
+ //------------------------------------------------------------------------ | |
+ template<class T, unsigned S> | |
+ void gradient_lut_opaque<T,S>::build_lut(double opaque) | |
+ { | |
+ quick_sort(m_color_profile, offset_less); | |
+ m_color_profile.cut_at(remove_duplicates(m_color_profile, offset_equal)); | |
+ if(m_color_profile.size() >= 2) | |
+ { | |
+ unsigned i; | |
+ unsigned start = uround(m_color_profile[0].offset * color_lut_size); | |
+ unsigned end; | |
+ color_type c = m_color_profile[0].color; | |
+ c.opacity(c.opacity() * opaque); | |
+ for(i = 0; i < start; i++) | |
+ { | |
+ m_color_lut[i] = c; | |
+ } | |
+ for(i = 1; i < m_color_profile.size(); i++) | |
+ { | |
+ end = uround(m_color_profile[i].offset * color_lut_size); | |
+ color_type c1 = m_color_profile[i-1].color; | |
+ c1.opacity(c1.opacity() * opaque); | |
+ color_type c2 = m_color_profile[i].color; | |
+ c2.opacity(c2.opacity() * opaque); | |
+ interpolator_type ci(c1, | |
+ c2 , | |
+ end - start + 1); | |
+ while(start < end) | |
+ { | |
+ m_color_lut[start] = ci.color(); | |
+ ++ci; | |
+ ++start; | |
+ } | |
+ } | |
+ c = m_color_profile.last().color; | |
+ c.opacity(c.opacity() * opaque); | |
+ for(; end < m_color_lut.size(); end++) | |
+ { | |
+ m_color_lut[end] = c; | |
+ } | |
+ } | |
+ } | |
+ class gradient { | |
+ public: | |
+ static const int lut_range = 128; | |
+ typedef gradient_lut_opaque<agg::color_interpolator<agg::rgba8>, lut_range * 2> color_func_type; | |
+ enum gradients_type { | |
+ GRADIENT_LINEAR = 0, | |
+ GRADIENT_CIRCULAR, | |
+ GRADIENT_CIRCULAR_FOCAL, | |
+ GRADIENT_DIAMOND, | |
+ GRADIENT_CONIC, | |
+ GRADIENT_XY, | |
+ GRADIENT_SQRT_XY | |
+ }; | |
+ gradient(); | |
+ virtual ~gradient(); | |
+ | |
+ void set_id(const char* id); | |
+ const char* id() const; | |
+ void set_opaque(double opaque) | |
+ { | |
+ if(opaque != m_opaque) | |
+ { | |
+ m_opaque = opaque; | |
+ m_gradient_lut.build_lut(m_opaque); | |
+ } | |
+ } | |
+ virtual void add_stop(double offset, rgba8 color); | |
+ void set_transformation(const trans_affine& transform); | |
+ virtual void realize() = 0; | |
+ | |
+ | |
+ void add_string(const char *name, const char *string) | |
+ { | |
+ m_mapStrings[name] = string; | |
+ } | |
+ | |
+ color_func_type &lut() | |
+ { | |
+ return m_gradient_lut; | |
+ } | |
+ | |
+ void set_type(gradients_type type) | |
+ { | |
+ m_type = type; | |
+ } | |
+ gradients_type type() const | |
+ { return m_type; } | |
+ | |
+ const trans_affine & transform() | |
+ { | |
+ return m_transform; | |
+ } | |
+ unsigned count_colors() | |
+ { | |
+ return m_colcnt; | |
+ } | |
+ std::string xlink() | |
+ { | |
+ std::string xlink; | |
+ find_string("xlink:href",&xlink); | |
+ return xlink; | |
+ } | |
+ | |
+ protected: | |
+ bool find_string(const char * key,std::string * pVal ) const | |
+ { | |
+ std::map<std::string,std::string>::const_iterator it = m_mapStrings.find(key); | |
+ if(it == m_mapStrings.end()) | |
+ return false; | |
+ *pVal=it->second; | |
+ return true; | |
+ } | |
+ trans_affine m_transform; | |
+ color_func_type m_gradient_lut; | |
+ unsigned m_colcnt; | |
+ private: | |
+ std::map<std::string,std::string> m_mapStrings; | |
+ gradients_type m_type; | |
+ double m_opaque; | |
+ std::string m_id; | |
+ }; | |
+ | |
+ class linear_gradient : public gradient { | |
+ public: | |
+ linear_gradient(); | |
+ virtual ~linear_gradient(); | |
+ | |
+ virtual void realize(); | |
+ }; | |
+ | |
+ class radial_gradient : public gradient { | |
+ public: | |
+ radial_gradient(); | |
+ virtual ~radial_gradient(); | |
+ virtual void realize(); | |
+ }; | |
+ | |
+ } // namespace svg | |
+} // namespace agg | |
+ | |
+#endif // SVG_GRADIENTS_H | |
\ No newline at end of file | |
Index: svg_viewer/agg_svg_parser.cpp | |
=================================================================== | |
--- svg_viewer/agg_svg_parser.cpp (revision 50) | |
+++ svg_viewer/agg_svg_parser.cpp (working copy) | |
@@ -13,6 +13,19 @@ | |
// http://www.antigrain.com | |
//---------------------------------------------------------------------------- | |
// | |
+// Gunnar Roth: add support for linear and radial gradients(support xlink attr), | |
+// shape gradient opaqueness, rounded rects, circles,ellipses. support a command (arc) in pathes. | |
+// set new origin correctly to last postion on z command in a path( was set to 0,0 before). | |
+// enable parsing of colors written as rgb() | |
+// some code was inspired by code from Haiku OS | |
+/* | |
+* Copyright 2006-2007, Haiku. All rights reserved. | |
+* Distributed under the terms of the MIT License. | |
+* | |
+* Authors: | |
+* Stephan Aßmus <superstippi@gmx.de> | |
+*/ | |
+// | |
// SVG parser. | |
// | |
//---------------------------------------------------------------------------- | |
@@ -22,7 +35,7 @@ | |
#include <ctype.h> | |
#include "agg_svg_parser.h" | |
#include "expat.h" | |
- | |
+#include "agg_svg_gradient.h" | |
namespace agg | |
{ | |
namespace svg | |
@@ -185,7 +198,99 @@ | |
{ "zzzzzzzzzzz",0,0,0, 0 } | |
}; | |
+ //------------------------------------------------------------- | |
+ 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; | |
+ if (*str == '#') { | |
+ str++; | |
+ int32 length = strlen(str); | |
+ unsigned c = 0; | |
+ if (length == 3) { | |
+ // if there are only 3 byte, than it means that we | |
+ // need to expand the color (#f60 -> #ff6600) | |
+ // TODO: There must be an easier way... | |
+ char expanded[7]; | |
+ expanded[0] = *str; | |
+ expanded[1] = *str++; | |
+ expanded[2] = *str; | |
+ expanded[3] = *str++; | |
+ expanded[4] = *str; | |
+ expanded[5] = *str++; | |
+ expanded[6] = 0; | |
+ sscanf(expanded, "%x", &c); | |
+ } else { | |
+ sscanf(str, "%x", &c); | |
+ } | |
+ return rgb8_packed(c); | |
+ } | |
+ else if(*str == 'r') | |
+ { | |
+ int r,g,b; | |
+ sscanf(str,"rgb(%d,%d,%d)",&r,&g,&b); | |
+ return rgba8(r,g,b); | |
+ } | |
+ 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; | |
+ double value = atof(str); | |
+ // handle percent | |
+ int32 length = strlen(str); | |
+ if (str[length - 1] == '%') | |
+ value /= 100.0; | |
+ return value; | |
+ } | |
+// parse_url | |
+ char* | |
+ parse_url(const char* str) | |
+ { | |
+ const char* begin = str; | |
+ while (*begin != '#') | |
+ begin++; | |
+ | |
+ begin++; | |
+ const char* end = begin; | |
+ while (*end != ')') | |
+ end++; | |
+ | |
+ end--; | |
+ | |
+ int32 length = end - begin + 2; | |
+ char* result = new char[length]; | |
+ memcpy(result, begin, length - 1); | |
+ result[length - 1] = 0; | |
+ | |
+ return result; | |
+} | |
+ | |
//------------------------------------------------------------------------ | |
parser::~parser() | |
{ | |
@@ -207,7 +312,8 @@ | |
m_attr_name(new char[128]), | |
m_attr_value(new char[1024]), | |
m_attr_name_len(127), | |
- m_attr_value_len(1023) | |
+ m_attr_value_len(1023), | |
+ m_tags_ignored(false) | |
{ | |
m_title[0] = 0; | |
} | |
@@ -264,7 +370,11 @@ | |
void parser::start_element(void* data, const char* el, const char** attr) | |
{ | |
parser& self = *(parser*)data; | |
- | |
+ if (strcmp(el, "svg") == 0) | |
+ { | |
+ self.parse_svg(attr); | |
+ } | |
+ else | |
if(strcmp(el, "title") == 0) | |
{ | |
self.m_title_flag = true; | |
@@ -288,6 +398,16 @@ | |
self.m_path_flag = true; | |
} | |
else | |
+ if (strcmp(el, "circle") == 0) | |
+ { | |
+ self.parse_circle(attr); | |
+ } | |
+ else | |
+ if (strcmp(el, "ellipse") == 0) | |
+ { | |
+ self.parse_ellipse(attr); | |
+ } | |
+ else | |
if(strcmp(el, "rect") == 0) | |
{ | |
self.parse_rect(attr); | |
@@ -307,11 +427,26 @@ | |
{ | |
self.parse_poly(attr, true); | |
} | |
+ else | |
+ if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0) | |
+ { | |
+ self.parse_gradient(attr, strcmp(el, "radialGradient") == 0); | |
+ } | |
+ else | |
+ if (strcmp(el, "stop") == 0) | |
+ { | |
+ self.parse_gradient_stop(attr); | |
+ } | |
//else | |
//if(strcmp(el, "<OTHER_ELEMENTS>") == 0) | |
//{ | |
//} | |
// . . . | |
+ else | |
+ { | |
+ fprintf(stderr, "SVGParser igoring tag: \"%s\"\n", el); | |
+ self.m_tags_ignored = true; | |
+ } | |
} | |
@@ -334,6 +469,12 @@ | |
{ | |
self.m_path_flag = false; | |
} | |
+ else | |
+ if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0) | |
+ { | |
+ self.m_path.end_gradient(); | |
+ } | |
+ | |
//else | |
//if(strcmp(el, "<OTHER_ELEMENTS>") == 0) | |
//{ | |
@@ -361,6 +502,55 @@ | |
} | |
} | |
+// parse_svg | |
+ void parser::parse_svg(const char** attr) | |
+ { | |
+ double width = 0.0; | |
+ double height = 0.0; | |
+ rect_d viewBox(0.0, 0.0, -1.0, -1.0); | |
+ | |
+ for (int i = 0; attr[i]; i += 2) { | |
+ if (strcmp(attr[i], "width") == 0) | |
+ { | |
+ width = parse_double(attr[i + 1]); | |
+ } | |
+ else | |
+ if (strcmp(attr[i], "height") == 0) | |
+ { | |
+ height = parse_double(attr[i + 1]); | |
+ } | |
+ else | |
+ if (strcmp(attr[i], "viewBox") == 0) | |
+ { | |
+ m_tokenizer.set_path_str(attr[i + 1]); | |
+ if(!m_tokenizer.next()) | |
+ { | |
+ throw exception("parse_svg (viewBox): Too few coordinates"); | |
+ } | |
+ viewBox.x1 = m_tokenizer.last_number(); | |
+ if(!m_tokenizer.next()) | |
+ { | |
+ throw exception("parse_svg (viewBox): Too few coordinates"); | |
+ } | |
+ viewBox.y1 = m_tokenizer.last_number(); | |
+ if(!m_tokenizer.next()) | |
+ { | |
+ throw exception("parse_svg (viewBox): Too few coordinates"); | |
+ } | |
+ viewBox.x2 = m_tokenizer.last_number(); | |
+ if(!m_tokenizer.next()) | |
+ { | |
+ throw exception("parse_svg (viewBox): Too few coordinates"); | |
+ } | |
+ viewBox.y2 = m_tokenizer.last_number(); | |
+ } | |
+ } | |
+ if (width >= 0.0 && height >= 0.0) { | |
+ //set viewBox | |
+ } else { | |
+ throw exception("parse_svg: Invalid width or height\n"); | |
+ } | |
+ } | |
//------------------------------------------------------------------------ | |
void parser::parse_attr(const char** attr) | |
@@ -410,53 +600,10 @@ | |
} | |
- //------------------------------------------------------------- | |
- 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) | |
{ | |
@@ -465,12 +612,21 @@ | |
parse_style(value); | |
} | |
else | |
+ if(strcmp(name, "opacity") == 0) { | |
+ m_path.opacity(parse_double(value)); | |
+ } else | |
if(strcmp(name, "fill") == 0) | |
{ | |
if(strcmp(value, "none") == 0) | |
{ | |
m_path.fill_none(); | |
} | |
+ else if (strncmp(value, "url", 3) == 0) | |
+ { | |
+ char* url = parse_url(value); | |
+ m_path.fill_url(url); | |
+ delete[] url; | |
+ } | |
else | |
{ | |
m_path.fill(parse_color(value)); | |
@@ -482,12 +638,22 @@ | |
m_path.fill_opacity(parse_double(value)); | |
} | |
else | |
+ if(strcmp(name, "fill-rule") == 0) | |
+ { | |
+ m_path.even_odd(strcmp(value, "evenodd") == 0); | |
+ } | |
+ else | |
if(strcmp(name, "stroke") == 0) | |
{ | |
if(strcmp(value, "none") == 0) | |
{ | |
m_path.stroke_none(); | |
} | |
+ else if (strncmp(value, "url", 3) == 0) { | |
+ char* url = parse_url(value); | |
+ m_path.stroke_url(url); | |
+ delete[] url; | |
+ } | |
else | |
{ | |
m_path.stroke(parse_color(value)); | |
@@ -525,8 +691,17 @@ | |
else | |
if(strcmp(name, "transform") == 0) | |
{ | |
- parse_transform(value); | |
+ m_path.transform().premultiply(parse_transform(value)); | |
} | |
+ else | |
+ if (strcmp(name, "stop-color") == 0) | |
+ { | |
+ m_gradient_stop_color = parse_color(value); | |
+ } | |
+ else | |
+ if (strcmp(name, "stop-opacity") == 0) { | |
+ m_gradient_stop_color.opacity(parse_double(value)); | |
+ } | |
//else | |
//if(strcmp(el, "<OTHER_ATTRIBUTES>") == 0) | |
//{ | |
@@ -617,7 +792,89 @@ | |
} | |
+ // parse_circle | |
+ void | |
+ parser::parse_circle(const char** attr) | |
+ { | |
+ int i; | |
+ double cx = 0.0; | |
+ double cy = 0.0; | |
+ double r = 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], "cx") == 0) cx = parse_double(attr[i + 1]); | |
+ if(strcmp(attr[i], "cy") == 0) cy = parse_double(attr[i + 1]); | |
+ if(strcmp(attr[i], "r") == 0) r = parse_double(attr[i + 1]); | |
+ } | |
+ } | |
+ | |
+ | |
+ if (r != 0.0) { | |
+ if (r < 0.0) throw exception("parse_circle: Invalid radius: %f", r); | |
+ | |
+ m_path.move_to(cx, cy - r); | |
+ m_path.curve4(cx + r * 0.56, cy - r, | |
+ cx + r, cy - r * 0.56, | |
+ cx + r, cy); | |
+ m_path.curve4(cx + r, cy + r * 0.56, | |
+ cx + r * 0.56, cy + r, | |
+ cx, cy + r); | |
+ m_path.curve4(cx - r * 0.56, cy + r, | |
+ cx - r, cy + r * 0.56, | |
+ cx - r, cy); | |
+ m_path.curve4(cx - r, cy - r * 0.56, | |
+ cx - r * 0.56, cy - r, | |
+ cx, cy - r); | |
+ m_path.close_subpath(); | |
+ } | |
+ m_path.end_path(); | |
+ } | |
+ | |
+ // parse_ellipse | |
+ void | |
+ parser::parse_ellipse(const char** attr) | |
+ { | |
+ int i; | |
+ double cx = 0.0; | |
+ double cy = 0.0; | |
+ double rx = 0.0; | |
+ double ry = 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], "cx") == 0) cx = parse_double(attr[i + 1]); | |
+ if(strcmp(attr[i], "cy") == 0) cy = parse_double(attr[i + 1]); | |
+ if(strcmp(attr[i], "rx") == 0) rx = parse_double(attr[i + 1]); | |
+ if(strcmp(attr[i], "ry") == 0) ry = parse_double(attr[i + 1]); | |
+ } | |
+ } | |
+ | |
+ | |
+ if (rx != 0.0 && ry != 0.0) { | |
+ if (rx < 0.0) throw exception("parse_ellipse: Invalid x-radius: %f", rx); | |
+ if (ry < 0.0) throw exception("parse_ellipse: Invalid y-radius: %f", ry); | |
+ | |
+ m_path.move_to(cx, cy - ry); | |
+ m_path.curve4(cx + rx * 0.56, cy - ry, | |
+ cx + rx, cy - ry * 0.56, | |
+ cx + rx, cy); | |
+ m_path.curve4(cx + rx, cy + ry * 0.56, | |
+ cx + rx * 0.56, cy + ry, | |
+ cx, cy + ry); | |
+ m_path.curve4(cx - rx * 0.56, cy + ry, | |
+ cx - rx, cy + ry * 0.56, | |
+ cx - rx, cy); | |
+ m_path.curve4(cx - rx, cy - ry * 0.56, | |
+ cx - rx * 0.56, cy - ry, | |
+ cx, cy - ry); | |
+ m_path.close_subpath(); | |
+ } | |
+ m_path.end_path(); | |
+ } | |
+ | |
//------------------------------------------------------------- | |
void parser::parse_rect(const char** attr) | |
{ | |
@@ -626,7 +883,8 @@ | |
double y = 0.0; | |
double w = 0.0; | |
double h = 0.0; | |
- | |
+ double rx = 0.0; | |
+ double ry = 0.0; | |
m_path.begin_path(); | |
for(i = 0; attr[i]; i += 2) | |
{ | |
@@ -636,9 +894,9 @@ | |
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(strcmp(attr[i], "rx") == 0) rx = parse_double(attr[i + 1]); | |
+ if(strcmp(attr[i], "ry") == 0) ry = parse_double(attr[i + 1]); | |
+ } | |
} | |
@@ -646,12 +904,18 @@ | |
{ | |
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(); | |
+ if(rx >0 || ry > 0) | |
+ { | |
+ m_path.roundrect(x,y,x+w,y+h,rx,ry); | |
+ } | |
+ else | |
+ { | |
+ 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(); | |
} | |
@@ -731,18 +995,19 @@ | |
} | |
//------------------------------------------------------------- | |
- void parser::parse_transform(const char* str) | |
+ trans_affine parser::parse_transform(const char* str) | |
{ | |
+ trans_affine transform; | |
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 | |
+ if(strncmp(str, "matrix", 6) == 0) str += parse_matrix(str,transform); else | |
+ if(strncmp(str, "translate", 9) == 0) str += parse_translate(str,transform); else | |
+ if(strncmp(str, "rotate", 6) == 0) str += parse_rotate(str,transform); else | |
+ if(strncmp(str, "scale", 5) == 0) str += parse_scale(str,transform); else | |
+ if(strncmp(str, "skewX", 5) == 0) str += parse_skew_x(str,transform); else | |
+ if(strncmp(str, "skewY", 5) == 0) str += parse_skew_y(str,transform); else | |
{ | |
++str; | |
} | |
@@ -752,9 +1017,70 @@ | |
++str; | |
} | |
} | |
+ return transform; | |
} | |
+ // parse_gradient | |
+ void | |
+ parser::parse_gradient(const char** attr, bool radial) | |
+ { | |
+ // printf("parser::parse_gradient(%s)\n", attr[0]); | |
+ m_path.start_gradient(radial); | |
+ | |
+ for (int32 i = 0; attr[i]; i += 2) | |
+ { | |
+ /* if(!parse_attr(attr[i], attr[i + 1])) | |
+ {*/ | |
+ if (strcmp(attr[i], "id") == 0) | |
+ m_path.current_gradient()->set_id(attr[i + 1]); | |
+ else if(strcmp(attr[i], "gradientTransform") == 0) { | |
+ m_path.current_gradient()->set_transformation(parse_transform(attr[i + 1])); | |
+ } else | |
+ m_path.current_gradient()->add_string(attr[i], attr[i + 1]); | |
+ /*}*/ | |
+ } | |
+ } | |
+ | |
+ // parse_gradient_stop | |
+ void | |
+ parser::parse_gradient_stop(const char** attr) | |
+ { | |
+ // printf("parser::parse_gradient_stop(%s)\n", attr[0]); | |
+ | |
+ double offset = 0.0; | |
+ rgba8 color; | |
+ for (int32 i = 0; attr[i]; i += 2) { | |
+ if (strcmp(attr[i], "offset") == 0) { | |
+ offset = parse_double(attr[i + 1]); | |
+ } else | |
+ if (strcmp(attr[i], "style") == 0) { | |
+ parse_style(attr[i + 1]); | |
+ // here we get a bit hacky, in order not to change too much code at once... | |
+ // historically, parse_style() was for parsing path attributes only, but | |
+ // it comes in handy here as well, and I added "stop-color" and "stop-opacity" | |
+ // to parse_name_value(). It remembers the color in "fGradientStopColor". | |
+ // The color will of course be broken if the "style" attribute did not contain | |
+ // any valid stuff. | |
+ color = m_gradient_stop_color; | |
+ } else | |
+ if (strcmp(attr[i], "stop-color") == 0) { | |
+ color = parse_color(attr[i + 1]); | |
+ } else | |
+ if (strcmp(attr[i], "stop-opacity") == 0) { | |
+ color.opacity(parse_double(attr[i + 1])); | |
+ } | |
+ } | |
+ | |
+ // printf(" offset: %f, color: %d, %d, %d, %d\n", offset, color.r, color.g, color.b, color.a); | |
+ | |
+ if (gradient* gradient = m_path.current_gradient()) { | |
+ gradient->add_stop(offset, color); | |
+ } else { | |
+ throw exception("parse_gradient_stop() outside of gradient tag!\n"); | |
+ } | |
+ } | |
+ | |
//------------------------------------------------------------- | |
static bool is_numeric(char c) | |
{ | |
@@ -801,7 +1127,7 @@ | |
} | |
//------------------------------------------------------------- | |
- unsigned parser::parse_matrix(const char* str) | |
+ unsigned parser::parse_matrix(const char* str, trans_affine& transform) | |
{ | |
double args[6]; | |
unsigned na = 0; | |
@@ -810,37 +1136,37 @@ | |
{ | |
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])); | |
+ 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) | |
+ unsigned parser::parse_translate(const char* str, trans_affine& transform) | |
{ | |
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])); | |
+ transform.premultiply(trans_affine_translation(args[0], args[1])); | |
return len; | |
} | |
//------------------------------------------------------------- | |
- unsigned parser::parse_rotate(const char* str) | |
+ unsigned parser::parse_rotate(const char* str, trans_affine& transform) | |
{ | |
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]))); | |
+ 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); | |
+ transform.premultiply(t); | |
} | |
else | |
{ | |
@@ -850,33 +1176,33 @@ | |
} | |
//------------------------------------------------------------- | |
- unsigned parser::parse_scale(const char* str) | |
+ unsigned parser::parse_scale(const char* str, trans_affine& transform) | |
{ | |
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])); | |
+ transform.premultiply(trans_affine_scaling(args[0], args[1])); | |
return len; | |
} | |
//------------------------------------------------------------- | |
- unsigned parser::parse_skew_x(const char* str) | |
+ unsigned parser::parse_skew_x(const char* str, trans_affine& transform) | |
{ | |
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)); | |
+ transform.premultiply(trans_affine_skewing(deg2rad(arg), 0.0)); | |
return len; | |
} | |
//------------------------------------------------------------- | |
- unsigned parser::parse_skew_y(const char* str) | |
+ unsigned parser::parse_skew_y(const char* str, trans_affine& transform) | |
{ | |
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))); | |
+ transform.premultiply(trans_affine_skewing(0.0, deg2rad(arg))); | |
return len; | |
} | |
Index: svg_viewer/agg_svg_parser.h | |
=================================================================== | |
--- svg_viewer/agg_svg_parser.h (revision 50) | |
+++ svg_viewer/agg_svg_parser.h (working copy) | |
@@ -12,6 +12,18 @@ | |
// mcseemagg@yahoo.com | |
// http://www.antigrain.com | |
//---------------------------------------------------------------------------- | |
+// Gunnar Roth: add support for linear and radial gradients(support xlink attr), | |
+// shape gradient opaqueness, rounded rects, circles,ellipses. support a command (arc) in pathes. | |
+// set new origin correctly to last postion on z command in a path( was set to 0,0 before). | |
+// enable parsing of colors written as rgb() | |
+// some code was inspired by code from Haiku OS | |
+/* | |
+* Copyright 2006-2007, Haiku. All rights reserved. | |
+* Distributed under the terms of the MIT License. | |
+* | |
+* Authors: | |
+* Stephan Aßmus <superstippi@gmx.de> | |
+*/ | |
// | |
// SVG parser. | |
// | |
@@ -38,27 +50,35 @@ | |
void parse(const char* fname); | |
const char* title() const { return m_title; } | |
+ bool tags_ignored() const | |
+ { return m_tags_ignored; } | |
+ | |
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_svg(const char** attr); | |
void parse_attr(const char** attr); | |
void parse_path(const char** attr); | |
void parse_poly(const char** attr, bool close_flag); | |
+ void parse_circle(const char** attr); | |
+ void parse_ellipse(const char** attr); | |
void parse_rect(const char** attr); | |
void parse_line(const char** attr); | |
void parse_style(const char* str); | |
- void parse_transform(const char* str); | |
+ trans_affine parse_transform(const char* str); | |
+ void parse_gradient(const char** attr, bool radial); | |
+ void parse_gradient_stop(const char** attr); | |
- 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); | |
+ unsigned parse_matrix(const char* str, trans_affine& transform); | |
+ unsigned parse_translate(const char* str, trans_affine& transform); | |
+ unsigned parse_rotate(const char* str, trans_affine& transform); | |
+ unsigned parse_scale(const char* str, trans_affine& transform); | |
+ unsigned parse_skew_x(const char* str, trans_affine& transform); | |
+ unsigned parse_skew_y(const char* str, trans_affine& transform); | |
bool parse_attr(const char* name, const char* value); | |
bool parse_name_value(const char* nv_start, const char* nv_end); | |
@@ -77,6 +97,10 @@ | |
char* m_attr_value; | |
unsigned m_attr_name_len; | |
unsigned m_attr_value_len; | |
+ rgba8 m_gradient_stop_color; | |
+ | |
+bool m_tags_ignored; | |
+ | |
}; | |
} | |
Index: svg_viewer/agg_svg_path_renderer.cpp | |
=================================================================== | |
--- svg_viewer/agg_svg_path_renderer.cpp (revision 50) | |
+++ svg_viewer/agg_svg_path_renderer.cpp (working copy) | |
@@ -13,6 +13,19 @@ | |
// http://www.antigrain.com | |
//---------------------------------------------------------------------------- | |
// | |
+// Gunnar Roth: add support for linear and radial gradients(support xlink attr), | |
+// shape gradient opaqueness, rounded rects, circles,ellipses. support a command (arc) in pathes. | |
+// set new origin correctly to last postion on z command in a path( was set to 0,0 before). | |
+// enable parsing of colors written as rgb() | |
+// some code was inspired by code from Haiku OS | |
+/* | |
+* Copyright 2006-2007, Haiku. All rights reserved. | |
+* Distributed under the terms of the MIT License. | |
+* | |
+* Authors: | |
+* Stephan Aßmus <superstippi@gmx.de> | |
+*/ | |
+// | |
// SVG path renderer. | |
// | |
//---------------------------------------------------------------------------- | |
@@ -26,7 +39,9 @@ | |
{ | |
//------------------------------------------------------------------------ | |
- path_renderer::path_renderer() : | |
+ path_renderer::path_renderer() | |
+ : | |
+ m_cur_gradient(NULL), | |
m_curved(m_storage), | |
m_curved_count(m_curved), | |
@@ -163,7 +178,19 @@ | |
m_storage.curve4(x2, y2, x, y); | |
} | |
} | |
- | |
+// elliptical_arc | |
+void | |
+path_renderer::elliptical_arc(double rx, double ry, double angle, | |
+ bool large_arc_flag, bool sweep_flag, | |
+ double x, double y, bool rel) | |
+ { | |
+ angle = angle / 180.0 * pi; | |
+ 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() | |
{ | |
@@ -223,7 +250,8 @@ | |
//------------------------------------------------------------------------ | |
void path_renderer::stroke_width(double w) | |
{ | |
- cur_attr().stroke_width = w; | |
+ path_attributes& attr = cur_attr(); | |
+ attr.stroke_width = w; | |
} | |
//------------------------------------------------------------------------ | |
@@ -231,13 +259,31 @@ | |
{ | |
cur_attr().fill_flag = false; | |
} | |
+// fill_url | |
+ void path_renderer::fill_url(const char* url) | |
+ { | |
+ sprintf(cur_attr().fill_url, "%s", url); | |
+ } | |
+ | |
//------------------------------------------------------------------------ | |
void path_renderer::stroke_none() | |
{ | |
cur_attr().stroke_flag = false; | |
} | |
- | |
+// stroke_url | |
+ void | |
+ path_renderer::stroke_url(const char* url) | |
+ { | |
+ sprintf(cur_attr().stroke_url, "%s", url); | |
+ } | |
+ | |
+ // opacity | |
+ void | |
+ path_renderer::opacity(double op) | |
+ { | |
+ cur_attr().opacity *= op; | |
+ } | |
//------------------------------------------------------------------------ | |
void path_renderer::fill_opacity(double op) | |
{ | |
@@ -277,6 +323,7 @@ | |
//------------------------------------------------------------------------ | |
void path_renderer::parse_path(path_tokenizer& tok) | |
{ | |
+ char lastCmd = 0; | |
while(tok.next()) | |
{ | |
double arg[10]; | |
@@ -287,7 +334,10 @@ | |
case 'M': case 'm': | |
arg[0] = tok.last_number(); | |
arg[1] = tok.next(cmd); | |
- move_to(arg[0], arg[1], cmd == 'm'); | |
+ if (lastCmd != cmd) | |
+ move_to(arg[0], arg[1], cmd == 'm'); | |
+ else | |
+ line_to(arg[0], arg[1], lastCmd == 'm'); | |
break; | |
case 'L': case 'l': | |
@@ -337,11 +387,30 @@ | |
curve4(arg[0], arg[1], arg[2], arg[3], cmd == 's'); | |
break; | |
- case 'A': case 'a': | |
- throw exception("parse_path: Command A: NOT IMPLEMENTED YET"); | |
+ case 'A': case 'a': { | |
+ arg[0] = tok.last_number(); | |
+ for(i = 1; i < 3; i++) { | |
+ arg[i] = tok.next(cmd); | |
+ } | |
+ bool large_arc_flag = tok.next(cmd) ? true : false; | |
+ bool sweep_flag = tok.next(cmd) ? true : false; | |
+ for(i = 3; i < 5; i++) { | |
+ arg[i] = tok.next(cmd); | |
+ } | |
+ elliptical_arc(arg[0], arg[1], arg[2], | |
+ large_arc_flag, sweep_flag, | |
+ arg[3], arg[4], cmd == 'a'); | |
+ break; | |
+ } | |
case 'Z': case 'z': | |
- close_subpath(); | |
+ { | |
+ double x = m_storage.last_x(); | |
+ double y = m_storage.last_y(); | |
+ close_subpath(); | |
+ move_to(x,y,false); | |
+ } | |
+ | |
break; | |
default: | |
@@ -351,9 +420,68 @@ | |
throw exception(buf); | |
} | |
} | |
+ lastCmd = cmd; | |
} | |
} | |
+ | |
+ void path_renderer::start_gradient(bool radial) | |
+ { | |
+ if (m_cur_gradient) { | |
+ fprintf(stderr, "path_renderer::StartGradient() - ERROR: " | |
+ "previous gradient (%s) not finished!\n", | |
+ m_cur_gradient->id()); | |
+ } | |
+ | |
+ if (radial) | |
+ m_cur_gradient = new radial_gradient(); | |
+ else | |
+ m_cur_gradient = new linear_gradient(); | |
+ | |
+ add_gradient(m_cur_gradient); | |
+ } | |
+ | |
+ | |
+ void path_renderer::end_gradient() | |
+ { | |
+ if (m_cur_gradient) { | |
+ m_cur_gradient->realize(); | |
+ } else { | |
+ fprintf(stderr, "path_renderer::EndGradient() - " | |
+ "ERROR: no gradient started!\n"); | |
+ } | |
+ m_cur_gradient = NULL; | |
+ } | |
+ | |
+ // #pragma mark - | |
+ | |
+ // _AddGradient | |
+ void | |
+ path_renderer::add_gradient(gradient* gradient) | |
+ { | |
+ if (gradient) { | |
+ m_gradients.push_back(gradient); | |
+ } | |
+ } | |
+ | |
+ // _GradientAt | |
+ gradient* | |
+ path_renderer::gradient_at(int32 index) const | |
+ { | |
+ return m_gradients.at(index); | |
+ } | |
+ | |
+ // _FindGradient | |
+ gradient* | |
+ path_renderer::find_gradient(const char* name) const | |
+ { | |
+ for (int32 i = 0; gradient* g = gradient_at(i); i++) { | |
+ if (strcmp(g->id(), name) == 0) | |
+ return g; | |
+ } | |
+ return NULL; | |
+ } | |
+ | |
} | |
} | |
Index: svg_viewer/agg_svg_path_renderer.h | |
=================================================================== | |
--- svg_viewer/agg_svg_path_renderer.h (revision 50) | |
+++ svg_viewer/agg_svg_path_renderer.h (working copy) | |
@@ -12,7 +12,20 @@ | |
// mcseemagg@yahoo.com | |
// http://www.antigrain.com | |
//---------------------------------------------------------------------------- | |
+// Gunnar Roth: add support for linear and radial gradients(support xlink attr), | |
+// shape gradient opaqueness, rounded rects, circles,ellipses. support a command (arc) in pathes. | |
+// set new origin correctly to last postion on z command in a path( was set to 0,0 before). | |
+// enable parsing of colors written as rgb() | |
+// some code was inspired by code from Haiku OS | |
+/* | |
+* Copyright 2006-2007, Haiku. All rights reserved. | |
+* Distributed under the terms of the MIT License. | |
+* | |
+* Authors: | |
+* Stephan Aßmus <superstippi@gmx.de> | |
+*/ | |
// | |
+// | |
// SVG path renderer. | |
// | |
//---------------------------------------------------------------------------- | |
@@ -29,7 +42,12 @@ | |
#include "agg_bounding_rect.h" | |
#include "agg_rasterizer_scanline_aa.h" | |
#include "agg_svg_path_tokenizer.h" | |
- | |
+#include <vector> | |
+#include "agg_svg_gradient.h" | |
+#include "agg_span_gradient.h" | |
+#include "agg_span_allocator.h" | |
+#include "agg_span_interpolator_linear.h" | |
+#include "agg_rounded_rect.h" | |
namespace agg | |
{ | |
namespace svg | |
@@ -56,6 +74,7 @@ | |
+class gradient; | |
//============================================================================ | |
// Basic path attributes | |
@@ -64,6 +83,7 @@ | |
unsigned index; | |
rgba8 fill_color; | |
rgba8 stroke_color; | |
+ double opacity; | |
bool fill_flag; | |
bool stroke_flag; | |
bool even_odd_flag; | |
@@ -73,20 +93,26 @@ | |
double stroke_width; | |
trans_affine transform; | |
+ char stroke_url[64]; | |
+ char fill_url[64]; | |
+ | |
// Empty constructor | |
path_attributes() : | |
index(0), | |
fill_color(rgba(0,0,0)), | |
stroke_color(rgba(0,0,0)), | |
+ opacity(1.0), | |
fill_flag(true), | |
stroke_flag(false), | |
- even_odd_flag(false), | |
+ even_odd_flag(true), | |
line_join(miter_join), | |
line_cap(butt_cap), | |
miter_limit(4.0), | |
stroke_width(1.0), | |
transform() | |
{ | |
+ stroke_url[0] = 0; | |
+ fill_url[0] = 0; | |
} | |
// Copy constructor | |
@@ -94,6 +120,7 @@ | |
index(attr.index), | |
fill_color(attr.fill_color), | |
stroke_color(attr.stroke_color), | |
+ opacity(attr.opacity), | |
fill_flag(attr.fill_flag), | |
stroke_flag(attr.stroke_flag), | |
even_odd_flag(attr.even_odd_flag), | |
@@ -103,6 +130,8 @@ | |
stroke_width(attr.stroke_width), | |
transform(attr.transform) | |
{ | |
+ sprintf(stroke_url, "%s", attr.stroke_url); | |
+ sprintf(fill_url, "%s", attr.fill_url); | |
} | |
// Copy constructor with new index value | |
@@ -110,6 +139,7 @@ | |
index(idx), | |
fill_color(attr.fill_color), | |
stroke_color(attr.stroke_color), | |
+ opacity(attr.opacity), | |
fill_flag(attr.fill_flag), | |
stroke_flag(attr.stroke_flag), | |
even_odd_flag(attr.even_odd_flag), | |
@@ -119,6 +149,8 @@ | |
stroke_width(attr.stroke_width), | |
transform(attr.transform) | |
{ | |
+ sprintf(stroke_url, "%s", attr.stroke_url); | |
+ sprintf(fill_url, "%s", attr.fill_url); | |
} | |
}; | |
@@ -139,8 +171,14 @@ | |
typedef conv_transform<curved_count> curved_trans; | |
typedef conv_contour<curved_trans> curved_trans_contour; | |
+ typedef agg::span_allocator<rgba8> span_allocator_type; | |
+ | |
path_renderer(); | |
- | |
+ ~path_renderer() | |
+ { | |
+ for(size_t i = 0; i < m_gradients.size(); i++) | |
+ delete m_gradients[i]; | |
+ } | |
void remove_all(); | |
// Use these functions as follows: | |
@@ -165,6 +203,29 @@ | |
double x, double y, bool rel=false); | |
void curve4(double x2, double y2, // S, s | |
double x, double y, bool rel=false); | |
+ void elliptical_arc(double rx, double ry, | |
+ double angle, | |
+ bool large_arc_flag, | |
+ bool sweep_flag, | |
+ double x, double y, | |
+ bool rel = false); // A, a | |
+ void roundrect(double x1, double y1, // C, c | |
+ double x2, double y2,double rx, double ry,bool rel = false) | |
+ { | |
+ if(rel) | |
+ { | |
+ m_storage.rel_to_abs(&x1, &y1); | |
+ m_storage.rel_to_abs(&x2, &y2); | |
+ | |
+ } | |
+ agg::rounded_rect rc; | |
+ rc.rect(x1, y1, x2, y2); | |
+ rc.radius(rx,ry); | |
+ rc.normalize_radius(); | |
+ m_storage.concat_path(rc,0); | |
+ | |
+ | |
+ } | |
void close_subpath(); // Z, z | |
// template<class VertexSource> | |
@@ -189,7 +250,10 @@ | |
void even_odd(bool flag); | |
void stroke_width(double w); | |
void fill_none(); | |
+ void fill_url(const char* url); | |
void stroke_none(); | |
+ void stroke_url(const char* url); | |
+ void opacity(double op); | |
void fill_opacity(double op); | |
void stroke_opacity(double op); | |
void line_join(line_join_e join); | |
@@ -221,21 +285,79 @@ | |
agg::bounding_rect(trans, *this, 0, m_attr_storage.size(), x1, y1, x2, y2); | |
} | |
+ template<class Rasterizer, class Scanline, class RendererBase,class GradientFunction> | |
+ void render_gradient(Rasterizer& ras, | |
+ Scanline& sl, | |
+ RendererBase& rb, const trans_affine& mtx, | |
+ GradientFunction gradient_func, gradient::color_func_type &lut, int start,int end) | |
+ { | |
+ typedef agg::span_interpolator_linear<> interpolator_type; | |
+ interpolator_type span_interpolator(mtx); | |
+ | |
+ typedef agg::span_gradient<rgba8, | |
+ interpolator_type, | |
+ GradientFunction, | |
+ gradient::color_func_type> span_gradient_type; | |
+ | |
+ span_gradient_type span_gradient(span_interpolator, | |
+ gradient_func, | |
+ lut, | |
+ start, end); | |
+ | |
+ agg::render_scanlines_aa(ras, sl, rb, m_alloc, span_gradient); | |
+ | |
+ } | |
+ template<class Rasterizer, class Scanline, class RendererBase> | |
+ void render_gradient(Rasterizer& ras, | |
+ Scanline& sl, | |
+ RendererBase& rb, const char * gradient_url,double opaque) | |
+ { | |
+ if (gradient* g = find_gradient(gradient_url)) | |
+ { | |
+ gradient* gl = NULL; | |
+ if(g->count_colors() == 0) | |
+ { | |
+ std::string & xlink = g->xlink(); | |
+ gl = find_gradient(xlink.c_str() + 1); | |
+ } | |
+ trans_affine mtxgr = g->transform(); | |
+ mtxgr.multiply(m_transform); | |
+ mtxgr.invert(); | |
+ if(gl) | |
+ gl->set_opaque(opaque); | |
+ else | |
+ g->set_opaque(opaque); | |
+ gradient::color_func_type &lut = gl ? gl->lut() : g->lut(); | |
+ if(g->type() == gradient::GRADIENT_CIRCULAR) | |
+ { | |
+ gradient_circle gradient_func; | |
+ render_gradient(ras,sl,rb,mtxgr,gradient_func,lut, 0, gradient::lut_range); | |
+ } | |
+ else if(g->type() == gradient::GRADIENT_LINEAR) | |
+ { gradient_x gradient_func; | |
+ render_gradient(ras,sl,rb,mtxgr,gradient_func,lut,-gradient::lut_range,gradient::lut_range); | |
+ } | |
+ } | |
+ | |
+ } | |
// 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> | |
+ template<class Rasterizer, class Scanline, class RendererBase> | |
void render(Rasterizer& ras, | |
Scanline& sl, | |
- Renderer& ren, | |
+ RendererBase& rb, | |
const trans_affine& mtx, | |
const rect_i& cb, | |
double opacity=1.0) | |
{ | |
unsigned i; | |
+ typedef agg::renderer_scanline_aa_solid<RendererBase> renderer_solid; | |
+ renderer_solid ren(rb); | |
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++) | |
{ | |
@@ -262,11 +384,17 @@ | |
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.fill_url[0] != 0) | |
+ { | |
+ render_gradient(ras,sl,rb,attr.fill_url,attr.opacity); | |
+ } | |
+ else | |
+ { | |
+ color = attr.fill_color; | |
+ color.opacity(color.opacity() * opacity*attr.opacity); | |
+ ren.color(color); | |
+ agg::render_scanlines(ras, sl, ren); | |
+ } | |
} | |
if(attr.stroke_flag) | |
@@ -289,15 +417,29 @@ | |
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); | |
+ if(attr.stroke_url[0] != 0) | |
+ { | |
+ render_gradient(ras,sl,rb,attr.stroke_url,attr.opacity); | |
+ } | |
+ else | |
+ { | |
+ color = attr.stroke_color; | |
+ color.opacity(color.opacity() * opacity * attr.opacity); | |
+ ren.color(color); | |
+ agg::render_scanlines(ras, sl, ren); | |
+ } | |
+ | |
} | |
} | |
} | |
- | |
+ void start_gradient(bool radial = false); | |
+ void end_gradient(); | |
+ gradient* current_gradient() const | |
+ { return m_cur_gradient; } | |
private: | |
+ void add_gradient(gradient* gradient); | |
+ gradient* gradient_at(int32 index) const; | |
+ gradient* find_gradient(const char* name) const; | |
path_attributes& cur_attr(); | |
path_storage m_storage; | |
@@ -305,6 +447,10 @@ | |
attr_storage m_attr_stack; | |
trans_affine m_transform; | |
+ span_allocator_type m_alloc; | |
+ std::vector<gradient*> m_gradients; | |
+ gradient* m_cur_gradient; | |
+ | |
curved m_curved; | |
curved_count m_curved_count; | |
Index: svg_viewer/agg_svg_path_tokenizer.cpp | |
=================================================================== | |
--- svg_viewer/agg_svg_path_tokenizer.cpp (revision 50) | |
+++ svg_viewer/agg_svg_path_tokenizer.cpp (working copy) | |
@@ -12,7 +12,15 @@ | |
// mcseemagg@yahoo.com | |
// http://www.antigrain.com | |
//---------------------------------------------------------------------------- | |
+//change from haiku | |
// | |
+// Copyright 2006, Haiku. All rights reserved. | |
+// Distributed under the terms of the MIT License. | |
+// Authors: | |
+//Stephan Aßmus <superstippi@gmx.de> | |
+// | |
+// Adrien Destugues: Fix parsing of numbers in svg files : the code used obsolete atod | |
+//instead of strtod and led to numbers in the form 2.5e-4 to make the parsing fail as 'e' was interpreted as the end of the number. | |
// SVG path tokenizer. | |
// | |
//---------------------------------------------------------------------------- | |
@@ -116,22 +124,9 @@ | |
//------------------------------------------------------------------------ | |
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 | |
- while(buf_ptr < buf+255 && is_numeric(*m_path)) | |
- { | |
- *buf_ptr++ = *m_path++; | |
- } | |
- *buf_ptr = 0; | |
- m_last_number = atof(buf); | |
+ char* end; | |
+ m_last_number = strtod(m_path, &end); | |
+ m_path = end; | |
return true; | |
} | |
Index: svg_viewer/svg_test.cpp | |
=================================================================== | |
--- svg_viewer/svg_test.cpp (revision 50) | |
+++ svg_viewer/svg_test.cpp (working copy) | |
@@ -1,3 +1,20 @@ | |
+//---------------------------------------------------------------------------- | |
+// 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 | |
+//---------------------------------------------------------------------------- | |
+//^ | |
+// Gunnar Roth : pass base renderer instead of solid renderer to render function. | |
+ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include "agg_basics.h" | |
@@ -113,7 +130,7 @@ | |
m_path.expand(m_expand.value()); | |
start_timer(); | |
- m_path.render(ras, sl, ren, mtx, rb.clip_box(), 1.0); | |
+ m_path.render(ras, sl, rb, mtx, rb.clip_box(), 1.0); | |
double tm = elapsed_time(); | |
unsigned vertex_count = m_path.vertex_count(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment