*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%
}
- const Type & GetType()
+ Type GetType()
{
if (type != Bezier::UNKNOWN)
return type;
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;
}
Real discriminant = d1*d3*4 -d2*d2;
- if (discriminant == 0)
+ if (fabs(discriminant) < 1e-6)
{
type = CUSP;
//Debug("CUSP %s", Str().c_str());
type = LOOP;
//Debug("LOOP %s", Str().c_str());
}
+ //Debug("disc %.30f", discriminant);
return type;
}
* 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;
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);
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;
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();
}
private slots:
void SetGPURendering();
void SetCPURendering();
- void ToggleShowObjectBounds();
+ void ToggleShowBezierBounds();
+ void ToggleShowBezierType();
+ void ToggleShowFillBounds();
+ void ToggleShowFillPoints();
void ToggleScreenDebugFont();
void ToggleEnableShading();
void SetViewBounds();
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;
#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);
}
}
+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());
//Debug("Path data attribute is \"%s\"", d.c_str());
bool closed = false;
pair<unsigned, unsigned> range = ParseSVGPathData(d, transform, closed);
- if (closed)
+ if (true)//(closed)
{
- Colour c(0,0,0,0);
+
string colour_str("");
map<string, string> style;
if (child.attribute("style"))
{
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);
}
}
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);
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");
/**
* 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?
/**
* 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)
{
/**
* 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)
/**
* 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)
{
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<Bezier> 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<Bezier> 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);
/**
* 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)
{
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);
+ }
+ }
}
}
- */
-
}
}
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;
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;
} 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<PixelPoint > traverse;
traverse.push(PixelPoint(x,y));
// now with 100% less stack overflows!
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));
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
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;
/** 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 **/
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
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
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 **/
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;
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) {}
};
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;
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);
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;
std::vector<Vec2> m_fill_points;
Colour m_fill; // colour to fill with
+ Colour m_stroke; // colour to outline with
};
}
: 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());
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;}
// Trust me it will be easier to generalise things this way. Even though there are pointers.
std::vector<ObjectRenderer*> 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.