From: Sam Moore Date: Tue, 26 Aug 2014 10:42:33 +0000 (+0800) Subject: A Song of Floodfills and Segfaults X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=commitdiff_plain;h=3172dd5af487e0f8a6e5cd5439dea594b9cbd7c9 A Song of Floodfills and Segfaults Ok, just need to make the FloodFillOnCPU not stack overflow... --- diff --git a/.gitignore b/.gitignore index 28b8762..7973e79 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ *.dat bin/ipdf data/* +src/moc_controlpanel.cpp diff --git a/src/Makefile b/src/Makefile index af72dfa..cec94f2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ ARCH := $(shell uname -m) # 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 diff --git a/src/bezier.cpp b/src/bezier.cpp index b3129e4..1c83660 100644 --- a/src/bezier.cpp +++ b/src/bezier.cpp @@ -52,7 +52,7 @@ Real Bernstein(int k, int n, const Real & u) * In one coordinate direction */ -static pair BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3) +pair BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3) { // straight line if (p1 == p2 && p2 == p3) @@ -90,6 +90,155 @@ inline bool CompRealByPtr(const Real * a, const Real * b) return (*a) < (*b); } +/** + * Get top most *point* on Bezier curve + */ +pair Bezier::GetTop() const +{ + pair 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 v(4); + v[0] = &y0; + v[1] = &y3; + v[2] = &ty0; + v[3] = &ty1; + sort(v.begin(), v.end(), CompRealByPtr); + pair 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 Bezier::GetBottom() const +{ + pair 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 v(4); + v[0] = &y0; + v[1] = &y3; + v[2] = &ty0; + v[3] = &ty1; + sort(v.begin(), v.end(), CompRealByPtr); + pair 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 Bezier::GetLeft() const +{ + pair 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 v(4); + v[0] = &x0; + v[1] = &x3; + v[2] = &tx0; + v[3] = &tx1; + sort(v.begin(), v.end(), CompRealByPtr); + pair 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 Bezier::GetRight() const +{ + pair 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 v(4); + v[0] = &x0; + v[1] = &x3; + v[2] = &tx0; + v[3] = &tx1; + sort(v.begin(), v.end(), CompRealByPtr); + pair 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 */ diff --git a/src/bezier.h b/src/bezier.h index 17bc5b6..99530b3 100644 --- a/src/bezier.h +++ b/src/bezier.h @@ -11,6 +11,7 @@ namespace IPDF 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 BezierTurningPoints(const Real & p0, const Real & p1, const Real & p2, const Real & p3); inline std::pair SolveQuadratic(const Real & a, const Real & b, const Real & c) { @@ -122,6 +123,11 @@ namespace IPDF Rect SolveBounds() const; + std::pair GetTop() const; + std::pair GetBottom() const; + std::pair GetLeft() const; + std::pair GetRight() const; + Bezier ToAbsolute(const Rect & bounds) const { return Bezier(*this, bounds); @@ -320,6 +326,15 @@ namespace IPDF 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);} }; diff --git a/src/document.cpp b/src/document.cpp index 0b72a3e..6c39079 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -295,8 +295,8 @@ void Document::Load(const string & filename) LoadStructVector(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; } @@ -310,32 +310,12 @@ void Document::Load(const string & filename) #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; } @@ -346,6 +326,12 @@ unsigned Document::AddBezier(const Bezier & bezier) { 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); } @@ -364,10 +350,10 @@ unsigned Document::AddBezierData(const Bezier & bezier) 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() @@ -603,7 +589,7 @@ void Document::ParseSVGNode(pugi::xml_node & root, SVGMatrix & parent_transform) } Debug("fill-opacity is %f", Float(c.a)); - AddGroup(range.first, range.second, c); + AddPath(range.first, range.second, c); } } @@ -1008,7 +994,7 @@ void Document::AddFontGlyphAtPoint(stbtt_fontinfo *font, int character, Real sca 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)); diff --git a/src/document.h b/src/document.h index cab7684..9169d8a 100644 --- a/src/document.h +++ b/src/document.h @@ -52,11 +52,11 @@ namespace IPDF 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 **/ diff --git a/src/group.cpp b/src/group.cpp deleted file mode 100644 index 8407a4b..0000000 --- a/src/group.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "group.h" -using namespace std; - -namespace IPDF -{ - - -} diff --git a/src/group.h b/src/group.h deleted file mode 100644 index f72369b..0000000 --- a/src/group.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _GROUP_H -#define _GROUP_H - -#include -#include - -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 diff --git a/src/ipdf.h b/src/ipdf.h index f9d3a96..633c8ef 100644 --- a/src/ipdf.h +++ b/src/ipdf.h @@ -6,7 +6,7 @@ #include "bezier.h" #include "rect.h" -#include "group.h" +#include "path.h" namespace IPDF { @@ -26,7 +26,7 @@ namespace IPDF RECT_FILLED, RECT_OUTLINE, BEZIER, - GROUP, + PATH, NUMBER_OF_OBJECT_TYPES } ObjectType; @@ -37,7 +37,7 @@ namespace IPDF CT_OBJBOUNDS, CT_OBJINDICES, CT_OBJBEZIERS, - CT_OBJGROUPS + CT_OBJPATHS }; struct Objects @@ -49,8 +49,8 @@ namespace IPDF std::vector data_indices; /** Used by BEZIER only **/ std::vector beziers; // bezier curves - look up by data_indices - /** Used by GROUP only **/ - std::vector groups; + /** Used by PATH only **/ + std::vector paths; }; class View; diff --git a/src/objectrenderer.cpp b/src/objectrenderer.cpp index 6108f4f..8234547 100644 --- a/src/objectrenderer.cpp +++ b/src/objectrenderer.cpp @@ -5,7 +5,7 @@ #include "objectrenderer.h" #include "view.h" -#include +#include using namespace std; @@ -122,6 +122,8 @@ void RectFilledRenderer::RenderUsingCPU(const Objects & objects, const View & vi 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) @@ -133,6 +135,7 @@ void RectFilledRenderer::RenderUsingCPU(const Objects & objects, const View & vi target.pixels[index+3] = 255; } } + */ } } @@ -209,6 +212,15 @@ Rect ObjectRenderer::CPURenderBounds(const Rect & bounds, const View & view, con result.h *= Real(target.h); return result; } + +pair ObjectRenderer::CPUPointLocation(const pair & 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(x,y); +} /** @@ -332,17 +344,12 @@ void BezierRenderer::RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id) 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; @@ -355,75 +362,29 @@ void GroupRenderer::RenderUsingCPU(const Objects & objects, const View & view, c 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 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::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 top(CPUPointLocation(path.m_top, view, target)); + pair bottom(CPUPointLocation(path.m_bottom, view, target)); + pair left(CPUPointLocation(path.m_left, view, target)); + pair 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); @@ -531,4 +492,22 @@ void ObjectRenderer::RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t } 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); + +} + } diff --git a/src/objectrenderer.h b/src/objectrenderer.h index 8f52aa0..66daafb 100644 --- a/src/objectrenderer.h +++ b/src/objectrenderer.h @@ -47,7 +47,29 @@ namespace IPDF 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; @@ -55,7 +77,7 @@ namespace IPDF }; static Rect CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target); - + static std::pair CPUPointLocation(const std::pair & point, const View & view, const CPURenderTarget & target); static void SaveBMP(const CPURenderTarget & target, const char * filename); @@ -73,6 +95,8 @@ namespace IPDF /** 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 **/ @@ -130,12 +154,12 @@ namespace IPDF }; - /** 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) {} diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 0000000..050453b --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,68 @@ +#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); +} + +} diff --git a/src/path.h b/src/path.h new file mode 100644 index 0000000..ac6956c --- /dev/null +++ b/src/path.h @@ -0,0 +1,47 @@ +#ifndef _PATH_H +#define _PATH_H + +#include +#include +#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 m_top; + std::pair m_bottom; + std::pair m_left; + std::pair m_right; + + Colour m_fill; // colour to fill with + }; + +} +#endif //_PATH_H diff --git a/src/view.cpp b/src/view.cpp index 48e0a5a..8f4a8a4 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -33,7 +33,7 @@ View::View(Document & document, Screen & screen, const Rect & bounds, const Colo 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