Ok, just need to make the FloodFillOnCPU not stack overflow...
*.dat
bin/ipdf
data/*
+src/moc_controlpanel.cpp
# TODO: stb_truetype doesn't compile with some of these warnings.
CXX = g++ -std=gnu++0x -g -Wall -Werror -Wshadow -pedantic -rdynamic
MAIN = main.o
-OBJ = log.o real.o bezier.o document.o objectrenderer.o view.o screen.o vfpu.o quadtree.o graphicsbuffer.o framebuffer.o shaderprogram.o stb_truetype.o gl_core44.o add_digits_asm.o sub_digits_asm.o mul_digits_asm.o div_digits_asm.o arbint.o moc_controlpanel.o controlpanel.o group.o
+OBJ = log.o real.o bezier.o document.o objectrenderer.o view.o screen.o vfpu.o quadtree.o graphicsbuffer.o framebuffer.o shaderprogram.o stb_truetype.o gl_core44.o add_digits_asm.o sub_digits_asm.o mul_digits_asm.o div_digits_asm.o arbint.o moc_controlpanel.o controlpanel.o path.o
QT_INCLUDE := -I/usr/share/qt4/mkspecs/linux-g++-64 -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4 -I. -Itests -I.
QT_DEF := -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB
* In one coordinate direction
*/
-static pair<Real, Real> BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3)
+pair<Real, Real> BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3)
{
// straight line
if (p1 == p2 && p2 == p3)
return (*a) < (*b);
}
+/**
+ * Get top most *point* on Bezier curve
+ */
+pair<Real,Real> Bezier::GetTop() const
+{
+ pair<Real, Real> tsols = BezierTurningPoints(y0,y1,y2,y3);
+ Real tx0; Real ty0;
+ Real tx1; Real ty1;
+ Evaluate(tx0, ty0, tsols.first);
+ Evaluate(tx1, ty1, tsols.second);
+ vector<const Real*> v(4);
+ v[0] = &y0;
+ v[1] = &y3;
+ v[2] = &ty0;
+ v[3] = &ty1;
+ sort(v.begin(), v.end(), CompRealByPtr);
+ pair<Real,Real> result;
+ result.second = *v[0];
+ if (v[0] == &y0)
+ {
+ result.first = x0;
+ }
+ else if (v[0] == &y3)
+ {
+ result.first = x3;
+ }
+ else if (v[0] == &ty0)
+ {
+ result.first = tx0;
+ }
+ else if (v[0] == &ty1)
+ {
+ result.first = tx1;
+ }
+ return result;
+}
+
+/**
+ * Get bottom most *point* on Bezier curve
+ */
+pair<Real,Real> Bezier::GetBottom() const
+{
+ pair<Real, Real> tsols = BezierTurningPoints(y0,y1,y2,y3);
+ Real tx0; Real ty0;
+ Real tx1; Real ty1;
+ Evaluate(tx0, ty0, tsols.first);
+ Evaluate(tx1, ty1, tsols.second);
+ vector<const Real*> v(4);
+ v[0] = &y0;
+ v[1] = &y3;
+ v[2] = &ty0;
+ v[3] = &ty1;
+ sort(v.begin(), v.end(), CompRealByPtr);
+ pair<Real,Real> result;
+ result.second = *v[3];
+ if (v[3] == &y0)
+ {
+ result.first = x0;
+ }
+ else if (v[3] == &y3)
+ {
+ result.first = x3;
+ }
+ else if (v[3] == &ty0)
+ {
+ result.first = tx0;
+ }
+ else if (v[3] == &ty1)
+ {
+ result.first = tx1;
+ }
+ return result;
+}
+
+/**
+ * Get left most *point* on Bezier curve
+ */
+pair<Real,Real> Bezier::GetLeft() const
+{
+ pair<Real, Real> tsols = BezierTurningPoints(x0,x1,x2,x3);
+ Real tx0; Real ty0;
+ Real tx1; Real ty1;
+ Evaluate(tx0, ty0, tsols.first);
+ Evaluate(tx1, ty1, tsols.second);
+ vector<const Real*> v(4);
+ v[0] = &x0;
+ v[1] = &x3;
+ v[2] = &tx0;
+ v[3] = &tx1;
+ sort(v.begin(), v.end(), CompRealByPtr);
+ pair<Real,Real> result;
+ result.first = *v[0];
+ if (v[0] == &x0)
+ {
+ result.second = y0;
+ }
+ else if (v[0] == &x3)
+ {
+ result.second = y3;
+ }
+ else if (v[0] == &tx0)
+ {
+ result.second = ty0;
+ }
+ else if (v[0] == &tx1)
+ {
+ result.second = ty1;
+ }
+ return result;
+}
+
+
+/**
+ * Get left most *point* on Bezier curve
+ */
+pair<Real,Real> Bezier::GetRight() const
+{
+ pair<Real, Real> tsols = BezierTurningPoints(x0,x1,x2,x3);
+ Real tx0; Real ty0;
+ Real tx1; Real ty1;
+ Evaluate(tx0, ty0, tsols.first);
+ Evaluate(tx1, ty1, tsols.second);
+ vector<const Real*> v(4);
+ v[0] = &x0;
+ v[1] = &x3;
+ v[2] = &tx0;
+ v[3] = &tx1;
+ sort(v.begin(), v.end(), CompRealByPtr);
+ pair<Real,Real> result;
+ result.first = *v[3];
+ if (v[3] == &x0)
+ {
+ result.second = y0;
+ }
+ else if (v[3] == &x3)
+ {
+ result.second = y3;
+ }
+ else if (v[3] == &tx0)
+ {
+ result.second = ty0;
+ }
+ else if (v[3] == &tx1)
+ {
+ result.second = ty1;
+ }
+ return result;
+}
+
/**
* Get Bounds Rectangle of Bezier
*/
extern int Factorial(int n);
extern int BinomialCoeff(int n, int k);
extern Real Bernstein(int k, int n, const Real & u);
+ extern std::pair<Real,Real> BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3);
inline std::pair<Real,Real> SolveQuadratic(const Real & a, const Real & b, const Real & c)
{
Rect SolveBounds() const;
+ std::pair<Real,Real> GetTop() const;
+ std::pair<Real,Real> GetBottom() const;
+ std::pair<Real,Real> GetLeft() const;
+ std::pair<Real,Real> GetRight() const;
+
Bezier ToAbsolute(const Rect & bounds) const
{
return Bezier(*this, bounds);
x = x0*coeff[0] + x1*coeff[1] + x2*coeff[2] + x3*coeff[3];
y = y0*coeff[0] + y1*coeff[1] + y2*coeff[2] + y3*coeff[3];
}
+
+ bool operator==(const Bezier & equ) const
+ {
+ return (x0 == equ.x0 && y0 == equ.y0
+ && x1 == equ.x1 && y1 == equ.y1
+ && x2 == equ.x2 && y2 == equ.y2
+ && x3 == equ.x3 && y3 == equ.y3);
+ }
+ bool operator!=(const Bezier & equ) const {return !this->operator==(equ);}
};
LoadStructVector<Bezier>(file, chunk_size/sizeof(Bezier), m_objects.beziers);
break;
- case CT_OBJGROUPS:
- Debug("Group data...");
+ case CT_OBJPATHS:
+ Debug("Path data...");
Warn("Not handled because lazy");
break;
}
#endif
}
-unsigned Document::AddGroup(unsigned start_index, unsigned end_index, const Colour & fill)
+unsigned Document::AddPath(unsigned start_index, unsigned end_index, const Colour & fill)
{
- Real xmin = 0; Real ymin = 0;
- Real xmax = 0; Real ymax = 0;
-
- for (unsigned i = start_index; i <= end_index; ++i)
- {
- Rect & objb = m_objects.bounds[i];
-
- if (i == start_index || objb.x < xmin)
- xmin = objb.x;
- if (i == start_index || (objb.x+objb.w) > xmax)
- xmax = (objb.x+objb.w);
-
- if (i == start_index || objb.y < ymin)
- ymin = objb.y;
- if (i == start_index || (objb.y+objb.h) > ymax)
- ymax = (objb.y+objb.h);
- }
-
- Rect bounds(xmin,ymin, xmax-xmin, ymax-ymin);
-
- Group group(start_index, end_index, 0U, fill);
-
- unsigned data_index = AddGroupData(group);
- unsigned result = Add(GROUP, bounds,data_index);
+ Path path(m_objects, start_index, end_index, fill);
+ unsigned data_index = AddPathData(path);
+ Rect bounds = path.SolveBounds(m_objects);
+ unsigned result = Add(PATH, bounds,data_index);
return result;
}
{
Rect bounds = bezier.SolveBounds();
Bezier data = bezier.ToRelative(bounds); // Relative
+ if (data.ToAbsolute(bounds) != bezier)
+ {
+ Error("%s != %s", data.ToAbsolute(Rect(0,0,1,1)).Str().c_str(),
+ bezier.Str().c_str());
+ Fatal("ToAbsolute on ToRelative does not give original Bezier");
+ }
unsigned index = AddBezierData(data);
return Add(BEZIER, bounds, index);
}
return m_objects.beziers.size()-1;
}
-unsigned Document::AddGroupData(const Group & group)
+unsigned Document::AddPathData(const Path & path)
{
- m_objects.groups.push_back(group);
- return m_objects.groups.size()-1;
+ m_objects.paths.push_back(path);
+ return m_objects.paths.size()-1;
}
void Document::DebugDumpObjects()
}
Debug("fill-opacity is %f", Float(c.a));
- AddGroup(range.first, range.second, c);
+ AddPath(range.first, range.second, c);
}
}
if (start_index < m_count && end_index < m_count)
{
- AddGroup(start_index, end_index);
+ AddPath(start_index, end_index);
}
Debug("Added Glyph \"%c\" at %f %f, scale %f", (char)character, Float(x), Float(y), Float(scale));
bool operator==(const Document & equ) const;
bool operator!=(const Document & equ) const {return !(this->operator==(equ));}
- unsigned AddGroup(unsigned start_index, unsigned end_index, const Colour & shading=Colour(0.6,0.6,0.6,1));
+ unsigned AddPath(unsigned start_index, unsigned end_index, const Colour & shading=Colour(0.6,0.6,0.6,1));
unsigned AddBezier(const Bezier & bezier);
unsigned Add(ObjectType type, const Rect & bounds, unsigned data_index = 0);
unsigned AddBezierData(const Bezier & bezier);
- unsigned AddGroupData(const Group & group);
+ unsigned AddPathData(const Path & path);
/** SVG Related functions **/
+++ /dev/null
-#include "group.h"
-using namespace std;
-
-namespace IPDF
-{
-
-
-}
+++ /dev/null
-#ifndef _GROUP_H
-#define _GROUP_H
-
-#include <vector>
-#include <algorithm>
-
-namespace IPDF
-{
-
- struct Colour
- {
- float r; float g; float b; float a;
- Colour() = default;
- Colour(float _r, float _g, float _b, float _a) : r(_r), g(_g), b(_b), a(_a) {}
- };
-
- class Objects;
-
- struct Group
- {
- Group(unsigned _start, unsigned _end, unsigned _index, const Colour & _fill = Colour(0.8,0.8,0.8,1))
- : m_start(_start), m_end(_end), m_index(_index), m_fill(_fill)
- {
-
- }
- unsigned m_start;
- unsigned m_end;
- unsigned m_index;
- Colour m_fill;
- };
-
-}
-#endif //_GROUP_H
#include "bezier.h"
#include "rect.h"
-#include "group.h"
+#include "path.h"
namespace IPDF
{
RECT_FILLED,
RECT_OUTLINE,
BEZIER,
- GROUP,
+ PATH,
NUMBER_OF_OBJECT_TYPES
} ObjectType;
CT_OBJBOUNDS,
CT_OBJINDICES,
CT_OBJBEZIERS,
- CT_OBJGROUPS
+ CT_OBJPATHS
};
struct Objects
std::vector<unsigned> data_indices;
/** Used by BEZIER only **/
std::vector<Bezier> beziers; // bezier curves - look up by data_indices
- /** Used by GROUP only **/
- std::vector<Group> groups;
+ /** Used by PATH only **/
+ std::vector<Path> paths;
};
class View;
#include "objectrenderer.h"
#include "view.h"
-#include <list>
+#include <vector>
using namespace std;
if (m_indexes[i] < first_obj_id) continue;
if (m_indexes[i] >= last_obj_id) continue;
PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
+ FloodFillOnCPU(bounds.x+1, bounds.y+1, bounds, target, Colour(0,0,0,1));
+ /*
for (int64_t x = max((int64_t)0, bounds.x); x <= min(bounds.x+bounds.w, target.w-1); ++x)
{
for (int64_t y = max((int64_t)0, bounds.y); y <= min(bounds.y+bounds.h, target.h-1); ++y)
target.pixels[index+3] = 255;
}
}
+ */
}
}
result.h *= Real(target.h);
return result;
}
+
+pair<int64_t, int64_t> ObjectRenderer::CPUPointLocation(const pair<Real, Real> & point, const View & view, const CPURenderTarget & target)
+{
+ // hack...
+ Rect result = view.TransformToViewCoords(Rect(point.first, point.second,1,1));
+ int64_t x = result.x*target.w;
+ int64_t y = result.y*target.h;
+ return pair<int64_t, int64_t>(x,y);
+}
/**
glDrawElements(GL_LINES, (last_index-first_index)*2, GL_UNSIGNED_INT, (GLvoid*)(2*first_index*sizeof(uint32_t)));
}
-inline bool IsBlack(uint8_t * pixels, int64_t index)
-{
- bool result = (pixels[index+0] == 0 && pixels[index+1] == 0 && pixels[index+2] == 0 && pixels[index+3] == 255);
- //pixels[index+3] = 254; // hax
- return result;
-}
+
/**
- * Render Group (shading)
+ * Render Path (shading)
*/
-void GroupRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
+void PathRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
{
if (!view.ShowingObjectBounds() && !view.PerformingShading())
return;
Rect bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
PixelBounds pix_bounds(bounds);
-
- const Group & group = objects.groups[objects.data_indices[m_indexes[i]]];
- if (group.m_fill.a == 0 || !view.PerformingShading())
- continue;
-
- // make the bounds just a little bit bigger
pix_bounds.x-=1;
pix_bounds.w+=2;
pix_bounds.y-=1;
pix_bounds.h+=2;
-
- // Attempt to shade the region
- // Assumes the outline has been drawn first...
- //#ifdef SHADING_DUMB
- for (int64_t y = max((int64_t)0, pix_bounds.y); y <= min(pix_bounds.y+pix_bounds.h, target.h-1); ++y)
- {
- struct Segment
- {
- int64_t first;
- int64_t second;
- bool all_black;
- };
- list<Segment> segments;
- int64_t min_x = max((int64_t)0, pix_bounds.x);
- int64_t max_x = min(pix_bounds.x+pix_bounds.w, target.w-1);
- int64_t yy = y*target.w;
+ const Path & path = objects.paths[objects.data_indices[m_indexes[i]]];
+ if (path.m_fill.a == 0 || !view.PerformingShading())
+ continue;
- int64_t x = min_x;
- while (x <= max_x)
- {
- bool start_black = IsBlack(target.pixels, 4*(x+yy));
- bool black = start_black;
- segments.push_back({x,x,start_black});
- while (black == start_black && ++x <= max_x)
- {
- black = IsBlack(target.pixels, 4*(x+yy));
- }
- segments.back().second = x-1;
- }
-
- // Keep only the interior segments
- list<Segment>::iterator j = segments.begin();
- //TODO: Magically delete unneeded segments here...
-
- // Fill in remaining segments
- for (j=segments.begin(); j != segments.end(); ++j)
- {
- Colour c(group.m_fill);
- if (j->all_black)
- {
- c.r = 1;//1; // Change to debug the outline scanning
- c.g = 0;
- c.b = 0;
- c.a = 1;
- }
- for (x = max(min_x, j->first); x <= min(max_x, j->second); ++x)
- {
- int64_t index = 4*(x+yy);
- target.pixels[index+0] = 255*c.r;
- target.pixels[index+1] = 255*c.g;
- target.pixels[index+2] = 255*c.b;
- target.pixels[index+3] = 255*c.a;
- }
- }
- }
- //#endif //SHADING_DUMB
+
+ pair<int64_t,int64_t> top(CPUPointLocation(path.m_top, view, target));
+ pair<int64_t,int64_t> bottom(CPUPointLocation(path.m_bottom, view, target));
+ pair<int64_t,int64_t> left(CPUPointLocation(path.m_left, view, target));
+ pair<int64_t,int64_t> right(CPUPointLocation(path.m_right, view, target));
+ FloodFillOnCPU(top.first, top.second+1, pix_bounds, target, path.m_fill);
+ FloodFillOnCPU(bottom.first, bottom.second-1, pix_bounds, target, path.m_fill);
+ FloodFillOnCPU(left.first+1, left.second, pix_bounds, target, path.m_fill);
+ FloodFillOnCPU(right.first-1, right.second, pix_bounds, target, path.m_fill);
+
if (view.ShowingObjectBounds())
{
- const Colour & c = group.m_fill;
+ Colour c(0,0,1,1);
+ RenderLineOnCPU(top.first, top.second, bottom.first, bottom.second, target, c);
+ RenderLineOnCPU(left.first, left.second, right.first, right.second, target, c);
ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y, target, c);
ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y+pix_bounds.h, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, c);
ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x, pix_bounds.y+pix_bounds.h, target, c);
} while (++x <= x_end);
}
+void ObjectRenderer::FloodFillOnCPU(int64_t x, int64_t y, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill)
+{
+ if (x < 0 || x < bounds.x || x > bounds.x+bounds.w || x >= target.w)
+ return;
+ if (y < 0 || y < bounds.y || y > bounds.y+bounds.h || y >= target.h)
+ return;
+
+ if (GetColour(target, x, y) != Colour(1,1,1,1))
+ return;
+
+ SetColour(target, x, y, fill);
+ FloodFillOnCPU(x-1, y, bounds, target, fill);
+ FloodFillOnCPU(x+1, y, bounds, target, fill);
+ FloodFillOnCPU(x,y-1,bounds,target,fill);
+ FloodFillOnCPU(x,y+1,bounds,target,fill);
+
+}
+
}
uint8_t * pixels;
int64_t w;
int64_t h;
+
+
+
};
+
+ static Colour GetColour(const CPURenderTarget & target, int64_t x, int64_t y)
+ {
+ int64_t index = 4*(x+y*target.w);
+ return Colour(Real(target.pixels[index+0])/Real(255),
+ Real(target.pixels[index+1])/Real(255),
+ Real(target.pixels[index+2])/Real(255),
+ Real(target.pixels[index+3])/Real(255));
+ }
+
+ static void SetColour(const CPURenderTarget & target, int64_t x, int64_t y, const Colour & c)
+ {
+ int64_t index = 4*(x+y*target.w);
+ target.pixels[index+0] = c.r*255;
+ target.pixels[index+1] = c.g*255;
+ target.pixels[index+2] = c.b*255;
+ target.pixels[index+3] = c.a*255;
+ }
+
struct PixelBounds
{
int64_t x; int64_t y; int64_t w; int64_t h;
};
static Rect CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target);
-
+ static std::pair<int64_t, int64_t> CPUPointLocation(const std::pair<Real, Real> & point, const View & view, const CPURenderTarget & target);
static void SaveBMP(const CPURenderTarget & target, const char * filename);
/** Helper for CPU rendering that will render a line using Bresenham's algorithm. Do not use the transpose argument. **/
static void RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t y1, const CPURenderTarget & target, const Colour & colour = Colour(0,0,0,1), bool transpose = false);
+
+ static void FloodFillOnCPU(int64_t x0, int64_t y0, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill);
ShaderProgram m_shader_program; /** GLSL shaders for GPU **/
GraphicsBuffer m_ibo; /** Index Buffer Object for GPU rendering **/
};
- /** Renderer for filled circles **/
- class GroupRenderer : public ObjectRenderer
+ /** Renderer for filled paths **/
+ class PathRenderer : public ObjectRenderer
{
public:
- GroupRenderer() : ObjectRenderer(GROUP, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/rect_outline_geom.glsl") {}
- virtual ~GroupRenderer() {}
+ PathRenderer() : ObjectRenderer(PATH, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/rect_outline_geom.glsl") {}
+ virtual ~PathRenderer() {}
virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id);
// do nothing on GPU
virtual void RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id) {}
--- /dev/null
+#include "ipdf.h"
+#include "path.h"
+using namespace std;
+
+namespace IPDF
+{
+
+Path::Path(const Objects & objects, unsigned start, unsigned end, const Colour & fill)
+ : m_start(start), m_end(end), m_fill(fill)
+{
+ Real xmin = 0; Real ymin = 0;
+ Real xmax = 0; Real ymax = 0;
+
+ // Find the bounds coordinates
+ // and identify the top left and bottom right objects
+
+ unsigned left;
+ unsigned right;
+ unsigned top;
+ unsigned bottom;
+
+ for (unsigned i = m_start; i <= m_end; ++i)
+ {
+ const Rect & objb = objects.bounds[i];
+
+ if (i == m_start || objb.x < xmin)
+ {
+ xmin = objb.x;
+ left = i;
+ }
+ if (i == m_start || (objb.x+objb.w) > xmax)
+ {
+ xmax = (objb.x+objb.w);
+ right = i;
+ }
+
+ if (i == m_start || objb.y < ymin)
+ {
+ ymin = objb.y;
+ top = i;
+ }
+ if (i == m_start || (objb.y+objb.h) > ymax)
+ {
+ ymax = (objb.y+objb.h);
+ bottom = i;
+ }
+ }
+
+ // Get actual turning point coords of the 4 edge case beziers
+ m_top = objects.beziers[objects.data_indices[top]].ToAbsolute(objects.bounds[top]).GetTop();
+ m_bottom = objects.beziers[objects.data_indices[bottom]].ToAbsolute(objects.bounds[bottom]).GetBottom();
+ m_left = objects.beziers[objects.data_indices[left]].ToAbsolute(objects.bounds[left]).GetLeft();
+ m_right = objects.beziers[objects.data_indices[right]].ToAbsolute(objects.bounds[right]).GetRight();
+ /*Debug("Top: %f, %f", m_top.first, m_top.second);
+ Debug("Bottom: %f, %f", m_bottom.first, m_bottom.second);
+ Debug("Left: %f, %f", m_left.first, m_left.second);
+ Debug("Right: %f, %f", m_right.first, m_right.second);
+ Debug("Left - Right: %f, %f", m_right.first - m_left.first, m_right.second - m_left.second);
+ Debug("Top - Bottom: %f, %f", m_top.first - m_bottom.first, m_top.second - m_bottom.second);
+ */
+}
+
+Rect Path::SolveBounds(const Objects & objects) const
+{
+ return Rect(m_left.first, m_top.second, m_right.first-m_left.first, m_bottom.second-m_top.second);
+}
+
+}
--- /dev/null
+#ifndef _PATH_H
+#define _PATH_H
+
+#include <vector>
+#include <algorithm>
+#include "rect.h"
+#include "real.h"
+
+namespace IPDF
+{
+
+ struct Colour
+ {
+ float r; float g; float b; float a;
+ Colour() = default;
+ Colour(float _r, float _g, float _b, float _a) : r(_r), g(_g), b(_b), a(_a) {}
+ bool operator==(const Colour & c) const
+ {
+ return (r == c.r && g == c.g && b == c.b && a == c.a);
+ }
+ bool operator!=(const Colour & c) const {return !this->operator==(c);}
+ };
+
+ class Objects;
+
+ struct Path
+ {
+ Path(const Objects & objects, unsigned _start, unsigned _end, const Colour & _fill = Colour(0.8,0.8,0.8,1));
+
+ Rect SolveBounds(const Objects & objects) const;
+
+
+
+ unsigned m_start; // First bounding Bezier index
+ unsigned m_end; // Last (inclusive) '' ''
+ unsigned m_index; // index into Objects array
+
+ std::pair<Real,Real> m_top;
+ std::pair<Real,Real> m_bottom;
+ std::pair<Real,Real> m_left;
+ std::pair<Real,Real> m_right;
+
+ Colour m_fill; // colour to fill with
+ };
+
+}
+#endif //_PATH_H
m_object_renderers[RECT_OUTLINE] = new RectOutlineRenderer();
m_object_renderers[CIRCLE_FILLED] = new CircleFilledRenderer();
m_object_renderers[BEZIER] = new BezierRenderer();
- m_object_renderers[GROUP] = new GroupRenderer();
+ m_object_renderers[PATH] = new PathRenderer();
// To add rendering for a new type of object;
// 1. Add enum to ObjectType in ipdf.h