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_enable_shading = new QAction("&Enable Shading", this);
+ m_view_enable_shading->setCheckable(true);
+ view->addAction(m_view_enable_shading);
+ connect(m_view_enable_shading, SIGNAL(triggered()), this, SLOT(ToggleEnableShading()));
+
return view;
}
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_enable_shading->setChecked(m_view.PerformingShading());
+
// update things based on state
const char * title;
const char * tooltip;
setToolTip(tooltip);
}
+void ControlPanel::ToggleShowObjectBounds()
+{
+ bool state = m_view.ShowingObjectBounds();
+ m_view.ShowObjectBounds(!state);
+ UpdateAll();
+}
+
+void ControlPanel::ToggleEnableShading()
+{
+ bool state = m_view.PerformingShading();
+ m_view.PerformShading(!state);
+ UpdateAll();
+}
+
void ControlPanel::SetGPURendering()
{
m_view.SetGPURendering(true);
private slots:
void SetGPURendering();
void SetCPURendering();
+ void ToggleShowObjectBounds();
void ToggleScreenDebugFont();
+ void ToggleEnableShading();
void SetViewBounds();
void LoadSVGIntoDocument();
void SetDocumentFont();
QAction * m_document_parse_svg;
QAction * m_document_load_svg;
QAction * m_view_set_bounds;
+ QAction * m_view_show_object_bounds;
+ QAction * m_view_enable_shading;
QTextEdit * m_text_edit;
case BEZIER:
{
Rect child_node_bounds = TransformFromQuadChild({0,0,1,1}, type);
- Rect clip_bezier_bounds;
- clip_bezier_bounds.x = (child_node_bounds.x - m_objects.bounds[object_id].x) / m_objects.bounds[object_id].w;
- clip_bezier_bounds.y = (child_node_bounds.y - m_objects.bounds[object_id].y) / m_objects.bounds[object_id].h;
- clip_bezier_bounds.w = child_node_bounds.w / m_objects.bounds[object_id].w;
- clip_bezier_bounds.h = child_node_bounds.h / m_objects.bounds[object_id].h;
- std::vector<Bezier> new_curves = m_objects.beziers[m_objects.data_indices[object_id]].ClipToRectangle(clip_bezier_bounds);
+ std::vector<Bezier> new_curves = m_objects.beziers[m_objects.data_indices[object_id]].ClipToRectangle(child_node_bounds);
+ Rect obj_bounds = TransformToQuadChild(m_objects.bounds[object_id], type);
for (size_t i = 0; i < new_curves.size(); ++i)
{
- Rect new_bounds = TransformToQuadChild(m_objects.bounds[object_id], type);
- //new_bounds = TransformToQuadChild(new_bounds, type);
- //Bezier new_curve_data = new_curves[i].ToRelative(new_bounds);
unsigned index = AddBezierData(new_curves[i]);
- m_objects.bounds.push_back(new_bounds);
+ m_objects.bounds.push_back(obj_bounds);
m_objects.types.push_back(BEZIER);
m_objects.data_indices.push_back(index);
}
#endif
}
-unsigned Document::AddGroup(unsigned start_index, unsigned end_index)
+unsigned Document::AddGroup(unsigned start_index, unsigned end_index, const Colour & shading)
{
Real xmin = 0; Real ymin = 0;
Real xmax = 0; Real ymax = 0;
}
Rect bounds(xmin,ymin, xmax-xmin, ymax-ymin);
- unsigned result = Add(GROUP, bounds,0);
- m_objects.groups[m_count-1].first = start_index;
- m_objects.groups[m_count-1].second = end_index;
+
+ Group group = {start_index, end_index, shading};
+
+ unsigned data_index = AddGroupData(group);
+ unsigned result = Add(GROUP, bounds,data_index);
return result;
}
m_objects.types.push_back(type);
m_objects.bounds.push_back(bounds);
m_objects.data_indices.push_back(data_index);
- m_objects.groups.push_back(pair<unsigned, unsigned>(data_index, data_index));
return (m_count++); // Why can't we just use the size of types or something?
}
return m_objects.beziers.size()-1;
}
+unsigned Document::AddGroupData(const Group & group)
+{
+ m_objects.groups.push_back(group);
+ return m_objects.groups.size()-1;
+}
void Document::DebugDumpObjects()
{
y = strtod(GetToken(d, token, i, delims).c_str(),NULL);
}
+static bool GetKeyValuePair(const string & d, string & key, string & value, unsigned & i, const string & delims = "()[],{}<>;:=")
+{
+ key = "";
+ string token;
+ while (GetToken(d, token, i, delims) == ":" || token == ";");
+ key = token;
+ if (GetToken(d, token, i, delims) != ":")
+ {
+ Error("Expected \":\" seperating key:value pair");
+ return false;
+ }
+ value = "";
+ GetToken(d, value, i, delims);
+ return true;
+}
+
static void TransformXYPair(Real & x, Real & y, const SVGMatrix & transform)
{
Real x0(x);
ParseSVGTransform(attrib_trans.as_string(), transform);
}
+
+
if (strcmp(child.name(), "svg") == 0 || strcmp(child.name(),"g") == 0
|| strcmp(child.name(), "group") == 0)
{
else if (strcmp(child.name(), "path") == 0)
{
string d = child.attribute("d").as_string();
- Debug("Path data attribute is \"%s\"", d.c_str());
- pair<unsigned, unsigned> range = ParseSVGPathData(d, transform);
- AddGroup(range.first, range.second);
+ //Debug("Path data attribute is \"%s\"", d.c_str());
+ bool closed = false;
+ pair<unsigned, unsigned> range = ParseSVGPathData(d, transform, closed);
+ if (closed)
+ {
+ Colour c(0,0,0,0);
+ string colour_str("");
+ map<string, string> style;
+ if (child.attribute("style"))
+ {
+ ParseSVGStyleData(child.attribute("style").as_string(), style);
+ }
+
+ // Determine shading colour
+ if (child.attribute("fill"))
+ {
+ colour_str = child.attribute("fill").as_string();
+ }
+ else if (style.find("fill") != style.end())
+ {
+ 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] == '#')
+ {
+ 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));
+ }
+
+ // Determin shading alpha
+ if (child.attribute("fill-opacity"))
+ {
+ c.a = child.attribute("fill-opacity").as_float();
+ }
+ else if (style.find("fill-opacity") != style.end())
+ {
+ c.a = strtod(style["fill-opacity"].c_str(), NULL);
+ }
+
+ Debug("fill-opacity is %f", Float(c.a));
+ AddGroup(range.first, range.second, c);
+ }
}
else if (strcmp(child.name(), "line") == 0)
}
}
+void Document::ParseSVGStyleData(const string & style, map<string, string> & results)
+{
+ unsigned i = 0;
+ string key;
+ string value;
+ while (i < style.size() && GetKeyValuePair(style, key, value, i))
+ {
+ results[key] = value;
+ }
+}
+
/**
* Parse an SVG string into a rectangle
*/
// Fear the wrath of the tokenizing svg data
// Seriously this isn't really very DOM-like at all is it?
-pair<unsigned, unsigned> Document::ParseSVGPathData(const string & d, const SVGMatrix & transform)
+pair<unsigned, unsigned> Document::ParseSVGPathData(const string & d, const SVGMatrix & transform, bool & closed)
{
+ closed = false;
Real x[4] = {0,0,0,0};
Real y[4] = {0,0,0,0};
}
else if (command == "l" || command == "L" || command == "h" || command == "H" || command == "v" || command == "V")
{
- Debug("Construct lineto command, relative %d", relative);
+ //Debug("Construct lineto command, relative %d", relative);
Real dx = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL));
Real dy;
x[0] = x3;
y[0] = y3;
command = "m";
+ closed = true;
}
else
{
#include "ipdf.h"
#include "quadtree.h"
+#include <map>
+
#include "../contrib/pugixml-1.4/src/pugixml.hpp"
#include "stb_truetype.h"
bool operator==(const Document & equ) const;
bool operator!=(const Document & equ) const {return !(this->operator==(equ));}
- unsigned AddGroup(unsigned start_index, unsigned end_index);
+ unsigned AddGroup(unsigned start_index, unsigned end_index, const Colour & shading=Colour(0.1,0.1,0.1,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);
-
-
-
-
/** SVG Related functions **/
/** Load an SVG text file and add to the document **/
/** Parse an SVG node or SVG-group node, adding children to the document **/
void ParseSVGNode(pugi::xml_node & root, SVGMatrix & transform);
/** Parse an SVG path with string **/
- std::pair<unsigned, unsigned> ParseSVGPathData(const std::string & d, const SVGMatrix & transform);
+ std::pair<unsigned, unsigned> ParseSVGPathData(const std::string & d, const SVGMatrix & transform, bool & closed);
/** Modify an SVG transformation matrix **/
static void ParseSVGTransform(const std::string & s, SVGMatrix & transform);
+
+ /** Extract CSS values (shudder) from style **/
+ static void ParseSVGStyleData(const std::string & style, std::map<std::string, std::string> & results);
/** Font related functions **/
void SetFont(const std::string & font_filename);
struct Group
{
+ unsigned start;
+ unsigned end;
Colour shading;
};
/** Used by all objects **/
std::vector<ObjectType> types; // types of objects
std::vector<Rect> bounds; // rectangle bounds of objects
-
- /** Used by BEZIER to identify data position in relevant vector **/
+ /** Used by BEZIER and GROUP to identify data position in relevant vector **/
std::vector<unsigned> data_indices;
-
/** Used by BEZIER only **/
std::vector<Bezier> beziers; // bezier curves - look up by data_indices
-
- std::vector<std::pair<unsigned, unsigned> > groups;
+ /** Used by GROUP only **/
+ std::vector<Group> groups;
};
class View;
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
- /*
- 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));
- */
+ if (view.ShowingObjectBounds())
+ {
+ 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);
Real x[2]; Real y[2];
control.Evaluate(x[0], y[0], Real(0));
//Debug("target is (%lu, %lu)", target.w, target.h);
- int64_t blen = 100;
- //blen = min(max((int64_t)2, (int64_t)(target.w/view.GetBounds().w)), (int64_t)100);
+ int64_t blen = min(max((int64_t)2, (int64_t)(target.w/view.GetBounds().w)), (int64_t)100);
Real invblen(1); invblen /= blen;
//Debug("Using %li lines, inverse %f", blen, Double(invblen));
*/
void GroupRenderer::RenderUsingCPU(const 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)
{
if (m_indexes[i] < first_obj_id) continue;
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]]];
+ const Colour & c = group.shading;
+ if (c.a == 0 || !view.PerformingShading())
+ continue;
- Colour c(0.5,0.5,1,1);
// make the bounds just a little bit bigger
pix_bounds.x--;
pix_bounds.w++;
pix_bounds.y--;
pix_bounds.h++;
- /*
- 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);
- ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, c);
- */
+
// 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)
{
bool inside = false;
}
}
}
+ //#endif //SHADING_DUMB
+ if (view.ShowingObjectBounds())
+ {
+
+ 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);
+ ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, c);
+ }
}
// TODO: Avoid extra inner conditionals
do
- {
+ {
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];
}
-
if (p < 0)
p += two_dy;
else
View::View(Document & document, Screen & screen, const Rect & bounds, const Colour & colour)
: 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_objbounds_vbo(), m_object_renderers(NUMBER_OF_OBJECT_TYPES), m_cpu_rendering_pixels(NULL),
+ m_show_object_bounds(false), m_perform_shading(USE_SHADING)
{
Debug("View Created - Bounds => {%s}", m_bounds.Str().c_str());
y *= m_bounds.h;
m_bounds.x += x;
m_bounds.y += y;
- Debug("View Bounds => %s", m_bounds.Str().c_str());
+ //Debug("View Bounds => %s", m_bounds.Str().c_str());
if (!m_use_gpu_transform)
m_buffer_dirty = true;
m_bounds_dirty = true;
#include "framebuffer.h"
#include "objectrenderer.h"
-#define USE_GPU_TRANSFORM true
-#define USE_GPU_RENDERING true
+#define USE_GPU_TRANSFORM false
+#define USE_GPU_RENDERING false
+#define USE_SHADING !(USE_GPU_RENDERING) && true
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 PerformingShading() const {return m_perform_shading;}
+ void PerformShading(bool state) {m_perform_shading = state; m_bounds_dirty = true; m_buffer_dirty = true;}
void ForceBoundsDirty() {m_bounds_dirty = true;}
void ForceBufferDirty() {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;
+ bool m_perform_shading;
#ifndef QUADTREE_DISABLED
QuadTreeIndex m_current_quadtree_node; // The highest node we will traverse.