It's hard to type the ́e so I will just call them Beziers from now.
New struct represents a cubic bezier, can be evaluated. The Objects struct contains a vector of beziers, and a vector of indices for each object.
If an ObjectType is BEZIER than the index can be used to look up the bezier control points. Control points are relative to the bounding rectangle;
so we can reuse the same curves (eg: For fonts).
Rendering happens on CPU only, sub divide and use Bresenham lines.
Bresenham lines are not quite optimal but I eventually gave up.
So we don't have a "line" type, but you can make one by creating a Bezier where x1,y1 == x0,y0
They look kind of wobbly.
Save/Load not tested. It might break. But it will have to be pretty heavily rewritten soon anyway.
# TODO: stb_truetype doesn't compile with some of these warnings.
CXX = g++ -std=gnu++0x -g -Wall -Werror -Wshadow -pedantic
MAIN = main.o
-OBJ = log.o real.o document.o objectrenderer.o view.o screen.o vfpu.o graphicsbuffer.o framebuffer.o shaderprogram.o stb_truetype.o gl_core44.o
+OBJ = log.o real.o bezier.o document.o objectrenderer.o view.o screen.o vfpu.o graphicsbuffer.o framebuffer.o shaderprogram.o stb_truetype.o gl_core44.o
LIB_x86_64 = ../contrib/lib/libSDL2-2.0.so.0 -lGL
LIB_i386 = ../contrib/lib32/libSDL2-2.0.so.0 -lGL
--- /dev/null
+#include "bezier.h"
+
+#include <unordered_map>
+#include <cmath>
+
+using namespace std;
+
+namespace IPDF
+{
+
+/**
+ * Factorial
+ * Use dynamic programming / recursion
+ */
+int Factorial(int n)
+{
+ static unordered_map<int, int> dp;
+ static bool init = false;
+ if (!init)
+ {
+ init = true;
+ dp[0] = 1;
+ }
+ auto it = dp.find(n);
+ if (it != dp.end())
+ return it->second;
+ int result = n*Factorial(n-1);
+ dp[n] = result;
+ return result;
+}
+
+/**
+ * Binomial coefficients
+ */
+int BinomialCoeff(int n, int k)
+{
+ return Factorial(n) / Factorial(k) / Factorial(n-k);
+}
+
+/**
+ * Bernstein Basis Polynomial
+ */
+Real Bernstein(int k, int n, const Real & u)
+{
+ return Real(BinomialCoeff(n, k)) * pow(u, k) * pow(Real(1.0) - u, n-k);
+}
+
+}
--- /dev/null
+#ifndef _BEZIER_H
+#define _BEZIER_H
+
+#include "real.h"
+#include "rect.h"
+namespace IPDF
+{
+ extern int Factorial(int n);
+ extern int BinomialCoeff(int n, int k);
+ extern Real Bernstein(int k, int n, const Real & u);
+
+ /** A _cubic_ bezier. I really want to make them an arbitrary size but that might upset David. **/
+ struct Bezier
+ {
+ Real x0; Real y0;
+ Real x1; Real y1;
+ Real x2; Real y2;
+ Bezier() = default; // Needed so we can fread/fwrite this struct... for now.
+ Bezier(Real _x0, Real _y0, Real _x1, Real _y1, Real _x2, Real _y2) : x0(_x0), y0(_y0), x1(_x1), y1(_y1), x2(_x2), y2(_y2) {}
+ std::string Str() const
+ {
+ std::stringstream s;
+ s << "Bezier{" << Float(x0) << "," << Float(y0) << " -> " << Float(x1) << "," << Float(y1) << " -> " << Float(x2) << "," << Float(y2) << "}";
+ return s.str();
+ }
+ Bezier(const Bezier & cpy, const Rect & t = Rect(0,0,1,1)) : x0(cpy.x0+t.x), y0(cpy.y0+t.y), x1(cpy.x1+t.x), y1(cpy.y1+t.y), x2(cpy.x2+t.x),y2(cpy.y2+t.y)
+ {
+ x1 = x0 + (x1-x0)*t.w;
+ y1 = y0 + (y1-y0)*t.h;
+ x2 = x0 + (x2-x0)*t.w;
+ y2 = y0 + (y2-y0)*t.h;
+ }
+
+ Rect ToRect() {return Rect(x0,y0,x2-x0,y2-y0);}
+
+ /** Evaluate the Bezier at parametric parameter u, puts resultant point in (x,y) **/
+ void Evaluate(Real & x, Real & y, const Real & u)
+ {
+ Real coeff[3];
+ for (unsigned i = 0; i < 3; ++i)
+ coeff[i] = Bernstein(i,2,u);
+ x = x0*coeff[0] + x1*coeff[1] + x2*coeff[2];
+ y = y0*coeff[0] + y1*coeff[1] + y2*coeff[2];
+ }
+
+ };
+
+
+
+}
+
+#endif //_BEZIER_H
#include "document.h"
-
+#include "bezier.h"
#include <cstdio>
using namespace IPDF;
using namespace std;
+//TODO: Make this work for variable sized Reals
+
// Loads an std::vector<T> of size num_elements from a file.
template<typename T>
static void LoadStructVector(FILE *src_file, size_t num_elems, std::vector<T>& dest)
WriteChunkHeader(file, CT_OBJBOUNDS, m_objects.bounds.size() * sizeof(Rect));
SaveStructVector<Rect>(file, m_objects.bounds);
+ Debug("Object data indices...");
+ WriteChunkHeader(file, CT_OBJINDICES, m_objects.data_indices.size() * sizeof(unsigned));
+ SaveStructVector<unsigned>(file, m_objects.data_indices);
+
+ Debug("Bezier data...");
+ WriteChunkHeader(file, CT_OBJBEZIERS, m_objects.beziers.size() * sizeof(uint8_t));
+ SaveStructVector<Bezier>(file, m_objects.beziers);
+
int err = fclose(file);
if (err != 0)
Fatal("Failed to close file \"%s\" - %s", filename.c_str(), strerror(err));
Debug("Object bounds...");
LoadStructVector<Rect>(file, chunk_size/sizeof(Rect), m_objects.bounds);
break;
+ case CT_OBJINDICES:
+ Debug("Object data indices...");
+ LoadStructVector<unsigned>(file, chunk_size/sizeof(unsigned), m_objects.data_indices);
+ break;
+ case CT_OBJBEZIERS:
+ Debug("Bezier data...");
+ LoadStructVector<Bezier>(file, chunk_size/sizeof(Bezier), m_objects.beziers);
+ break;
}
}
Debug("Successfully loaded %u objects from \"%s\"", ObjectCount(), filename.c_str());
}
-void Document::Add(ObjectType type, const Rect & bounds)
+void Document::Add(ObjectType type, const Rect & bounds, unsigned data_index)
{
m_objects.types.push_back(type);
m_objects.bounds.push_back(bounds);
- m_count++;
+ m_objects.data_indices.push_back(data_index);
+ ++m_count; // Why can't we just use the size of types or something?
}
+unsigned Document::AddBezierData(const Bezier & bezier)
+{
+ m_objects.beziers.push_back(bezier);
+ return m_objects.beziers.size()-1;
+}
+
+
void Document::DebugDumpObjects()
{
Debug("Objects for Document %p are:", this);
bool Document::operator==(const Document & equ) const
{
- return (ObjectCount() == equ.ObjectCount() && memcmp(m_objects.bounds.data(), equ.m_objects.bounds.data(), ObjectCount() * sizeof(Rect)) == 0);
+ return (ObjectCount() == equ.ObjectCount()
+ && memcmp(m_objects.bounds.data(), equ.m_objects.bounds.data(), ObjectCount() * sizeof(Rect)) == 0
+ && memcmp(m_objects.data_indices.data(), equ.m_objects.data_indices.data(), ObjectCount() * sizeof(unsigned)) == 0
+ && memcmp(m_objects.beziers.data(), equ.m_objects.beziers.data(), m_objects.beziers.size() * sizeof(Bezier)) == 0);
}
void Load(const std::string & filename = "");
void Save(const std::string & filename);
- void Add(ObjectType type, const Rect & bounds);
void DebugDumpObjects();
unsigned ObjectCount() const {return m_count;}
bool operator==(const Document & equ) const;
bool operator!=(const Document & equ) const {return !(this->operator==(equ));}
+ void Add(ObjectType type, const Rect & bounds, unsigned data_index = 0);
+ unsigned AddBezierData(const Bezier & bezier);
+
private:
friend class View;
Objects m_objects;
unsigned m_count;
+
+
};
}
#include "common.h"
#include "real.h"
+#include "bezier.h"
+#include "rect.h"
#define C_RED Colour(1,0,0,1)
#define C_GREEN Colour(0,1,0,1)
#define C_BLUE Colour(0,0,1,1)
+#define C_BLACK Colour(0,0,0,1);
namespace IPDF
{
CIRCLE_FILLED = 0,
RECT_FILLED,
RECT_OUTLINE,
+ BEZIER,
NUMBER_OF_OBJECT_TYPES
} ObjectType;
CT_NUMOBJS,
CT_OBJTYPES,
CT_OBJBOUNDS,
+ CT_OBJINDICES,
+ CT_OBJBEZIERS
+ //CT_OBJGROUPS
};
- struct Rect
- {
- Real x; Real y; Real w; Real h;
- Rect() = default; // Needed so we can fread/fwrite this struct
- Rect(Real _x, Real _y, Real _w, Real _h) : x(_x), y(_y), w(_w), h(_h) {}
- std::string Str() const
- {
- std::stringstream s;
- // float conversion needed because it is fucking impossible to get ostreams working with template classes
- s << "{" << Float(x) << ", " << Float(y) << ", " << Float(w) << ", " << Float(h) << "}";
- return s.str();
- }
- };
+
+
+
struct Colour
{
float r; float g; float b; float a;
Colour(float _r, float _g, float _b, float _a) : r(_r), g(_g), b(_b), a(_a) {}
};
+ struct ObjectData
+ {
+ Colour colour;
+
+ };
+
struct Objects
{
- std::vector<ObjectType> types;
- std::vector<Rect> bounds;
+ /** 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 **/
+ std::vector<unsigned> data_indices;
+
+ /** Used by BEZIER only **/
+ std::vector<Bezier> beziers; // bezier curves - look up by data_indices
};
class View;
}
else
{
+ doc.AddBezierData(Bezier(0,0,0,1,1,0));
+ doc.AddBezierData(Bezier(0,0,1,0,0,1));
+ doc.AddBezierData(Bezier(0,0,1,1,1,0));
+ doc.AddBezierData(Bezier(0,1,1,0,0,1));
+
+
for(int x = 0; x < 8; ++x)
{
for (int y = 0; y < 8; ++y)
{
- doc.Add(static_cast<IPDF::ObjectType>((x^y)%3), Rect(0.2+x-4.0,0.2+y-4.0,0.6,0.6));
+ //doc.Add(static_cast<IPDF::ObjectType>((x^y)%3), Rect(0.2+x-4.0,0.2+y-4.0,0.6,0.6));
+ doc.Add(BEZIER, Rect(0.2+x-4.0, 0.2+y-4.0, 0.6,0.6), (x^y)%3);
}
}
- //doc.Add(IPDF::RECT_OUTLINE, Rect(0.4,0.4,0.6,0.6));
-
}
Rect bounds(b[0],b[1],b[2],b[3]);
clock_gettime(CLOCK_MONOTONIC_RAW, &real_clock_start);
real_clock_now = real_clock_start;
double frames = 0;
- double data_rate = 1; // period between data output to stdout (if <= 0 there will be no output)
+ double data_rate = -1; // period between data output to stdout (if <= 0 there will be no output)
uint64_t data_points = 0;
setbuf(stdout, NULL);
while (scr.PumpEvents())
/**
* ObjectRenderer constructor
- * Note the ShaderProgram constructor which compiles the shaders for GPU rendering (if they exist)
+ * Note we cannot compile the shaders in the constructor because the Screen class needs to initialise GL and it has a ShaderProgram member
*/
ObjectRenderer::ObjectRenderer(const ObjectType & type,
const char * vert_glsl_file, const char * frag_glsl_file, const char * geom_glsl_file)
glDrawElements(GL_LINES, m_indexes.size()*2, GL_UNSIGNED_INT, 0);
}
-/**
- * Helper structuretransforms coordinates to pixels
- */
-
-ObjectRenderer::CPURenderBounds::CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target)
-{
- Rect view_bounds = view.TransformToViewCoords(bounds);
- x = view_bounds.x * Real(target.w);
- y = view_bounds.y * Real(target.h);
- w = view_bounds.w * Real(target.w);
- h = view_bounds.h * Real(target.h);
- //Debug("CPURenderBounds %s -> %s -> {%li,%li,%li,%li}", bounds.Str().c_str(), view_bounds.Str().c_str(), x, y, w, h);
-}
/**
* Default implementation for rendering using CPU
void ObjectRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target)
{
Error("Cannot render objects of type %d on CPU", m_type);
+ //TODO: Render a rect or something instead?
}
/**
{
for (unsigned i = 0; i < m_indexes.size(); ++i)
{
- CPURenderBounds bounds(objects.bounds[m_indexes[i]], view, target);
+ PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
for (int64_t x = max(0L, bounds.x); x <= min(bounds.x+bounds.w, target.w-1); ++x)
{
for (int64_t y = max(0L, bounds.y); y <= min(bounds.y+bounds.h, target.h-1); ++y)
*/
void RectOutlineRenderer::RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target)
{
+ //Debug("Render %u outlined rectangles on CPU", m_indexes.size());
for (unsigned i = 0; i < m_indexes.size(); ++i)
{
- CPURenderBounds bounds(objects.bounds[m_indexes[i]], view, target);
- for (int64_t x = max(0L, bounds.x); x <= min(bounds.x+bounds.w, target.w-1); ++x)
- {
- int64_t top = (x+target.w*bounds.y)*4;
- int64_t bottom = (x+target.w*(bounds.y+bounds.h))*4;
-
- if (top >= 0L && top <4*target.w*target.h)
- {
- for (int j = 0; j < 3; ++j)
- target.pixels[top+j] = 0;
- target.pixels[top+3] = 255;
- }
- if (bottom >= 0L && bottom <4*target.w*target.h)
- {
- for (int j = 0; j < 3; ++j)
- target.pixels[bottom+j] = 0;
- target.pixels[bottom+3] = 255;
- }
- }
+ PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
+
+ // Using bresenham's lines now mainly because I want to see if they work
+ // top
+ ObjectRenderer::RenderLineOnCPU(bounds.x, bounds.y, bounds.x+bounds.w, bounds.y, target);
+ // bottom
+ ObjectRenderer::RenderLineOnCPU(bounds.x, bounds.y+bounds.h, bounds.x+bounds.w, bounds.y+bounds.h, target);
+ // left
+ ObjectRenderer::RenderLineOnCPU(bounds.x, bounds.y, bounds.x, bounds.y+bounds.h, target);
+ // right
+ ObjectRenderer::RenderLineOnCPU(bounds.x+bounds.w, bounds.y, bounds.x+bounds.w, bounds.y+bounds.h, target);
- for (int64_t y = max(0L, bounds.y); y <= min(bounds.y+bounds.h, target.h-1); ++y)
- {
- int64_t left = (bounds.x >= 0L && bounds.x < target.w) ? (bounds.x + target.w*y)*4 : -1L;
- int64_t right = (bounds.x+bounds.w >= 0L && bounds.x+bounds.w < target.w) ? (bounds.x+bounds.w + target.w*y)*4 : -1L;
- if (left >= 0L && left <4*target.w*target.h)
- {
- for (int j = 0; j < 3; ++j)
- target.pixels[left+j] = 0;
- target.pixels[left+3] = 255;
- }
- if (right >= 0L && right <4*target.w*target.h)
- {
- for (int j = 0; j < 3; ++j)
- target.pixels[right+j] = 0;
- target.pixels[right+3] = 255;
- }
- }
+ // Diagonal for testing (from bottom left to top right)
+ //ObjectRenderer::RenderLineOnCPU(bounds.x,bounds.y+bounds.h, bounds.x+bounds.w, bounds.y,target, C_BLUE);
+ //ObjectRenderer::RenderLineOnCPU(bounds.x+bounds.w, bounds.y+bounds.h, bounds.x, bounds.y, target,C_GREEN);
}
}
{
for (unsigned i = 0; i < m_indexes.size(); ++i)
{
- CPURenderBounds bounds(objects.bounds[m_indexes[i]], view, target);
+ PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
int64_t centre_x = bounds.x + bounds.w / 2;
int64_t centre_y = bounds.y + bounds.h / 2;
}
}
+Rect ObjectRenderer::CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target)
+{
+ Rect result = view.TransformToViewCoords(bounds);
+ result.x *= Real(target.w);
+ result.y *= Real(target.h);
+ result.w *= Real(target.w);
+ result.h *= Real(target.h);
+ return result;
+}
+
+
+/**
+ * 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)
+{
+ //Warn("Rendering Beziers on CPU. Things may explode.");
+ for (unsigned i = 0; i < m_indexes.size(); ++i)
+ {
+ Rect bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
+ PixelBounds pix_bounds(bounds);
+
+
+ Bezier control(objects.beziers[objects.data_indices[m_indexes[i]]], bounds);
+ //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 coord transforms
+ //ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y, target);
+ //ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y+pix_bounds.h, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target);
+ //ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x, pix_bounds.y+pix_bounds.h, target);
+ //ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target);
+
+ // 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));
+ for (unsigned j = 1; j <= 100; ++j)
+ {
+ control.Evaluate(x[j % 2],y[j % 2], Real(0.01)*j);
+ ObjectRenderer::RenderLineOnCPU((int64_t)x[0],(int64_t)y[0], (int64_t)x[1],(int64_t)y[1], target);
+ }
+
+ /*
+ Real u(0);
+ while (u < Real(1))
+ {
+ u += Real(1e-6);
+ Real x; Real y; control.Evaluate(x,y,u);
+ int64_t index = ((int64_t)x + (int64_t)y*target.w)*4;
+ if (index >= 0 && index < 4*(target.w*target.h))
+ {
+ target.pixels[index+0] = 0;
+ target.pixels[index+1] = 0;
+ target.pixels[index+2] = 0;
+ target.pixels[index+3] = 255;
+ }
+ }
+ */
+
+ }
+}
/**
* For debug, save pixels to bitmap
SDL_FreeSurface(surf);
}
+/**
+ * Bresenham's lines
+ */
+void ObjectRenderer::RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t y1, const CPURenderTarget & target, const Colour & colour, bool transpose)
+{
+ int64_t dx = x1 - x0;
+ int64_t dy = y1 - y0;
+ bool neg_m = (dy*dx < 0);
+ dy = abs(dy);
+ dx = abs(dx);
+
+ // If positive slope > 1, just swap x and y
+ if (dy > dx)
+ {
+ RenderLineOnCPU(y0,x0,y1,x1,target,colour,!transpose);
+ return;
+ }
+
+ int64_t two_dy = 2*dy;
+ int64_t p = two_dy - dx;
+ int64_t two_dxdy = 2*(dy-dx);
+ int64_t x; int64_t y; int64_t x_end;
+ 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;
+ y = y1;
+ x_end = x0;
+ }
+ else
+ {
+ x = x0;
+ y = y0;
+ x_end = x1;
+ }
+
+ if (x < 0)
+ {
+ if (x_end < 0) return;
+ y = (neg_m ? y - (dy*-x)/dx : y + (dy*-x)/dx);
+ x = 0;
+ }
+
+ if (x_end > width)
+ {
+ if (x > width) return;
+ x_end = width-1;
+ }
+
+ // 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
+ {
+ if (neg_m) --y; else ++y;
+ p += two_dxdy;
+ }
+ } while (++x < x_end);
+}
+
}
* Includes GPU rendering and CPU rendering
* For GPU rendering, pass GLSL shader source files to constructor in the constructor of a base class
* To leave unimplemented, just pass NULL filename strings
- * For CPU rendering, implement RenderUsingCPU in the base class
- * To leave unimplemented, just call ObjectRenderer::RenderUsingCPU in the base class
+ * For CPU rendering, implement RenderUsingCPU in the derived class
+ * To leave unimplemented, just call ObjectRenderer::RenderUsingCPU in the derived class
* The View class uses ObjectRenderer's; see view.h
*/
class ObjectRenderer
* Use the GPU to render the objects - GLSL shader approach
* This way is definitely faster, but subject to the GPU's limitations on precision
*/
- void RenderUsingGPU();
+ virtual void RenderUsingGPU();
/**
* Use the CPU to render the objects - "make a bitmap and convert it to a texture" approach
int64_t w;
int64_t h;
};
- struct CPURenderBounds
+ struct PixelBounds
{
int64_t x; int64_t y; int64_t w; int64_t h;
- CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target);
+ PixelBounds(const Rect & bounds) : x(bounds.x), y(bounds.y), w(bounds.w), h(bounds.h) {}
};
+ static Rect CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target);
+
+
static void SaveBMP(const CPURenderTarget & target, const char * filename);
void PrepareBuffers(unsigned max_size);
void FinaliseBuffers();
void AddObjectToBuffers(unsigned index);
-
+
+ /** 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);
ShaderProgram m_shader_program; /** GLSL shaders for GPU **/
GraphicsBuffer m_ibo; /** Index Buffer Object for GPU rendering **/
virtual ~CircleFilledRenderer() {}
virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target);
};
+
+ /** Renderer for bezier curves **/
+ class BezierRenderer : public ObjectRenderer
+ {
+ public:
+ BezierRenderer() : ObjectRenderer(BEZIER, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/rect_outline_geom.glsl") {}
+ virtual ~BezierRenderer() {}
+ virtual void RenderUsingGPU()
+ {
+ Error("Cannot render beziers on the GPU; they will appear as outlined rectangles.");
+ ObjectRenderer::RenderUsingGPU();
+ }
+ virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target);
+ };
}
#endif //_OBJECT_RENDERER_H
--- /dev/null
+#ifndef _RECT_H
+#define _RECT_H
+
+#include "common.h"
+#include "real.h"
+
+namespace IPDF
+{
+ struct Rect
+ {
+ Real x; Real y; Real w; Real h;
+ Rect() = default; // Needed so we can fread/fwrite this struct
+ Rect(Real _x, Real _y, Real _w, Real _h) : x(_x), y(_y), w(_w), h(_h) {}
+ std::string Str() const
+ {
+ std::stringstream s;
+ // float conversion needed because it is fucking impossible to get ostreams working with template classes
+ s << "{" << Float(x) << ", " << Float(y) << ", " << Float(w) << ", " << Float(h) << "}";
+ return s.str();
+ }
+ };
+}
+
+#endif //_RECT_H
Debug("View Created - Bounds => {%s}", m_bounds.Str().c_str());
// Create ObjectRenderers - new's match delete's in View::~View
- // Ok, look, this may seem disgusting, but go look at View::PrepareRender before you murder me
+ //TODO: Don't forget to put new renderers here or things will be segfaultastic
m_object_renderers[RECT_FILLED] = new RectFilledRenderer();
m_object_renderers[RECT_OUTLINE] = new RectOutlineRenderer();
m_object_renderers[CIRCLE_FILLED] = new CircleFilledRenderer();
+ m_object_renderers[BEZIER] = new BezierRenderer();
// To add rendering for a new type of object;
// 1. Add enum to ObjectType in ipdf.h
{
for (unsigned i = 0; i < m_object_renderers.size(); ++i)
{
- delete m_object_renderers[i];
+ delete m_object_renderers[i]; // delete's match new's in constructor
}
m_object_renderers.clear();
delete [] m_cpu_rendering_pixels;
*/
void View::PrepareRender()
{
+ Debug("Recreate buffers with %u objects", m_document.ObjectCount());
// Prepare bounds vbo
m_bounds_ubo.Invalidate();
m_bounds_ubo.SetType(GraphicsBuffer::BufferTypeUniform);
ObjectType type = m_document.m_objects.types[id];
m_object_renderers.at(type)->AddObjectToBuffers(id); // Use at() in case the document is corrupt TODO: Better error handling?
// (Also, Wow I just actually used std::vector::at())
+ // (Also, I just managed to make it throw an exception because I'm a moron)
+ Debug("Object of type %d", type);
}
// Finish the buffers