From 1d179b93f6a1b2a4fe3823c26fba862c24bc5d6e Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Wed, 24 Sep 2014 14:27:26 +0800 Subject: [PATCH] Add #define to transform Object bounds on the fly View model: Object bounds are fixed, transforming view transforms the View bounds, Object bounds transformed to coordinates in View bounds before rendering. Mutable Objects model (?) : Object bounds are transformed by operations, no transformation to View bounds necessary The equivelant view bounds are still printed for comparison, but it is effectively (0,0,1,1) First approach requires one transformation when altering view and then a *lot* during rendering Second approach requires a lot of transformations when altering view and then during rendering Second approach also means we don't need a dedicated CPU renderer. Coordinates within bounds can be expressed as floats. ALSO - Added cat and panda test images - Fixed segfault due to empty paths in cat.svg ... by not including empty paths... - ipdf will now run if the window can't be created so I can run it on the bus without X - Not much you can actually do without X though... - ParanoidNumbers got considerably more #defines for debugging and trying to make them less generally shit - Fixed some bugs in PN not revealed by realops tester - There are still bugs in PN that aren't revealed by anything - Despite it basically being exactly equivelant to doubles at the moment :S - Having bugs in basic mathematics operations is actually really really really hard to fix - Want to fix all the bugs so I can make PN faster - They are slow, they spend about 30% of their time doing std::vector stuff - And that's when the std::vector is *empty* ALSO - The new approach to rendering means that the loss of precision is nowhere near as bad - This means we need to make new tests to demonstrate that there is in fact still a point to using arbitrary precision - Because floats are actually quite amazingly good if you aren't a moron about how you apply operations. - Really panicking now ALSO - I'm sure I did something else but I forgot what it was. --- src/controlpanel.cpp | 4 + src/document.cpp | 43 ++++- src/document.h | 6 +- src/main.cpp | 8 +- src/main.h | 14 +- src/objectrenderer.cpp | 9 +- src/objectrenderer.h | 11 +- src/paranoidnumber.cpp | 130 ++++++++++---- src/paranoidnumber.h | 21 ++- src/path.cpp | 2 + src/screen.cpp | 21 ++- src/screen.h | 2 + src/svg-tests/cat.svg | 335 ++++++++++++++++++++++++++++++++++++ src/svg-tests/cat2.svg | 371 ++++++++++++++++++++++++++++++++++++++++ src/svg-tests/panda.svg | 136 +++++++++++++++ src/tests/realops.cpp | 13 +- src/view.cpp | 55 ++++-- src/view.h | 2 + 18 files changed, 1123 insertions(+), 60 deletions(-) create mode 100644 src/svg-tests/cat.svg create mode 100644 src/svg-tests/cat2.svg create mode 100644 src/svg-tests/panda.svg diff --git a/src/controlpanel.cpp b/src/controlpanel.cpp index f27bc7a..daa4b21 100644 --- a/src/controlpanel.cpp +++ b/src/controlpanel.cpp @@ -336,7 +336,11 @@ void ControlPanel::LoadSVGIntoDocument() if (filename == "") return; + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + Rect bounds(0,0,1,1); + #else Rect bounds(m_view.GetBounds()); + #endif bounds.x += bounds.w/Real(2); bounds.y += bounds.h/Real(2); diff --git a/src/document.cpp b/src/document.cpp index 6b1e473..1522213 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -316,7 +316,7 @@ unsigned Document::AddBezier(const Bezier & bezier) Bezier data = bezier.ToRelative(bounds); // Relative if (data.ToAbsolute(bounds) != bezier) { - Warn("%s != %s", data.ToAbsolute(Rect(0,0,1,1)).Str().c_str(), + Warn("%s != %s", data.ToAbsolute(bounds).Str().c_str(), bezier.Str().c_str()); Warn("ToAbsolute on ToRelative does not give original Bezier"); } @@ -561,7 +561,7 @@ 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 (true)//(closed) + if (true && range.first < m_count && range.second < m_count)//(closed) { string colour_str(""); @@ -1024,3 +1024,42 @@ void Document::AddFontGlyphAtPoint(stbtt_fontinfo *font, int character, Real sca stbtt_FreeShape(font, instructions); } + +void Document::TransformObjectBounds(const SVGMatrix & transform) +{ + for (unsigned i = 0; i < m_count; ++i) + { + TransformXYPair(m_objects.bounds[i].x, m_objects.bounds[i].y, transform); + m_objects.bounds[i].w *= transform.a; + m_objects.bounds[i].h *= transform.d; + } +} + +void Document::TranslateObjects(const Real & dx, const Real & dy) +{ + for (unsigned i = 0; i < m_count; ++i) + { + m_objects.bounds[i].x += dx; + m_objects.bounds[i].y += dy; + } +} + +void Document::ScaleObjectsAboutPoint(const Real & x, const Real & y, const Real & scale_amount) +{ + for (unsigned i = 0; i < m_count; ++i) + { + m_objects.bounds[i].w /= scale_amount; + m_objects.bounds[i].h /= scale_amount; + //m_objects.bounds[i].x = x + (m_objects.bounds[i].x-x)/scale_amount; + //m_objects.bounds[i].y = y + (m_objects.bounds[i].y-x)/scale_amount; + m_objects.bounds[i].x -= x; + m_objects.bounds[i].x /= scale_amount; + m_objects.bounds[i].x += x; + + m_objects.bounds[i].y -= y; + m_objects.bounds[i].y /= scale_amount; + m_objects.bounds[i].y += y; + + } + +} diff --git a/src/document.h b/src/document.h index eb8710e..31a175b 100644 --- a/src/document.h +++ b/src/document.h @@ -81,7 +81,11 @@ namespace IPDF void AddText(const std::string & text, Real scale, Real x, Real y); void AddFontGlyphAtPoint(stbtt_fontinfo *font, int character, Real scale, Real x, Real y); - + + void TransformObjectBounds(const SVGMatrix & transform); + void TranslateObjects(const Real & x, const Real & y); + void ScaleObjectsAboutPoint(const Real & x, const Real & y, const Real & scale_amount); + #ifndef QUADTREE_DISABLED inline const QuadTree& GetQuadTree() { if (m_quadtree.root_id == QUADTREE_EMPTY) { GenBaseQuadtree(); } return m_quadtree; } QuadTreeIndex GenQuadChild(QuadTreeIndex parent, QuadTreeNodeChildren type); diff --git a/src/main.cpp b/src/main.cpp index 99e0b11..31ccf2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -167,8 +167,11 @@ int main(int argc, char ** argv) if (input_filename != NULL) { - - doc.LoadSVG(input_filename, Rect(bounds.x+bounds.w/Real(2),bounds.y+bounds.h/Real(2),bounds.w/Real(800),bounds.h/Real(600))); + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + doc.LoadSVG(input_filename, Rect(Real(1)/Real(2),Real(1)/Real(2),Real(1)/Real(800),Real(1)/Real(600))); + #else + doc.LoadSVG(input_filename, Rect(bounds.x+bounds.w/Real(2),bounds.y+bounds.h/Real(2),bounds.w/Real(800),bounds.h/Real(600))); + #endif } else if (input_text != NULL) { @@ -181,6 +184,7 @@ int main(int argc, char ** argv) #ifndef CONTROLPANEL_DISABLED + if (!scr.Valid()) hide_control_panel = true; SDL_Thread * cp_thread = NULL; if (!hide_control_panel) { diff --git a/src/main.h b/src/main.h index 92bf096..b9da3d7 100644 --- a/src/main.h +++ b/src/main.h @@ -115,15 +115,22 @@ inline void MainLoop(Document & doc, Screen & scr, View & view, int max_frames = } scr.DebugFontPrintF("Rendered frame %lu\n", (uint64_t)frames); scr.DebugFontPrintF("Lazy Rendering = %d\n", view.UsingLazyRendering()); - scr.DebugFontPrintF("[CPU] Render took %lf ms (%lf FPS) (total %lf s, avg FPS %lf)\n", cpu_frame*1e3, 1.0/cpu_frame, total_cpu_time,frames/total_cpu_time); - scr.DebugFontPrintF("[GPU] Render took %lf ms (%lf FPS) (total %lf s, avg FPS %lf)\n", gpu_frame*1e3, 1.0/gpu_frame, total_gpu_time, frames/total_gpu_time); - scr.DebugFontPrintF("[REALTIME] Render+Present+Cruft took %lf ms (%lf FPS) (total %lf s, avg FPS %lf)\n", real_frame*1e3, 1.0/real_frame, total_real_time,frames/total_real_time); + if (cpu_frame > 0 && total_cpu_time > 0) + scr.DebugFontPrintF("[CPU] Render took %lf ms (%lf FPS) (total %lf s, avg FPS %lf)\n", cpu_frame*1e3, 1.0/cpu_frame, total_cpu_time,frames/total_cpu_time); + if (gpu_frame > 0 && total_gpu_time > 0) + scr.DebugFontPrintF("[GPU] Render took %lf ms (%lf FPS) (total %lf s, avg FPS %lf)\n", gpu_frame*1e3, 1.0/gpu_frame, total_gpu_time, frames/total_gpu_time); + if (real_frame > 0 && total_real_time > 0) + scr.DebugFontPrintF("[REALTIME] Render+Present+Cruft took %lf ms (%lf FPS) (total %lf s, avg FPS %lf)\n", real_frame*1e3, 1.0/real_frame, total_real_time,frames/total_real_time); + scr.DebugFontPrintF("View bounds: %s\n", view.GetBounds().Str().c_str()); scr.DebugFontPrintF("type of Real == %s\n", g_real_name[REALTYPE]); //#if REALTYPE == REAL_MPFRCPP // scr.DebugFontPrintf("Precision: %s\nRounding: %s\n"); //#endif + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + scr.DebugFontPrint("Doing cumulative coordinate transforms on Objects.\n"); + #else if (view.UsingGPUTransform()) { scr.DebugFontPrint("Doing coordinate transform on the GPU.\n"); @@ -132,6 +139,7 @@ inline void MainLoop(Document & doc, Screen & scr, View & view, int max_frames = { scr.DebugFontPrint("Doing coordinate transform on the CPU.\n"); } + #endif if (view.UsingGPURendering()) { scr.DebugFontPrint("Doing rendering using GPU.\n"); diff --git a/src/objectrenderer.cpp b/src/objectrenderer.cpp index 9327c97..cbceb3a 100644 --- a/src/objectrenderer.cpp +++ b/src/objectrenderer.cpp @@ -24,9 +24,12 @@ ObjectRenderer::ObjectRenderer(const ObjectType & type, const char * vert_glsl_file, const char * frag_glsl_file, const char * geom_glsl_file) : m_type(type), m_shader_program(), m_indexes(), m_buffer_builder(NULL) { - m_shader_program.InitialiseShaders(vert_glsl_file, frag_glsl_file, geom_glsl_file); - m_shader_program.Use(); - glUniform4f(m_shader_program.GetUniformLocation("colour"), 0,0,0,1); //TODO: Allow different colours + if (vert_glsl_file != NULL && frag_glsl_file != NULL && geom_glsl_file != NULL) + { + m_shader_program.InitialiseShaders(vert_glsl_file, frag_glsl_file, geom_glsl_file); + m_shader_program.Use(); + glUniform4f(m_shader_program.GetUniformLocation("colour"), 0,0,0,1); //TODO: Allow different colours + } } /** diff --git a/src/objectrenderer.h b/src/objectrenderer.h index 841040f..5a65363 100644 --- a/src/objectrenderer.h +++ b/src/objectrenderer.h @@ -28,7 +28,7 @@ namespace IPDF { public: /** Construct the ObjectRenderer **/ - ObjectRenderer(const ObjectType & type, const char * vert_glsl_file, const char * frag_glsl_file, const char * geom_glsl_file = ""); + ObjectRenderer(const ObjectType & type, const char * vert_glsl_file="", const char * frag_glsl_file="", const char * geom_glsl_file = ""); virtual ~ObjectRenderer() {} /** @@ -171,6 +171,15 @@ namespace IPDF // do nothing on GPU virtual void RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id) {} }; + + class FakeRenderer : public ObjectRenderer + { + public: + FakeRenderer() : ObjectRenderer(PATH,NULL,NULL,NULL) {} + ~FakeRenderer() {} + virtual void RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id) {} + virtual void RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id) {} + }; } diff --git a/src/paranoidnumber.cpp b/src/paranoidnumber.cpp index 82c7ba4..b2a66f4 100644 --- a/src/paranoidnumber.cpp +++ b/src/paranoidnumber.cpp @@ -6,6 +6,8 @@ #include #include +// here be many copy paste bugs + using namespace std; namespace IPDF { @@ -80,20 +82,9 @@ ParanoidNumber & ParanoidNumber::operator=(const ParanoidNumber & a) for (auto n : a.m_next[i]) m_next[i].push_back(new ParanoidNumber(*n)); } - /* - for (unsigned j = 0; j < m_next[i].size() && j < a.m_next[i].size(); ++j) - { - if (a.m_next[i][j] != NULL) - m_next[i][j]->operator=(*(a.m_next[i][j])); - } - - for (unsigned j = a.m_next[i].size(); j < m_next[i].size(); ++j) - { - delete m_next[i][j]; - } - m_next[i].resize(a.m_next[i].size()); - */ - //} + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(a.Digit(),a.Digit()); + #endif return *this; } @@ -273,88 +264,147 @@ bool TrustingOp(int8_t & a, const int8_t & b, Optype op) ParanoidNumber & ParanoidNumber::operator+=(const digit_t & a) { - - //assert(this != NULL); + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare += a; + #endif delete Operation(new ParanoidNumber(a), ADD); - Simplify(ADD); Simplify(SUBTRACT); + Simplify(ADD); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator-=(const digit_t & a) { + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare -= a; + #endif delete Operation(new ParanoidNumber(a), SUBTRACT); - Simplify(SUBTRACT); Simplify(ADD); + Simplify(SUBTRACT); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator*=(const digit_t & a) { + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare *= a; + #endif delete Operation(new ParanoidNumber(a), MULTIPLY); + Simplify(DIVIDE); Simplify(MULTIPLY); - Simplify(DIVIDE); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator/=(const digit_t & a) { + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare /= a; + #endif delete Operation(new ParanoidNumber(a), DIVIDE); Simplify(MULTIPLY); Simplify(DIVIDE); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator+=(const ParanoidNumber & a) { - - //assert(this != NULL); + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare += a.Digit(); + #endif delete Operation(new ParanoidNumber(a), ADD); - Simplify(ADD); Simplify(SUBTRACT); + Simplify(ADD); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a.Digit()); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator-=(const ParanoidNumber & a) { + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare -= a.Digit(); + #endif delete Operation(new ParanoidNumber(a), SUBTRACT); - Simplify(SUBTRACT); Simplify(ADD); + Simplify(SUBTRACT); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a.Digit()); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator*=(const ParanoidNumber & a) { + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare *= a.Digit(); + #endif delete Operation(new ParanoidNumber(a), MULTIPLY); + Simplify(DIVIDE); Simplify(MULTIPLY); - Simplify(DIVIDE); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a.Digit()); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator/=(const ParanoidNumber & a) { + #ifdef PARANOID_COMPARE_EPSILON + digit_t compare = Digit(); + compare /= a.Digit(); + #endif delete Operation(new ParanoidNumber(a), DIVIDE); Simplify(MULTIPLY); Simplify(DIVIDE); + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(compare, a.Digit()); + #endif return *this; } ParanoidNumber & ParanoidNumber::operator=(const digit_t & a) { + for (int i = 0; i < NOP; ++i) { for (auto n : m_next[i]) delete n; + m_next[i].clear(); } m_value = a; #ifdef PARANOID_CACHE_RESULT m_cached_result = a; #endif + + #ifdef PARANOID_COMPARE_EPSILON + CompareForSanity(a,a); + #endif + return *this; } @@ -366,12 +416,16 @@ ParanoidNumber * ParanoidNumber::OperationTerm(ParanoidNumber * b, Optype op, Pa m_cached_result = NAN; #endif #ifdef PARANOID_SIZE_LIMIT - if (m_size > PARANOID_SIZE_LIMIT) + if (m_size >= PARANOID_SIZE_LIMIT) { if (op == ADD) - m_value += b->Digit(); + { + m_value += b->Digit() / GetFactors(); + } else - m_value -= b->Digit(); + { + m_value -= b->Digit() / GetFactors(); + } return b; } //Debug("At size limit %d", m_size); @@ -493,20 +547,23 @@ ParanoidNumber * ParanoidNumber::OperationTerm(ParanoidNumber * b, Optype op, Pa ParanoidNumber * ParanoidNumber::OperationFactor(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point, Optype * merge_op) { - //assert(SanityCheck()); - //assert(b->SanityCheck()); #ifdef PARANOID_CACHE_RESULTS m_cached_result = NAN; #endif #ifdef PARANOID_SIZE_LIMIT - if (m_size > PARANOID_SIZE_LIMIT) + if (m_size >= PARANOID_SIZE_LIMIT) { if (op == MULTIPLY) m_value *= b->Digit(); else m_value /= b->Digit(); - //Debug("At size limit %d", m_size); + + for (auto n : m_next[ADD]) + delete n->OperationFactor(new ParanoidNumber(*b), op); + for (auto n : m_next[SUBTRACT]) + delete n->OperationFactor(new ParanoidNumber(*b), op); return b; + } #endif @@ -530,7 +587,11 @@ ParanoidNumber * ParanoidNumber::OperationFactor(ParanoidNumber * b, Optype op, } if (b->Floating() && b->m_value == 1) return b; - + if (b->Floating() && b->m_value == 0 && op == MULTIPLY) + { + operator=(*b); + return b; + } if (NoTerms() && b->NoTerms()) @@ -846,6 +907,11 @@ bool ParanoidNumber::SanityCheck(set & visited) const { if (!div->SanityCheck(visited)) return false; + if (div->Digit() == 0) + { + Error("Divide by zero"); + return false; + } } return true; } diff --git a/src/paranoidnumber.h b/src/paranoidnumber.h index b152922..6ad24f2 100644 --- a/src/paranoidnumber.h +++ b/src/paranoidnumber.h @@ -16,12 +16,16 @@ // but let's not do that... -#define PARANOID_CACHE_RESULTS +//#define PARANOID_CACHE_RESULTS //#define PARANOID_USE_ARENA #define PARANOID_SIZE_LIMIT 0 +// Define to compare all ops against double ops and check within epsilon +#define PARANOID_COMPARE_EPSILON 1e-6 +#define CompareForSanity(...) this->ParanoidNumber::CompareForSanityEx(__func__, __FILE__, __LINE__, __VA_ARGS__) + namespace IPDF { typedef enum {ADD, SUBTRACT, MULTIPLY, DIVIDE, NOP} Optype; @@ -184,6 +188,7 @@ namespace IPDF ParanoidNumber operator+(const ParanoidNumber & a) const { ParanoidNumber result(*this); + a.SanityCheck(); result += a; return result; } @@ -197,6 +202,10 @@ namespace IPDF { ParanoidNumber result(*this); result *= a; + if (!result.SanityCheck()) + { + Fatal("Blargh"); + } return result; } ParanoidNumber operator/(const ParanoidNumber & a) const @@ -208,7 +217,15 @@ namespace IPDF std::string Str() const; - + inline void CompareForSanityEx(const char * func, const char * file, int line, const digit_t & compare, const digit_t & arg, const digit_t & eps = PARANOID_COMPARE_EPSILON) + { + if (fabs(Digit() - compare) > eps) + { + Error("Called via %s(%lf) (%s:%d)", func, arg, file, line); + Error("Failed: %s", Str().c_str()); + Fatal("This: %.30lf vs Expected: %.30lf", Digit(), compare); + } + } std::string PStr() const; diff --git a/src/path.cpp b/src/path.cpp index af9fb74..f12e2a7 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -21,6 +21,8 @@ Path::Path(const Objects & objects, unsigned start, unsigned end, const Colour & for (unsigned i = m_start; i <= m_end; ++i) { + if (i >= objects.bounds.size()) + break; const Rect & objb = objects.bounds[i]; if (i == m_start || objb.x < xmin) diff --git a/src/screen.cpp b/src/screen.cpp index e7d5d4b..46a2b73 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -37,7 +37,8 @@ Screen::Screen(bool visible) if (!m_window) { - Fatal("Couldn't create window!"); + Error("Couldn't create window!"); + return; } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); @@ -111,6 +112,8 @@ Screen::Screen(bool visible) Screen::~Screen() { + if (!Valid()) + return; SDL_GL_DeleteContext(m_gl_context); SDL_DestroyWindow(m_window); SDL_Quit(); @@ -118,6 +121,8 @@ Screen::~Screen() void Screen::Clear(float r, float g, float b, float a) { + if (!Valid()) + return; glClearColor(r,g,b,a); glClear(GL_COLOR_BUFFER_BIT); DebugFontClear(); @@ -134,6 +139,9 @@ void Screen::ResizeViewport(int width, int height) bool Screen::PumpEvents() { + if (!Valid()) + return true; + SDL_Event evt; while (SDL_PollEvent(&evt)) @@ -212,6 +220,8 @@ void Screen::SetMouseCursor(Screen::MouseCursors cursor) void Screen::Present() { + if (!Valid()) + return; if (m_debug_font_atlas) DebugFontFlush(); m_last_frame_time = SDL_GetPerformanceCounter() - m_frame_begin_time; @@ -227,7 +237,7 @@ void Screen::Present() double Screen::GetLastFrameTimeGPU() const { - if (!m_last_frame_gpu_timer) + if (!Valid() || !m_last_frame_gpu_timer) return 0; uint64_t frame_time_ns; glGetQueryObjectui64v(m_last_frame_gpu_timer, GL_QUERY_RESULT, &frame_time_ns); @@ -278,6 +288,7 @@ void Screen::RenderPixels(int x, int y, int w, int h, uint8_t *pixels) const void Screen::ScreenShot(const char * filename) const { + if (!Valid()) return; Debug("Attempting to save BMP to file %s", filename); int w = ViewportWidth(); @@ -316,6 +327,7 @@ void Screen::ScreenShot(const char * filename) const */ void Screen::RenderBMP(const char * filename) const { + if (!Valid()) return; if (access(filename, R_OK) == -1) { Error("No such file \"%s\" - Nothing to render - You might have done this deliberately?", filename); @@ -384,6 +396,8 @@ void Screen::RenderBMP(const char * filename) const void Screen::DebugFontInit(const char *name, float font_size) { + if (!Valid()) return; + unsigned char font_atlas_data[1024*1024]; FILE *font_file = fopen(name, "rb"); fseek(font_file, 0, SEEK_END); @@ -415,6 +429,7 @@ void Screen::DebugFontInit(const char *name, float font_size) void Screen::DebugFontClear() { + if (!Valid()) return; m_debug_font_x = m_debug_font_y = 0; if (!m_debug_font_atlas) return; DebugFontPrint("\n"); @@ -422,6 +437,7 @@ void Screen::DebugFontClear() void Screen::DebugFontFlush() { + if (!Valid()) return; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 40, -1, "Screen::DebugFontFlush()"); glEnable(GL_BLEND); @@ -461,6 +477,7 @@ struct fontvertex void Screen::DebugFontPrint(const char* str) { + if (!Valid()) return; if (!m_debug_font_atlas || !m_show_debug_font) return; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 41, -1, "Screen::DebugFontPrint()"); diff --git a/src/screen.h b/src/screen.h index e24bc3f..bdadd8e 100644 --- a/src/screen.h +++ b/src/screen.h @@ -75,6 +75,8 @@ namespace IPDF void ShowDebugFont(bool show = true) {m_show_debug_font = show;} bool DebugFontShown() const {return m_show_debug_font;} + + bool Valid() const {return m_window != NULL;} private: void ResizeViewport(int width, int height); void DebugFontFlush(); diff --git a/src/svg-tests/cat.svg b/src/svg-tests/cat.svg new file mode 100644 index 0000000..6357fa6 --- /dev/null +++ b/src/svg-tests/cat.svg @@ -0,0 +1,335 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/svg-tests/cat2.svg b/src/svg-tests/cat2.svg new file mode 100644 index 0000000..b4e6f19 --- /dev/null +++ b/src/svg-tests/cat2.svg @@ -0,0 +1,371 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/svg-tests/panda.svg b/src/svg-tests/panda.svg new file mode 100644 index 0000000..246c9c3 --- /dev/null +++ b/src/svg-tests/panda.svg @@ -0,0 +1,136 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/tests/realops.cpp b/src/tests/realops.cpp index 997eb64..dd45aa8 100644 --- a/src/tests/realops.cpp +++ b/src/tests/realops.cpp @@ -12,7 +12,7 @@ using namespace IPDF; static double g_totalerror = 0; -bool NotEqual(double a, double b, double threshold=1e-1) +bool NotEqual(double a, double b, double threshold=1e-4) { double error = fabs(a-b); g_totalerror += error; @@ -116,7 +116,18 @@ int main(int argc, char ** argv) { failures++; Warn("a /= b = %f should be %f, a before op was %f", Double(a), da, Double(abeforeop)); + } + if (NotEqual(Double(a*0.0 + 1.0), da*0.0 + 1.0)) + { + failures++; + Warn("a * 0 = %f should be %f, a before op was %f", Double(a), da, Double(abeforeop)); } + + if (NotEqual(Double(a=b), da=db)) + { + failures++; + Warn("a = b = %f should be %f, a before op was %f", Double(a), da, Double(abeforeop)); + } if (failures > old_failures) { diff --git a/src/view.cpp b/src/view.cpp index b8e1019..1f855a2 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -28,13 +28,23 @@ View::View(Document & document, Screen & screen, const Rect & bounds, const Colo screen.SetView(this); // oh dear... + + // Create ObjectRenderers - new's match delete's in View::~View //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(); - m_object_renderers[PATH] = new PathRenderer(); + if (screen.Valid()) + { + 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(); + m_object_renderers[PATH] = new PathRenderer(); + } + else + { + for (int i = RECT_FILLED; i <= PATH; ++i) + m_object_renderers[i] = new FakeRenderer(); + } // To add rendering for a new type of object; // 1. Add enum to ObjectType in ipdf.h @@ -69,14 +79,19 @@ View::~View() */ void View::Translate(Real x, Real y) { + if (!m_use_gpu_transform) + m_buffer_dirty = true; + m_bounds_dirty = true; + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + m_document.TranslateObjects(-x, -y); + #endif x *= m_bounds.w; y *= m_bounds.h; m_bounds.x += x; m_bounds.y += y; //Debug("View Bounds => %s", m_bounds.Str().c_str()); - if (!m_use_gpu_transform) - m_buffer_dirty = true; - m_bounds_dirty = true; + + } /** @@ -101,8 +116,18 @@ void View::SetBounds(const Rect & bounds) */ void View::ScaleAroundPoint(Real x, Real y, Real scale_amount) { + + // (x0, y0, w, h) -> (x*w - (x*w - x0)*s, y*h - (y*h - y0)*s, w*s, h*s) // x and y are coordinates in the window // Convert to local coords. + if (!m_use_gpu_transform) + m_buffer_dirty = true; + m_bounds_dirty = true; + + + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + m_document.ScaleObjectsAboutPoint(x, y, scale_amount); + #endif x *= m_bounds.w; y *= m_bounds.h; x += m_bounds.x; @@ -119,9 +144,8 @@ void View::ScaleAroundPoint(Real x, Real y, Real scale_amount) m_bounds.w *= scale_amount; m_bounds.h *= scale_amount; //Debug("Scale at {%s, %s} by %s View Bounds => %s", x.Str().c_str(), y.Str().c_str(), scale_amount.Str().c_str(), m_bounds.Str().c_str()); - if (!m_use_gpu_transform) - m_buffer_dirty = true; - m_bounds_dirty = true; + + } /** @@ -132,6 +156,9 @@ void View::ScaleAroundPoint(Real x, Real y, Real scale_amount) */ Rect View::TransformToViewCoords(const Rect& inp) const { + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + return inp; + #endif Rect out; out.x = (inp.x - m_bounds.x) / m_bounds.w; out.y = (inp.y - m_bounds.y) / m_bounds.h; @@ -149,6 +176,7 @@ Rect View::TransformToViewCoords(const Rect& inp) const */ void View::Render(int width, int height) { + if (!m_screen.Valid()) return; glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION,42,-1, "Beginning View::Render()"); // View dimensions have changed (ie: Window was resized) int prev_width = m_cached_display.GetWidth(); @@ -371,8 +399,13 @@ void View::RenderRange(int width, int height, unsigned first_obj, unsigned last_ if (m_use_gpu_transform) { + #ifdef TRANSFORM_OBJECTS_NOT_VIEW + GLfloat glbounds[] = {0.0f, 0.0f, 1.0f, 1.0f, + 0.0f, 0.0f, float(width), float(height)}; + #else GLfloat glbounds[] = {static_cast(Float(m_bounds.x)), static_cast(Float(m_bounds.y)), static_cast(Float(m_bounds.w)), static_cast(Float(m_bounds.h)), 0.0, 0.0, static_cast(width), static_cast(height)}; + #endif m_bounds_ubo.Upload(sizeof(float)*8, glbounds); } else diff --git a/src/view.h b/src/view.h index 9044306..6be1ebf 100644 --- a/src/view.h +++ b/src/view.h @@ -10,6 +10,8 @@ #define USE_GPU_RENDERING true #define USE_SHADING !(USE_GPU_RENDERING) && true +#define TRANSFORM_OBJECTS_NOT_VIEW + namespace IPDF { class Screen; -- 2.20.1