Perpetrate SVG on the codebase
authorSam Moore <matches@ucc.asn.au>
Tue, 5 Aug 2014 18:46:41 +0000 (02:46 +0800)
committerSam Moore <matches@ucc.asn.au>
Tue, 5 Aug 2014 18:46:41 +0000 (02:46 +0800)
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!

src/Makefile
src/document.cpp
src/document.h
src/main.cpp

index 94b1f67..f0733d4 100644 (file)
@@ -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
index 80e0651..c97aa56 100644 (file)
@@ -1,6 +1,7 @@
 #include "document.h"
 #include "bezier.h"
 #include <cstdio>
+#include <fstream>
 
 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;
+       }
+}
index 39c6458..07dc33b 100644 (file)
@@ -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; }
index 22a1662..d7df87a 100644 (file)
@@ -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!");

UCC git Repository :: git.ucc.asn.au