From e7066887c0d142ddef87ec9ae07ef08ff31573dc Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Tue, 2 Sep 2014 18:43:40 +0800 Subject: [PATCH] Compositing on CPU sort of kind of works if we ignore Alpha* *Alpha is hard and I'm just going to ignore it. Hacky fix to "overflood" problem where the fill point is outside the boundary; since it can't be more than 1 pixel outside, check the neighbours before starting the fill. Unfortunately this means if the fill point is only one pixel *inside* the boundary, there is no fill. Tried brute force approach for low resolution paths, but that doesn't really work. Path::PointInside doesn't seem to succeed where it should... Other things might have happened. Not much happened towards actually getting infinite precision, starting to run out of time, panic mode engaged... PANIC MODE ENGAGED PANIC LEVELS AT 100% EMERGENCY ANTIPROCRASTINATION MEASURES FAILED PANIC LEVELS AT 200% ENGAGE SHUT UP AND FIX EVERYTHING MODE FAILED TO SHUT UP PANIC LEVELS AT 300% ENGAGE DINNER TIME DINNER TIME SUCCESSFUL PANIC LEVELS STILL AT 300% apathy levels at 1000% --- src/bezier.h | 15 +-- src/controlpanel.cpp | 54 ++++++++-- src/controlpanel.h | 11 +- src/document.cpp | 84 ++++++++++----- src/document.h | 2 +- src/main.cpp | 16 --- src/objectrenderer.cpp | 233 +++++++++++++++++++++++++---------------- src/objectrenderer.h | 37 ++++--- src/path.cpp | 4 +- src/path.h | 7 +- src/view.cpp | 3 +- src/view.h | 21 +++- 12 files changed, 308 insertions(+), 179 deletions(-) diff --git a/src/bezier.h b/src/bezier.h index a7124af..5a773a7 100644 --- a/src/bezier.h +++ b/src/bezier.h @@ -34,7 +34,7 @@ namespace IPDF } - const Type & GetType() + Type GetType() { if (type != Bezier::UNKNOWN) return type; @@ -53,17 +53,17 @@ namespace IPDF Real d2 = a3*b1 - a1*b3; Real d3 = a1*b2 - a2*b1; - if (d1 == d2 && d2 == d3 && d3 == 0) + if (fabs(d1+d2+d3) < 1e-6) { type = LINE; //Debug("LINE %s", Str().c_str()); return type; } - Real delta1 = -d1*d1; + Real delta1 = -(d1*d1); Real delta2 = d1*d2; - Real delta3 = d1*d3 -d2*d2; - if (delta1 == delta2 && delta2 == delta3 && delta3 == 0) + Real delta3 = d1*d3 -(d2*d2); + if (fabs(delta1+delta2+delta3) < 1e-6) { type = QUADRATIC; @@ -72,7 +72,7 @@ namespace IPDF } Real discriminant = d1*d3*4 -d2*d2; - if (discriminant == 0) + if (fabs(discriminant) < 1e-6) { type = CUSP; //Debug("CUSP %s", Str().c_str()); @@ -87,6 +87,7 @@ namespace IPDF type = LOOP; //Debug("LOOP %s", Str().c_str()); } + //Debug("disc %.30f", discriminant); return type; } @@ -102,7 +103,7 @@ namespace IPDF * Construct absolute control points using relative control points to a bounding rectangle * ie: If cpy is relative to bounds rectangle, this will be absolute */ - Bezier(const Bezier & cpy, const Rect & t = Rect(0,0,1,1)) : x0(cpy.x0), y0(cpy.y0), x1(cpy.x1), y1(cpy.y1), x2(cpy.x2),y2(cpy.y2), x3(cpy.x3), y3(cpy.y3), type(UNKNOWN) + Bezier(const Bezier & cpy, const Rect & t = Rect(0,0,1,1)) : x0(cpy.x0), y0(cpy.y0), x1(cpy.x1), y1(cpy.y1), x2(cpy.x2),y2(cpy.y2), x3(cpy.x3), y3(cpy.y3), type(cpy.type) { x0 *= t.w; y0 *= t.h; diff --git a/src/controlpanel.cpp b/src/controlpanel.cpp index 5b62e91..e4e68d6 100644 --- a/src/controlpanel.cpp +++ b/src/controlpanel.cpp @@ -95,10 +95,25 @@ QMenu * ControlPanel::CreateViewMenu() view->addAction(m_view_set_bounds); connect(m_view_set_bounds, SIGNAL(triggered()), this, SLOT(SetViewBounds())); - m_view_show_object_bounds = new QAction("&Show Object Bounds", this); - m_view_show_object_bounds->setCheckable(true); - view->addAction(m_view_show_object_bounds); - connect(m_view_show_object_bounds, SIGNAL(triggered()), this, SLOT(ToggleShowObjectBounds())); + m_view_show_bezier_bounds = new QAction("&Show Bezier Bounds", this); + m_view_show_bezier_bounds->setCheckable(true); + view->addAction(m_view_show_bezier_bounds); + connect(m_view_show_bezier_bounds, SIGNAL(triggered()), this, SLOT(ToggleShowBezierBounds())); + + m_view_show_bezier_type = new QAction("&Show Bezier Type", this); + m_view_show_bezier_type->setCheckable(true); + view->addAction(m_view_show_bezier_type); + connect(m_view_show_bezier_type, SIGNAL(triggered()), this, SLOT(ToggleShowBezierType())); + + m_view_show_fill_bounds = new QAction("&Show Fill Bounds", this); + m_view_show_fill_bounds->setCheckable(true); + view->addAction(m_view_show_fill_bounds); + connect(m_view_show_fill_bounds, SIGNAL(triggered()), this, SLOT(ToggleShowFillBounds())); + + m_view_show_fill_points = new QAction("&Show Fill Points", this); + m_view_show_fill_points->setCheckable(true); + view->addAction(m_view_show_fill_points); + connect(m_view_show_fill_bounds, SIGNAL(triggered()), this, SLOT(ToggleShowFillPoints())); m_view_enable_shading = new QAction("&Enable Shading", this); m_view_enable_shading->setCheckable(true); @@ -157,9 +172,13 @@ void ControlPanel::UpdateAll() m_screen_cpu_rendering->setChecked(!using_gpu_rendering); m_screen_show_debug->setChecked(m_screen.DebugFontShown()); - m_view_show_object_bounds->setChecked(m_view.ShowingObjectBounds()); + m_view_show_bezier_bounds->setChecked(m_view.ShowingBezierBounds()); + m_view_show_bezier_type->setChecked(m_view.ShowingBezierType()); + m_view_show_fill_bounds->setChecked(m_view.ShowingFillBounds()); + m_view_show_fill_points->setChecked(m_view.ShowingFillPoints()); m_view_enable_shading->setChecked(m_view.PerformingShading()); + // update things based on state const char * title; const char * tooltip; @@ -200,10 +219,29 @@ void ControlPanel::UpdateAll() setToolTip(tooltip); } -void ControlPanel::ToggleShowObjectBounds() +void ControlPanel::ToggleShowBezierBounds() +{ + bool state = m_view.ShowingBezierBounds(); + m_view.ShowBezierBounds(!state); + UpdateAll(); +} +void ControlPanel::ToggleShowBezierType() +{ + bool state = m_view.ShowingBezierType(); + m_view.ShowBezierType(!state); + UpdateAll(); +} +void ControlPanel::ToggleShowFillBounds() +{ + bool state = m_view.ShowingFillBounds(); + m_view.ShowFillBounds(!state); + UpdateAll(); +} + +void ControlPanel::ToggleShowFillPoints() { - bool state = m_view.ShowingObjectBounds(); - m_view.ShowObjectBounds(!state); + bool state = m_view.ShowingFillPoints(); + m_view.ShowFillPoints(!state); UpdateAll(); } diff --git a/src/controlpanel.h b/src/controlpanel.h index ea557d0..67eff64 100644 --- a/src/controlpanel.h +++ b/src/controlpanel.h @@ -61,7 +61,10 @@ namespace IPDF private slots: void SetGPURendering(); void SetCPURendering(); - void ToggleShowObjectBounds(); + void ToggleShowBezierBounds(); + void ToggleShowBezierType(); + void ToggleShowFillBounds(); + void ToggleShowFillPoints(); void ToggleScreenDebugFont(); void ToggleEnableShading(); void SetViewBounds(); @@ -107,9 +110,13 @@ namespace IPDF QAction * m_document_parse_svg; QAction * m_document_load_svg; QAction * m_view_set_bounds; - QAction * m_view_show_object_bounds; + QAction * m_view_show_bezier_bounds; + QAction * m_view_show_bezier_type; + QAction * m_view_show_fill_bounds; + QAction * m_view_show_fill_points; QAction * m_view_enable_shading; + QTextEdit * m_text_edit; QPushButton * m_ok_button; diff --git a/src/document.cpp b/src/document.cpp index 5ba166c..0aeee1d 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -295,9 +295,9 @@ void Document::Load(const string & filename) #endif } -unsigned Document::AddPath(unsigned start_index, unsigned end_index, const Colour & fill) +unsigned Document::AddPath(unsigned start_index, unsigned end_index, const Colour & fill, const Colour & stroke) { - Path path(m_objects, start_index, end_index, fill); + Path path(m_objects, start_index, end_index, fill, stroke); unsigned data_index = AddPathData(path); Rect bounds = path.SolveBounds(m_objects); unsigned result = Add(PATH, bounds,data_index); @@ -492,6 +492,34 @@ void Document::ParseSVGTransform(const string & s, SVGMatrix & transform) } } +inline Colour ParseColourString(const string & colour_str) +{ + Colour c(0,0,0,0); + if (colour_str == "red") + c = {255,0,0,255}; + else if (colour_str == "blue") + c = {0,0,255,255}; + else if (colour_str == "green") + c = {0,255,0,255}; + else if (colour_str == "black") + c = {0,0,0,255}; + else if (colour_str == "white") + c = {255,255,255,255}; + else if (colour_str.size() == 7 && colour_str[0] == '#') + { + Debug("Parse colour string: \"%s\"", colour_str.c_str()); + char comp[3] = {colour_str[1], colour_str[2], '\0'}; + c.r = strtoul(comp, NULL, 16); + comp[0] = colour_str[3]; comp[1] = colour_str[4]; + c.g = strtoul(comp, NULL, 16); + comp[0] = colour_str[5]; comp[1] = colour_str[6]; + c.b = strtoul(comp, NULL, 16); + c.a = 255; + Debug("Colour is: %u, %u, %u, %u", c.r, c.g, c.b, c.a); + } + return c; +} + void Document::ParseSVGNode(pugi::xml_node & root, SVGMatrix & parent_transform) { //Debug("Parse node <%s>", root.name()); @@ -521,9 +549,9 @@ void Document::ParseSVGNode(pugi::xml_node & root, SVGMatrix & parent_transform) //Debug("Path data attribute is \"%s\"", d.c_str()); bool closed = false; pair range = ParseSVGPathData(d, transform, closed); - if (closed) + if (true)//(closed) { - Colour c(0,0,0,0); + string colour_str(""); map style; if (child.attribute("style")) @@ -540,41 +568,39 @@ void Document::ParseSVGNode(pugi::xml_node & root, SVGMatrix & parent_transform) { colour_str = style["fill"]; } - if (colour_str == "red") - c = {1,0,0,1}; - else if (colour_str == "blue") - c = {0,0,1,1}; - else if (colour_str == "green") - c = {0,1,0,1}; - else if (colour_str == "black") - c = {0,0,0,1}; - else if (colour_str == "white") - c = {1,1,1,1}; - else if (colour_str.size() == 7 && colour_str[0] == '#') + Colour fill = ParseColourString(colour_str); + Colour stroke = fill; + + if (child.attribute("stroke")) { - Debug("Parse colour string: \"%s\"", colour_str.c_str()); - char comp[2] = {colour_str[1], colour_str[2]}; - c.r = Real(strtoul(comp, NULL, 16))/Real(255); - comp[0] = colour_str[3]; comp[1] = colour_str[4]; - c.g = Real(strtoul(comp, NULL, 16))/Real(255); - comp[0] = colour_str[5]; comp[1] = colour_str[6]; - c.b = Real(strtoul(comp, NULL, 16))/Real(255); - c.a = 1; - Debug("Colour is: %f, %f, %f, %f", Float(c.r), Float(c.g), Float(c.b), Float(c.a)); + colour_str = child.attribute("stroke").as_string(); + stroke = ParseColourString(colour_str); } + else if (style.find("stroke") != style.end()) + { + colour_str = style["stroke"]; + stroke = ParseColourString(colour_str); + } + // Determin shading alpha if (child.attribute("fill-opacity")) { - c.a = child.attribute("fill-opacity").as_float(); + fill.a = 255*child.attribute("fill-opacity").as_float(); } else if (style.find("fill-opacity") != style.end()) { - c.a = strtod(style["fill-opacity"].c_str(), NULL); + fill.a = 255*strtod(style["fill-opacity"].c_str(), NULL); } - - Debug("fill-opacity is %f", Float(c.a)); - AddPath(range.first, range.second, c); + if (child.attribute("stroke-opacity")) + { + stroke.a = 255*child.attribute("stroke-opacity").as_float(); + } + else if (style.find("stroke-opacity") != style.end()) + { + stroke.a = 255*strtod(style["stroke-opacity"].c_str(), NULL); + } + AddPath(range.first, range.second, fill, stroke); } } diff --git a/src/document.h b/src/document.h index 9169d8a..eb8710e 100644 --- a/src/document.h +++ b/src/document.h @@ -52,7 +52,7 @@ namespace IPDF bool operator==(const Document & equ) const; bool operator!=(const Document & equ) const {return !(this->operator==(equ));} - unsigned AddPath(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), const Colour & stroke=Colour(0,0,0,0)); unsigned AddBezier(const Bezier & bezier); unsigned Add(ObjectType type, const Rect & bounds, unsigned data_index = 0); unsigned AddBezierData(const Bezier & bezier); diff --git a/src/main.cpp b/src/main.cpp index f183303..dc20d75 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -52,22 +52,6 @@ int main(int argc, char ** argv) output_bmp = argv[i]; break; - case 'c': - { - Debug("Reading paint colour"); - for (int j = 1; j <= 4; ++j) - { - if (i+j >= argc) - Fatal("No %d colour component following -c switch", j); - char * e; - float * comp = (j == 1) ? (&c.r) : ((j == 2) ? (&c.g) : ((j == 3) ? (&c.b) : &(c.a))); - *comp = strtof(argv[i+j], &e); - if (*e != '\0') - Fatal("Colour component %d not a valid float", j); - } - i += 4; - break; - } case 'b': { Debug("Reading view bounds"); diff --git a/src/objectrenderer.cpp b/src/objectrenderer.cpp index 4e59cd0..f2f0fdd 100644 --- a/src/objectrenderer.cpp +++ b/src/objectrenderer.cpp @@ -52,7 +52,7 @@ void ObjectRenderer::RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id) /** * Default implementation for rendering using CPU */ -void ObjectRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) +void ObjectRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) { Error("Cannot render objects of type %d on CPU", m_type); //TODO: Render a rect or something instead? @@ -117,7 +117,7 @@ void ObjectRenderer::FinaliseBuffers() /** * Rectangle (filled) */ -void RectFilledRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) +void RectFilledRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) { for (unsigned i = 0; i < m_indexes.size(); ++i) { @@ -144,7 +144,7 @@ void RectFilledRenderer::RenderUsingCPU(const Objects & objects, const View & vi /** * Rectangle (outine) */ -void RectOutlineRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) +void RectOutlineRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) { //Debug("Render %u outlined rectangles on CPU", m_indexes.size()); for (unsigned i = 0; i < m_indexes.size(); ++i) @@ -172,7 +172,7 @@ void RectOutlineRenderer::RenderUsingCPU(const Objects & objects, const View & v /** * Circle (filled) */ -void CircleFilledRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) +void CircleFilledRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) { for (unsigned i = 0; i < m_indexes.size(); ++i) { @@ -224,85 +224,92 @@ ObjectRenderer::PixelPoint ObjectRenderer::CPUPointLocation(const Vec2 & point, return PixelPoint(x,y); } + +void BezierRenderer::RenderBezierOnCPU(unsigned i, Objects & objects, const View & view, const CPURenderTarget & target, const Colour & c) +{ + const Rect & bounds = objects.bounds[i]; + PixelBounds pix_bounds(CPURenderBounds(bounds,view,target)); + Bezier control(objects.beziers[objects.data_indices[i]].ToAbsolute(bounds),CPURenderBounds(Rect(0,0,1,1), view, target)); + + if (view.ShowingBezierBounds()) + { + ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y, target, Colour(255,0,0,0)); + ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y+pix_bounds.h, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, Colour(0,255,0,0)); + ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x, pix_bounds.y+pix_bounds.h, target, Colour(255,0,0,0)); + ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, Colour(0,255,0,0)); + } + + unsigned blen = min(max(2U, (unsigned)(target.w/view.GetBounds().w)), + min((unsigned)(pix_bounds.w+pix_bounds.h)/4 + 1, 100U)); + + // DeCasteljau Divide the Bezier + queue divisions; + divisions.push(control); + while(divisions.size() < blen) + { + Bezier & current = divisions.front(); + if (current.GetType() == Bezier::LINE) + { + --blen; + continue; + } + divisions.push(current.DeCasteljauSubdivideRight(Real(1)/Real(2))); + divisions.push(current.DeCasteljauSubdivideLeft(Real(1)/Real(2))); + divisions.pop(); + } + while (divisions.size() > 0) + { + Bezier & current = divisions.front(); + RenderLineOnCPU(current.x0, current.y0, current.x3, current.y3, target, c); + divisions.pop(); + } +} /** * Bezier curve * Not sure how to apply De'Casteljau, will just use a bunch of Bresnham lines for now. */ -void BezierRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) +void BezierRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) { + if (view.PerformingShading()) + return; + //Warn("Rendering Beziers on CPU. Things may explode."); for (unsigned i = 0; i < m_indexes.size(); ++i) { if (m_indexes[i] < first_obj_id) continue; if (m_indexes[i] >= last_obj_id) continue; - const Rect & bounds = objects.bounds[m_indexes[i]]; - PixelBounds pix_bounds(CPURenderBounds(bounds,view,target)); - - Bezier control(objects.beziers[objects.data_indices[m_indexes[i]]].ToAbsolute(bounds),CPURenderBounds(Rect(0,0,1,1), view, target)); - //Debug("%s -> %s via %s", objects.beziers[objects.data_indices[m_indexes[i]]].Str().c_str(), control.Str().c_str(), bounds.Str().c_str()); - // Draw a rectangle around the bezier for debugging the bounds rectangle calculations - if (view.ShowingObjectBounds()) + Colour c(0,0,0,255); + if (view.ShowingBezierType()) { - ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y, target, Colour(1,0,0,1)); - ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y+pix_bounds.h, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, Colour(0,1,0,1)); - ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x, pix_bounds.y+pix_bounds.h, target, Colour(1,0,0,1)); - ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, Colour(0,1,0,1)); - } - // Draw lines between the control points for debugging - //ObjectRenderer::RenderLineOnCPU((int64_t)control.x0, (int64_t)control.y0, (int64_t)control.x1, (int64_t)control.y1,target); - //ObjectRenderer::RenderLineOnCPU((int64_t)control.x1, (int64_t)control.y1, (int64_t)control.x2, (int64_t)control.y2,target); - - - - - unsigned blen = min(max(2U, (unsigned)(target.w/view.GetBounds().w)), min((unsigned)(pix_bounds.w+pix_bounds.h)/4 + 1, 100U)); - - // DeCasteljau Divide the Bezier - queue divisions; - divisions.push(control); - while(divisions.size() < blen) - { - Bezier & current = divisions.front(); - if (current.GetType() == Bezier::LINE) + switch (objects.beziers[objects.data_indices[m_indexes[i]]].GetType()) { - --blen; - continue; + case Bezier::LINE: + break; + case Bezier::QUADRATIC: + c.b = 255; + break; + case Bezier::SERPENTINE: + c.r = 255; + break; + case Bezier::CUSP: + c.g = 255; + break; + case Bezier::LOOP: + c.r = 128; + c.b = 128; + break; + default: + c.r = 128; + c.g = 128; + break; } - divisions.push(current.DeCasteljauSubdivideRight(Real(1)/Real(2))); - divisions.push(current.DeCasteljauSubdivideLeft(Real(1)/Real(2))); - divisions.pop(); - - //Debug("divisions %u", divisions.size()); } - while (divisions.size() > 0) - { - Bezier & current = divisions.front(); - RenderLineOnCPU(current.x0, current.y0, current.x3, current.y3, target, Colour(0,0,0,!view.PerformingShading())); - divisions.pop(); - } - - /* Draw the Bezier by sampling - Real invblen(1); invblen /= blen; - Real t(invblen); - Vec2 v0; - Vec2 v1; - control.Evaluate(v0.x, v0.y, 0); - for (int64_t j = 1; j <= blen; ++j) - { - control.Evaluate(v1.x, v1.y, t); - - ObjectRenderer::RenderLineOnCPU(v0.x, v0.y, v1.x, v1.y, target, Colour(0,0,0,!view.PerformingShading())); - //ObjectRenderer::SetColour(target, x[0], y[0], Colour(1,0,0,1)); - //ObjectRenderer::SetColour(target, x[1], y[1], Colour(0,0,1,1)); - t += invblen; - v0 = v1; - } - */ + RenderBezierOnCPU(m_indexes[i], objects, view, target, c); } } -void BezierRenderer::PrepareBezierGPUBuffer(const Objects& objects) +void BezierRenderer::PrepareBezierGPUBuffer(Objects & objects) { m_bezier_coeffs.SetType(GraphicsBuffer::BufferTypeTexture); m_bezier_coeffs.SetUsage(GraphicsBuffer::BufferUsageDynamicDraw); @@ -365,10 +372,9 @@ void BezierRenderer::RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id) /** * Render Path (shading) */ -void PathRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) +void PathRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) { - if (!view.ShowingObjectBounds() && !view.PerformingShading()) - return; + for (unsigned i = 0; i < m_indexes.size(); ++i) { @@ -379,27 +385,57 @@ void PathRenderer::RenderUsingCPU(const Objects & objects, const View & view, co Rect bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target)); PixelBounds pix_bounds(bounds); const Path & path = objects.paths[objects.data_indices[m_indexes[i]]]; - if (path.m_fill.a == 0 || !view.PerformingShading()) + + if (view.ShowingFillPoints()) + { + + PixelPoint start(CPUPointLocation((path.m_top+path.m_left+path.m_right+path.m_bottom)/4, view, target)); + for (unsigned f = 0; f < path.m_fill_points.size(); ++f) + { + PixelPoint end(CPUPointLocation(path.m_fill_points[f], view, target)); + RenderLineOnCPU(start.first, start.second, end.first, end.second, target, Colour(0,0,255,0)); + } + } + + if (!view.PerformingShading()) continue; - for (unsigned f = 0; f < path.m_fill_points.size(); ++f) + for (unsigned b = path.m_start; b <= path.m_end; ++b) { - PixelPoint fill_point(CPUPointLocation(path.m_fill_points[f], view, target)); - FloodFillOnCPU(fill_point.first, fill_point.second, pix_bounds, target, path.m_fill); + BezierRenderer::RenderBezierOnCPU(b,objects,view,target,path.m_stroke); } - /*if (true)//(view.ShowingObjectBounds()) + if (pix_bounds.w*pix_bounds.h > 100) { - - PixelPoint start(CPUPointLocation((path.m_top+path.m_left+path.m_right+path.m_bottom)/4, view, target)); + Debug("High resolution; use fill points %u,%u", pix_bounds.w, pix_bounds.h); for (unsigned f = 0; f < path.m_fill_points.size(); ++f) { - PixelPoint end(CPUPointLocation(path.m_fill_points[f], view, target)); - RenderLineOnCPU(start.first, start.second, end.first, end.second, target, Colour(0,0,1,1)); + PixelPoint fill_point(CPUPointLocation(path.m_fill_points[f], view, target)); + + FloodFillOnCPU(fill_point.first, fill_point.second, pix_bounds, target, path.m_fill, path.m_stroke); + } + } + else + { + Debug("Low resolution; use brute force %u,%u",pix_bounds.w, pix_bounds.h); + int64_t y_min = max((int64_t)0, pix_bounds.y); + int64_t y_max = min(pix_bounds.y+pix_bounds.h, target.h); + int64_t x_min = max((int64_t)0, pix_bounds.x); + int64_t x_max = min(pix_bounds.x+pix_bounds.w, target.w); + for (int64_t y = y_min; y < y_max; ++y) + { + for (int64_t x = x_min; x < x_max; ++x) + { + Rect pb(path.SolveBounds(objects)); + Vec2 pt(pb.x + (Real(x-pix_bounds.x)/Real(pix_bounds.w))*pb.w, + pb.y + (Real(y-pix_bounds.y)/Real(pix_bounds.h))*pb.h); + if (path.PointInside(objects, pt)) + { + FloodFillOnCPU(x, y, pix_bounds, target, path.m_fill, path.m_stroke); + } + } } } - */ - } } @@ -449,12 +485,6 @@ void ObjectRenderer::RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t int64_t width = (transpose ? target.h : target.w); int64_t height = (transpose ? target.w : target.h); - uint8_t rgba[4]; - rgba[0] = 255*colour.r; - rgba[1] = 255*colour.g; - rgba[2] = 255*colour.b; - rgba[3] = 255*colour.a; - if (x0 > x1) { x = x1; @@ -487,8 +517,10 @@ void ObjectRenderer::RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t if (x >= 0 && x < width && y >= 0 && y < height) { int64_t index = (transpose ? (y + x*target.w)*4 : (x + y*target.w)*4); - for (int i = 0; i < 4; ++i) - target.pixels[index+i] = rgba[i]; + target.pixels[index+0] = colour.r; + target.pixels[index+1] = colour.g; + target.pixels[index+2] = colour.b; + target.pixels[index+3] = colour.a; } if (p < 0) p += two_dy; @@ -500,10 +532,27 @@ 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) + +void ObjectRenderer::FloodFillOnCPU(int64_t x, int64_t y, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill, const Colour & stroke) { - if (fill == Colour(1,1,1,1)) + // HACK to prevent overflooding (when the fill points for a path round to the pixel outside the boundary) + // (I totally just made that term up...) + Colour c = GetColour(target, x+1, y); + if (c == fill || c == stroke) + return; + c = GetColour(target, x-1, y); + if (c == fill || c == stroke) + return; + c = GetColour(target, x, y+1); + if (c == fill || c == stroke) + return; + c = GetColour(target, x, y-1); + if (c == fill || c == stroke) return; + + // The hack works but now we get underflooding, or, "droughts". + + queue traverse; traverse.push(PixelPoint(x,y)); // now with 100% less stack overflows! @@ -514,10 +563,14 @@ void ObjectRenderer::FloodFillOnCPU(int64_t x, int64_t y, const PixelBounds & bo if (cur.first < 0 || cur.first < bounds.x || cur.first >= bounds.x+bounds.w || cur.first >= target.w || cur.second < 0 || cur.second < bounds.y || cur.second >= bounds.y+bounds.h || cur.second >= target.h) continue; - if (GetColour(target, cur.first, cur.second) != Colour(1,1,1,1)) + c = GetColour(target, cur.first, cur.second); + if (c == fill || c == stroke) continue; + SetColour(target, cur.first, cur.second, fill); + //Debug("c is {%u,%u,%u,%u} fill is {%u,%u,%u,%u}, stroke is {%u,%u,%u,%u}", + // c.r,c.g,c.b,c.a, fill.r,fill.g,fill.b,fill.a, stroke.r,stroke.g,stroke.b,stroke.a); traverse.push(PixelPoint(cur.first+1, cur.second)); traverse.push(PixelPoint(cur.first-1, cur.second)); diff --git a/src/objectrenderer.h b/src/objectrenderer.h index e43e3b9..841040f 100644 --- a/src/objectrenderer.h +++ b/src/objectrenderer.h @@ -55,19 +55,21 @@ namespace IPDF 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)); + if (index < 0 || index >= 4*(target.w*target.h)) + return Colour(0,0,0,0); + return Colour(target.pixels[index+0],target.pixels[index+1],target.pixels[index+2],target.pixels[index+3]); } 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; + if (index < 0 || index >= 4*(target.w*target.h)) + return; + + target.pixels[index+0] = c.r; + target.pixels[index+1] = c.g; + target.pixels[index+2] = c.b; + target.pixels[index+3] = c.a; } struct PixelBounds @@ -84,7 +86,7 @@ namespace IPDF static void SaveBMP(const CPURenderTarget & target, const char * filename); - virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) = 0; + virtual void RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) = 0; @@ -98,7 +100,7 @@ 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); + static void FloodFillOnCPU(int64_t x0, int64_t y0, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill, const Colour & stroke=Colour(0,0,0,0)); ShaderProgram m_shader_program; /** GLSL shaders for GPU **/ GraphicsBuffer m_ibo; /** Index Buffer Object for GPU rendering **/ @@ -112,7 +114,7 @@ namespace IPDF public: RectFilledRenderer() : ObjectRenderer(RECT_FILLED, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl","shaders/rect_filled_geom.glsl") {} virtual ~RectFilledRenderer() {} - virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); + virtual void RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); }; /** Renderer for outlined rectangles **/ class RectOutlineRenderer : public ObjectRenderer @@ -120,7 +122,7 @@ namespace IPDF public: RectOutlineRenderer() : ObjectRenderer(RECT_OUTLINE, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/rect_outline_geom.glsl") {} virtual ~RectOutlineRenderer() {} - virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); + virtual void RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); }; /** Renderer for filled circles **/ class CircleFilledRenderer : public ObjectRenderer @@ -128,7 +130,7 @@ namespace IPDF public: CircleFilledRenderer() : ObjectRenderer(CIRCLE_FILLED, "shaders/rect_vert.glsl", "shaders/circle_frag.glsl", "shaders/circle_filled_geom.glsl") {} virtual ~CircleFilledRenderer() {} - virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); + virtual void RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); }; /** Renderer for bezier curves **/ @@ -138,8 +140,11 @@ namespace IPDF BezierRenderer() : ObjectRenderer(BEZIER, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/bezier_texbuf_geom.glsl") {} virtual ~BezierRenderer() {} virtual void RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id); - virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); - void PrepareBezierGPUBuffer(const Objects & objects); + virtual void RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id); + void PrepareBezierGPUBuffer(Objects & objects); + + static void RenderBezierOnCPU(unsigned index, Objects & objects, const View & view, const CPURenderTarget & target, const Colour & c=Colour(0,0,0,255)); + private: GraphicsBuffer m_bezier_coeffs; GraphicsBuffer m_bezier_ids; @@ -162,7 +167,7 @@ namespace IPDF public: 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); + virtual void RenderUsingCPU(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 index e0eca4a..3f93822 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -5,8 +5,8 @@ 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) +Path::Path(const Objects & objects, unsigned start, unsigned end, const Colour & fill, const Colour & stroke) + : m_start(start), m_end(end), m_fill(fill), m_stroke(stroke) { Real xmin = 0; Real ymin = 0; Real xmax = 0; Real ymax = 0; diff --git a/src/path.h b/src/path.h index b08c146..1a0ed53 100644 --- a/src/path.h +++ b/src/path.h @@ -11,9 +11,9 @@ namespace IPDF struct Colour { - float r; float g; float b; float a; + uint8_t r; uint8_t g; uint8_t b; uint8_t a; Colour() = default; - Colour(float _r, float _g, float _b, float _a) : r(_r), g(_g), b(_b), a(_a) {} + Colour(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _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); @@ -25,7 +25,7 @@ namespace IPDF struct Path { - Path(const Objects & objects, unsigned _start, unsigned _end, const Colour & _fill = Colour(0.8,0.8,0.8,1)); + Path(const Objects & objects, unsigned _start, unsigned _end, const Colour & _fill = Colour(128,128,128,255), const Colour & _stroke = Colour(0,0,0,0)); Rect SolveBounds(const Objects & objects) const; @@ -44,6 +44,7 @@ namespace IPDF std::vector m_fill_points; Colour m_fill; // colour to fill with + Colour m_stroke; // colour to outline with }; } diff --git a/src/view.cpp b/src/view.cpp index 2caaadc..94064ad 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -21,7 +21,8 @@ View::View(Document & document, Screen & screen, const Rect & bounds, const Colo : m_use_gpu_transform(USE_GPU_TRANSFORM), m_use_gpu_rendering(USE_GPU_RENDERING), m_bounds_dirty(true), m_buffer_dirty(true), m_render_dirty(true), m_document(document), m_screen(screen), m_cached_display(), m_bounds(bounds), m_colour(colour), m_bounds_ubo(), m_objbounds_vbo(), m_object_renderers(NUMBER_OF_OBJECT_TYPES), m_cpu_rendering_pixels(NULL), - m_show_object_bounds(false), m_perform_shading(USE_SHADING) + m_perform_shading(USE_SHADING), m_show_bezier_bounds(false), m_show_bezier_type(false), + m_show_fill_points(false), m_show_fill_bounds(false) { Debug("View Created - Bounds => {%s}", m_bounds.Str().c_str()); diff --git a/src/view.h b/src/view.h index abfbefe..d99db6e 100644 --- a/src/view.h +++ b/src/view.h @@ -42,8 +42,14 @@ namespace IPDF void SetGPURendering(bool state) {m_use_gpu_rendering = state; m_bounds_dirty = true; m_buffer_dirty = true;} - bool ShowingObjectBounds() const {return m_show_object_bounds;} // render bounds rectangles - void ShowObjectBounds(bool state) {m_show_object_bounds = state; m_bounds_dirty = true; m_buffer_dirty = true;} + bool ShowingBezierBounds() const {return m_show_bezier_bounds;} // render bounds rectangles + void ShowBezierBounds(bool state) {m_show_bezier_bounds = state; m_bounds_dirty = true; m_buffer_dirty = true;} + bool ShowingBezierType() const {return m_show_bezier_type;} + void ShowBezierType(bool state) {m_show_bezier_type = state; m_bounds_dirty = true; m_buffer_dirty = true;} + bool ShowingFillPoints() const {return m_show_fill_points;} + void ShowFillPoints(bool state) {m_show_fill_points = state; m_bounds_dirty = true; m_buffer_dirty = true;} + bool ShowingFillBounds() const {return m_show_fill_bounds;} + void ShowFillBounds(bool state) {m_show_fill_bounds = true;} bool PerformingShading() const {return m_perform_shading;} void PerformShading(bool state) {m_perform_shading = state; m_bounds_dirty = true; m_buffer_dirty = true;} @@ -84,10 +90,17 @@ namespace IPDF // Trust me it will be easier to generalise things this way. Even though there are pointers. std::vector m_object_renderers; uint8_t * m_cpu_rendering_pixels; // pixels to be used for CPU rendering + - // Debug rendering - bool m_show_object_bounds; + // shading bool m_perform_shading; + + // Debug rendering + bool m_show_bezier_bounds; + bool m_show_bezier_type; + bool m_show_fill_points; + bool m_show_fill_bounds; + #ifndef QUADTREE_DISABLED QuadTreeIndex m_current_quadtree_node; // The highest node we will traverse. -- 2.20.1