From: Sam Moore Date: Tue, 5 Aug 2014 18:46:41 +0000 (+0800) Subject: Perpetrate SVG on the codebase X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=commitdiff_plain;h=666fa863baba6345fa5cf66711e9149b5d06b825 Perpetrate SVG on the codebase Well, parts of it. We can have black and white SVG only. I think it's currently buggy. I figure we should probably be able to actually parse a document format. And SVG, despite being XML, seems like the least terrible. Using pugixml for XML parsing and horrible string tokenising because SVG is not really a DOM language because of the "d" attribute (-_-) Did I mention it was the least terrible? Also, there seem to be lots and lots of bugs with things, including Quadtree rendering of Beziers and actually just GPU rendering of more than one Bezier in general. Also I compiled atril without zoom limits but it crashes due to things not related to precision. So we're beating atril I guess. Woo! --- diff --git a/src/Makefile b/src/Makefile index 94b1f67..f0733d4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -26,23 +26,24 @@ MAINRPATH := $(MAINRPATH_$(ARCH)) TESTRPATH := $(TESTRPATH_$(ARCH)) CFLAGS := $(CFLAGS_$(ARCH)) -DEF = -DREAL=1 - +REALTYPE=1 LINKOBJ = $(OBJPATHS) RM = rm -f BIN = ../bin/ipdf -all : DEF = -DREAL=1 +all : REAL = 1 all : $(BIN) -single : DEF = -DREAL=0 +single : REAL = 0 single : $(BIN) -double : DEF = -DREAL=1 +double : REAL = 1 double : $(BIN) +DEF = -DREAL=$(REALTYPE) -DQUADTREE_REMOVED + demo : $(BIN) ../tools/stream_plot.py mkdir -p ../data/ $(RM) ../data/performance.dat diff --git a/src/document.cpp b/src/document.cpp index 80e0651..c97aa56 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1,6 +1,7 @@ #include "document.h" #include "bezier.h" #include +#include using namespace IPDF; using namespace std; @@ -218,3 +219,216 @@ bool Document::operator==(const Document & equ) const && memcmp(m_objects.data_indices.data(), equ.m_objects.data_indices.data(), ObjectCount() * sizeof(unsigned)) == 0 && memcmp(m_objects.beziers.data(), equ.m_objects.beziers.data(), m_objects.beziers.size() * sizeof(Bezier)) == 0); } + + +#include "../contrib/pugixml-1.4/src/pugixml.hpp" +#include "../contrib/pugixml-1.4/src/pugixml.cpp" + +void Document::LoadSVG(const string & filename, const Rect & bounds) +{ + using namespace pugi; + + xml_document doc_xml; + ifstream input(filename.c_str(), ios_base::in); + xml_parse_result result = doc_xml.load(input); + + if (!result) + Fatal("Couldn't load \"%s\" - %s", filename.c_str(), result.description()); + + Debug("Loaded XML - %s", result.description()); + + input.close(); + + // Combine all SVG tags into one thing because lazy + for (xml_node svg : doc_xml.children("svg")) + { + Real width = svg.attribute("width").as_float() * bounds.w; + Real height = svg.attribute("width").as_float() * bounds.h; + + + // Rectangles + Real coords[4]; + const char * attrib_names[] = {"x", "y", "width", "height"}; + for (pugi::xml_node rect : svg.children("rect")) + { + for (size_t i = 0; i < 4; ++i) + coords[i] = rect.attribute(attrib_names[i]).as_float(); + + bool outline = !(rect.attribute("fill")); + Add(outline?RECT_OUTLINE:RECT_FILLED, Rect(coords[0]/width + bounds.x, coords[1]/height + bounds.y, coords[2]/width, coords[3]/height),0); + Debug("Added rectangle"); + } + + // Circles + for (pugi::xml_node circle : svg.children("circle")) + { + Real cx = circle.attribute("cx").as_float(); + Real cy = circle.attribute("cy").as_float(); + Real r = circle.attribute("r").as_float(); + + Real x = (cx - r)/width + bounds.x; + Real y = (cy - r)/height + bounds.y; + Real w = 2*r/width; + Real h = 2*r/height; + + Rect rect(x,y,w,h); + Add(CIRCLE_FILLED, rect,0); + Debug("Added Circle %s", rect.Str().c_str()); + + } + + // paths + for (pugi::xml_node path : svg.children("path")) + { + + string d = path.attribute("d").as_string(); + Debug("Path data attribute is \"%s\"", d.c_str()); + AddPathFromString(d, Rect(bounds.x,bounds.y,width,height)); + + } + } + + //Fatal("Done"); + + + +} + +// Behold my amazing tokenizing abilities +static string & GetToken(const string & d, string & token, unsigned & i) +{ + token.clear(); + while (i < d.size() && iswspace(d[i])) + { + ++i; + } + + while (i < d.size()) + { + if (d[i] == ',' || isalpha(d[i]) || iswspace(d[i])) + { + if (token.size() == 0 && !iswspace(d[i])) + { + token += d[i++]; + } + break; + } + token += d[i++]; + } + Debug("Got token \"%s\"", token.c_str()); + return token; +} + + +// Fear the wrath of the tokenizing svg data +// Seriously this isn't really very DOM-like at all is it? +void Document::AddPathFromString(const string & d, const Rect & bounds) +{ + Real x[3] = {0,0,0}; + Real y[3] = {0,0,0}; + + string token(""); + string command("m"); + + unsigned i = 0; + unsigned prev_i = 0; + Real x0; + Real y0; + bool started = false; + while (i < d.size() && GetToken(d, token, i).size() > 0) + { + if (isalpha(token[0])) + command = token; + else + { + i = prev_i; // hax + if(command == "") + command = "l"; + } + + if (command == "m") + { + Debug("Construct moveto command"); + x[0] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.w; + assert(GetToken(d,token,i) == ","); + y[0] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.h; + if (!started) + { + x0 = x[0]; + y0 = y[0]; + started = true; + } + Debug("mmoveto %f,%f", Float(x[0]),Float(y[0])); + command = "l"; + } + else if (command == "c") + { + Debug("Construct curveto command"); + x[0] = strtod(GetToken(d,token,i).c_str(),NULL)/bounds.w; + assert(GetToken(d,token,i) == ","); + y[0] = strtod(GetToken(d,token,i).c_str(),NULL)/bounds.h; + + x[1] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.w; + assert(GetToken(d,token,i) == ","); + y[1] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.h; + + x[2] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.w; + assert(GetToken(d,token,i) == ","); + y[2] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.h; + + unsigned index = AddBezierData(Bezier(x[0],y[0],x[1],y[1],x[2],y[2])); + Add(BEZIER,bounds,index); + + + Debug("[%u] curveto %f,%f %f,%f %f,%f", index, Float(x[0]),Float(y[0]),Float(x[1]),Float(y[1]),Float(x[2]),Float(y[2])); + + x[0] = x[2]; + y[0] = y[2]; + + + } + else if (command == "l") + { + Debug("Construct lineto command"); + + x[1] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.w; + assert(GetToken(d,token,i) == ","); + y[1] = strtod(GetToken(d,token,i).c_str(),NULL) / bounds.h; + + x[2] = x[1]; + y[2] = y[1]; + + unsigned index = AddBezierData(Bezier(x[0],y[0],x[1],y[1],x[2],y[2])); + Add(BEZIER,bounds,index); + + Debug("[%u] lineto %f,%f %f,%f", index, Float(x[0]),Float(y[0]),Float(x[1]),Float(y[1])); + + x[0] = x[2]; + y[0] = y[2]; + + } + else if (command == "z") + { + Debug("Construct returnto command"); + x[1] = x0; + y[1] = y0; + x[2] = x0; + y[2] = y0; + + unsigned index = AddBezierData(Bezier(x[0],y[0],x[1],y[1],x[2],y[2])); + Add(BEZIER,bounds,index); + + Debug("[%u] returnto %f,%f %f,%f", index, Float(x[0]),Float(y[0]),Float(x[1]),Float(y[1])); + + x[0] = x[2]; + y[0] = y[2]; + command = "m"; + } + else + { + Warn("Unrecognised command \"%s\", set to \"m\"", command.c_str()); + command = "m"; + } + prev_i = i; + } +} diff --git a/src/document.h b/src/document.h index 39c6458..07dc33b 100644 --- a/src/document.h +++ b/src/document.h @@ -11,6 +11,8 @@ namespace IPDF public: Document(const std::string & filename = "") : m_objects(), m_count(0) {Load(filename);} virtual ~Document() {} + + void LoadSVG(const std::string & filename, const Rect & bounds = {0,0,1,1}); void Load(const std::string & filename = ""); void Save(const std::string & filename); @@ -24,6 +26,8 @@ namespace IPDF void Add(ObjectType type, const Rect & bounds, unsigned data_index = 0); unsigned AddBezierData(const Bezier & bezier); + + void AddPathFromString(const std::string & d, const Rect & bounds); #ifndef QUADTREE_DISABLED inline const QuadTree& GetQuadTree() { if (m_quadtree.root_id == QUADTREE_EMPTY) { GenBaseQuadtree(); } return m_quadtree; } diff --git a/src/main.cpp b/src/main.cpp index 22a1662..d7df87a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,14 +77,14 @@ int main(int argc, char ** argv) if (input_filename != NULL) { - doc.Load(input_filename); + doc.LoadSVG(input_filename); } else { doc.AddBezierData(Bezier(0,0,0,1,1,0)); - //doc.AddBezierData(Bezier(0,0,1,0,0,1)); - //doc.AddBezierData(Bezier(0,0,1,1,1,0)); - //doc.AddBezierData(Bezier(0,1,1,0,0,1)); + doc.AddBezierData(Bezier(0,0,1,0,0,1)); + doc.AddBezierData(Bezier(0,0,1,1,1,0)); + doc.AddBezierData(Bezier(0,1,1,0,0,1)); @@ -97,7 +97,10 @@ int main(int argc, char ** argv) //doc.Add(BEZIER, Rect(0.2+x-4.0, 0.2+y-4.0, 0.6,0.6), (x^y)%3); } } - doc.Add(RECT_OUTLINE, Rect(0.1,0.1,0.8,0.8), 0); + doc.Add(BEZIER, Rect(0.1,0.1,0.8,0.8), 0); + doc.Add(BEZIER, Rect(0.1,0.1,0.8,0.8), 1); + doc.Add(BEZIER, Rect(0.1,0.1,0.8,0.8), 2); + doc.Add(BEZIER, Rect(0.1,0.1,0.8,0.8), 3); //doc.Add(CIRCLE_FILLED, Rect(0.1,0.1,0.8,0.8), 0); } Debug("Start!");