From: Sam Moore Date: Mon, 15 Sep 2014 17:26:00 +0000 (+0800) Subject: Change to things to make performance testing easier X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=6ce000e7212d9f5db6e5998c41df15bcad2022c8;p=ipdf%2Fcode.git Change to things to make performance testing easier - Saving of CPU and GPU BMPs - OverlayBMP doesn't overlay a BMP which is good because we don't want it to... - SVG loads in centre of view - This will break the Quad tree, probably - tests/bmpdiff allows us to quantitatively compare output bmp images. - I didn't say it was a good metric, there's just a metric now. - Arguments to set rendering and transform type, argument to set maximum number of frames to render, argument to turn off the lazy rendering. Will make some kind of ipython notebook next I guess. --- diff --git a/.gitignore b/.gitignore index 7973e79..f415fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ bin/ipdf data/* src/moc_controlpanel.cpp +src/tests/* +!src/tests/*.cpp +!src/tests/*.h diff --git a/src/Makefile b/src/Makefile index 38f69fd..03cf3aa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -104,7 +104,7 @@ movie : $(BIN) ../tools/stream_plot.py # To change that you can run as `make DEFS="REALTYPE=X" tests/` where X is your chosen type # But remember to make clean first. tests/% : tests/%.cpp ../obj/tests/%.o $(LINKOBJ) - $(CXX) $(CFLAGS) -o $@.test $(LINKOBJ) ../obj/$@.o $(LIB) $(TESTRPATH) + $(CXX) $(CFLAGS) -o $@ $(LINKOBJ) ../obj/$@.o $(LIB) $(TESTRPATH) -include $(DEPS) diff --git a/src/controlpanel.cpp b/src/controlpanel.cpp index e4e68d6..f27bc7a 100644 --- a/src/controlpanel.cpp +++ b/src/controlpanel.cpp @@ -136,10 +136,16 @@ QMenu * ControlPanel::CreateScreenMenu() m_screen_cpu_rendering = new QAction("&CPU Rendering", this); m_screen_cpu_rendering->setCheckable(true); m_screen_gpu_rendering->setToolTip("Uses the CPU for Rendering"); + + m_screen_lazy_rendering = new QAction("&Lazy Rendering", this); + m_screen_lazy_rendering->setCheckable(true); screen->addAction(m_screen_gpu_rendering); screen->addAction(m_screen_cpu_rendering); + screen->addAction(m_screen_lazy_rendering); + connect(m_screen_lazy_rendering, SIGNAL(triggered()), this, SLOT(ToggleLazyRendering())); + connect(m_screen_gpu_rendering, SIGNAL(triggered()), this, SLOT(SetGPURendering())); connect(m_screen_cpu_rendering, SIGNAL(triggered()), this, SLOT(SetCPURendering())); @@ -171,6 +177,7 @@ void ControlPanel::UpdateAll() m_screen_gpu_rendering->setChecked(using_gpu_rendering); m_screen_cpu_rendering->setChecked(!using_gpu_rendering); m_screen_show_debug->setChecked(m_screen.DebugFontShown()); + m_screen_lazy_rendering->setChecked(m_view.UsingLazyRendering()); m_view_show_bezier_bounds->setChecked(m_view.ShowingBezierBounds()); m_view_show_bezier_type->setChecked(m_view.ShowingBezierType()); @@ -264,6 +271,13 @@ void ControlPanel::SetCPURendering() UpdateAll(); } +void ControlPanel::ToggleLazyRendering() +{ + bool state = m_view.UsingLazyRendering(); + m_view.SetLazyRendering(!state); + UpdateAll(); +} + void ControlPanel::ToggleScreenDebugFont() { bool state = m_screen.DebugFontShown(); @@ -303,6 +317,9 @@ void ControlPanel::InsertTextIntoDocument() void ControlPanel::InsertSVGIntoDocument() { Rect bounds(m_view.GetBounds()); + bounds.x += bounds.w/Real(2); + bounds.y += bounds.h/Real(2); + bounds.w /= Real(m_screen.ViewportWidth()); bounds.h /= Real(m_screen.ViewportHeight()); @@ -320,6 +337,9 @@ void ControlPanel::LoadSVGIntoDocument() return; Rect bounds(m_view.GetBounds()); + bounds.x += bounds.w/Real(2); + bounds.y += bounds.h/Real(2); + bounds.w /= Real(m_screen.ViewportWidth()); bounds.h /= Real(m_screen.ViewportHeight()); diff --git a/src/controlpanel.h b/src/controlpanel.h index 67eff64..ce7c3d9 100644 --- a/src/controlpanel.h +++ b/src/controlpanel.h @@ -61,6 +61,7 @@ namespace IPDF private slots: void SetGPURendering(); void SetCPURendering(); + void ToggleLazyRendering(); void ToggleShowBezierBounds(); void ToggleShowBezierType(); void ToggleShowFillBounds(); @@ -104,6 +105,7 @@ namespace IPDF QAction * m_screen_gpu_rendering; QAction * m_screen_cpu_rendering; QAction * m_screen_show_debug; + QAction * m_screen_lazy_rendering; QAction * m_document_set_font; QAction * m_document_insert_text; @@ -116,7 +118,6 @@ namespace IPDF QAction * m_view_show_fill_points; QAction * m_view_enable_shading; - QTextEdit * m_text_edit; QPushButton * m_ok_button; diff --git a/src/document.cpp b/src/document.cpp index 5890730..1558865 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -392,12 +392,12 @@ static void GetXYPair(const string & d, Real & x, Real & y, unsigned & i,const s { string token(""); while (GetToken(d, token, i, delims) == ","); - x = strtod(token.c_str(),NULL); + x = RealFromStr(token); if (GetToken(d, token, i, delims) != ",") { Fatal("Expected \",\" seperating x,y pair"); } - y = strtod(GetToken(d, token, i, delims).c_str(),NULL); + y = RealFromStr(GetToken(d,token,i,delims)); } static bool GetKeyValuePair(const string & d, string & key, string & value, unsigned & i, const string & delims = "()[],{}<>;:=") @@ -460,11 +460,11 @@ void Document::ParseSVGTransform(const string & s, SVGMatrix & transform) } else if (command == "scale") { - delta.a = (strtod(GetToken(s,token,i).c_str(), NULL)); + delta.a = RealFromStr(GetToken(s,token,i)); GetToken(s, token, i); if (token == ",") { - delta.d = (strtod(GetToken(s,token,i).c_str(), NULL)); + delta.d = RealFromStr(GetToken(s,token,i)); assert(GetToken(s, token, i) == ")"); } else @@ -510,7 +510,7 @@ inline Colour ParseColourString(const string & colour_str) c = {255,255,255,255}; else if (colour_str.size() == 7 && colour_str[0] == '#') { - Debug("Parse colour string: \"%s\"", colour_str.c_str()); + //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]; @@ -518,7 +518,7 @@ inline Colour ParseColourString(const string & colour_str) 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); + //Debug("Colour is: %u, %u, %u, %u", c.r, c.g, c.b, c.a); } return c; } @@ -527,7 +527,16 @@ void Document::ParseSVGNode(pugi::xml_node & root, SVGMatrix & parent_transform) { //Debug("Parse node <%s>", root.name()); - + + // Centre the SVGs + if (strcmp(root.name(),"svg") == 0) + { + Real ww = RealFromStr(root.attribute("width").as_string()); + Real hh = RealFromStr(root.attribute("height").as_string()); + parent_transform.e -= parent_transform.a * ww/Real(2); + parent_transform.f -= parent_transform.d * hh/Real(2); + } + for (pugi::xml_node child = root.first_child(); child; child = child.next_sibling()) { SVGMatrix transform(parent_transform); @@ -709,7 +718,7 @@ void Document::LoadSVG(const string & filename, const Rect & bounds) input.close(); // a c e, b d f - SVGMatrix transform = {bounds.w, 0,bounds.x, 0,bounds.h,bounds.y}; + SVGMatrix transform = {bounds.w,0 ,bounds.x, 0,bounds.h,bounds.y}; ParseSVGNode(doc_xml, transform); } @@ -755,9 +764,9 @@ pair Document::ParseSVGPathData(const string & d, const SVGM if (command == "m" || command == "M") { //Debug("Construct moveto command"); - Real dx = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + Real dx = RealFromStr(GetToken(d,token,i,delims)); assert(GetToken(d,token,i,delims) == ","); - Real dy = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + Real dy = RealFromStr(GetToken(d,token,i,delims)); x[0] = (relative) ? x[0] + dx : dx; y[0] = (relative) ? y[0] + dy : dy; @@ -770,25 +779,25 @@ pair Document::ParseSVGPathData(const string & d, const SVGM else if (command == "c" || command == "C" || command == "q" || command == "Q") { //Debug("Construct curveto command"); - Real dx = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + Real dx = RealFromStr(GetToken(d,token,i,delims)); assert(GetToken(d,token,i,delims) == ","); - Real dy = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + Real dy = RealFromStr(GetToken(d,token,i,delims)); x[1] = (relative) ? x[0] + dx : dx; y[1] = (relative) ? y[0] + dy : dy; - dx = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + dx = RealFromStr(GetToken(d,token,i,delims)); assert(GetToken(d,token,i,delims) == ","); - dy = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + dy = RealFromStr(GetToken(d,token,i,delims)); x[2] = (relative) ? x[0] + dx : dx; y[2] = (relative) ? y[0] + dy : dy; if (command != "q" && command != "Q") { - dx = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + dx = RealFromStr(GetToken(d,token,i,delims)); assert(GetToken(d,token,i,delims) == ","); - dy = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + dy = RealFromStr(GetToken(d,token,i,delims)); x[3] = (relative) ? x[0] + dx : dx; y[3] = (relative) ? y[0] + dy : dy; } @@ -821,12 +830,12 @@ pair Document::ParseSVGPathData(const string & d, const SVGM { //Debug("Construct lineto command, relative %d", relative); - Real dx = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + Real dx = RealFromStr(GetToken(d,token,i,delims)); Real dy; if (command == "l" || command == "L") { assert(GetToken(d,token,i,delims) == ","); - dy = Real(strtod(GetToken(d,token,i,delims).c_str(),NULL)); + dy = RealFromStr(GetToken(d,token,i,delims)); } else if (command == "v" || command == "V") { diff --git a/src/main.cpp b/src/main.cpp index d65dc60..a2693d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,10 +7,20 @@ #define _GNU_SOURCE #endif #include +#include +bool ignore_sigfpe = false; + +void sigfpe_handler(int sig) +{ + if (!ignore_sigfpe) + Fatal("Floating point exception!"); + exit(EXIT_SUCCESS); +} int main(int argc, char ** argv) { + signal(SIGFPE, sigfpe_handler); #if REALTYPE == REAL_IRRAM iRRAM_initialize(argc,argv); #endif @@ -31,11 +41,23 @@ int main(int argc, char ** argv) Colour c(0,0,0,1); - const char * input_bmp = NULL; + const char * output_bmp = NULL; const char * input_filename = NULL; + const char * input_text = NULL; float b[4] = {0,0,1,1}; + int max_frames = -1; + bool hide_control_panel; + bool lazy_rendering = true; + + + Screen scr; + View view(doc,scr, {0,0,1,1}); + + if (!lazy_rendering) + view.SetLazyRendering(false); + int i = 0; while (++i < argc) { @@ -48,13 +70,10 @@ int main(int argc, char ** argv) { case 'o': mode = OUTPUT_TO_BMP; - if (++i >= argc) - Fatal("No input argument following -o switch"); - input_bmp = argv[i]; if (++i >= argc) Fatal("No output argument following -o switch"); output_bmp = argv[i]; - + hide_control_panel = true; break; case 'b': { @@ -71,39 +90,113 @@ int main(int argc, char ** argv) i += 4; break; } + case 't': + { + if (++i >= argc) + Fatal("No text input following -t switch"); + input_text = argv[i]; + Debug("Insert text: %s", input_text); + break; + } + + case 'r': + { + if (++i >= argc) + Fatal("Expected \"gpu\" or \"cpu\" after -r switch"); + if (strcmp(argv[i], "gpu") == 0) + { + view.SetGPURendering(true); + } + else if (strcmp(argv[i], "cpu") == 0) + { + view.SetGPURendering(false); + } + else + { + Fatal("Expected \"gpu\" or \"cpu\" after -r switch, not \"%s\"", argv[i]); + } + break; + } + + case 'T': + { + if (++i >= argc) + Fatal("Expected \"gpu\" or \"cpu\" after -T switch"); + if (strcmp(argv[i], "gpu") == 0) + { + view.SetGPUTransform(true); + } + else if (strcmp(argv[i], "cpu") == 0) + { + view.SetGPUTransform(false); + } + else + { + Fatal("Expected \"gpu\" or \"cpu\" after -T switch, not \"%s\"", argv[i]); + } + break; + } + + + case 'l': + view.SetLazyRendering(!view.UsingLazyRendering()); + break; + + case 'f': + if (++i >= argc) + Fatal("No frame number following -f switch"); + max_frames = strtol(argv[i], NULL, 10); + hide_control_panel = true; + break; + + case 'q': + hide_control_panel = true; + break; + } } + Rect bounds(b[0],b[1],b[2],b[3]); + view.SetBounds(bounds); if (input_filename != NULL) { - doc.LoadSVG(input_filename, Rect(0,0,Real(1)/Real(800),Real(1)/Real(600))); + + 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))); + } + else if (input_text != NULL) + { + doc.AddText(input_text, bounds.h/Real(2), bounds.x, bounds.y+bounds.h/Real(2)); } - else + else { doc.Add(RECT_OUTLINE, Rect(0,0,0,0),0); // hack to stop segfault if document is empty (:S) } - Debug("Start!"); - Rect bounds(b[0],b[1],b[2],b[3]); - - Screen scr; - View view(doc,scr, bounds); + #ifndef CONTROLPANEL_DISABLED + SDL_Thread * cp_thread = NULL; + if (!hide_control_panel) + { ControlPanel::RunArgs args = {argc, argv, view, doc, scr}; - SDL_Thread * cp_thread = SDL_CreateThread(ControlPanel::Run, "ControlPanel", &args); + cp_thread = SDL_CreateThread(ControlPanel::Run, "ControlPanel", &args); if (cp_thread == NULL) { Error("Couldn't create ControlPanel thread: %s", SDL_GetError()); } + } #endif //CONTROLPANEL_DISABLED if (mode == LOOP) - MainLoop(doc, scr, view); + MainLoop(doc, scr, view, max_frames); else if (mode == OUTPUT_TO_BMP) //TODO: Remove this shit - OverlayBMP(doc, input_bmp, output_bmp, bounds, c); + { + if (view.UsingGPURendering()) + OverlayBMP(doc, output_bmp, output_bmp, bounds, c); + else + view.SaveCPUBMP(output_bmp); + } #ifndef CONTROLPANEL_DISABLED - if (cp_thread != NULL) { int cp_return; @@ -113,5 +206,7 @@ int main(int argc, char ** argv) Debug("ControlPanel thread returned %d", cp_return); } #endif //CONTROLPANEL_DISABLED + + ignore_sigfpe = true; return 0; } diff --git a/src/main.h b/src/main.h index d20ea52..9929d69 100644 --- a/src/main.h +++ b/src/main.h @@ -79,7 +79,7 @@ void RatCatcher(int x, int y, int buttons, int wheel, Screen * scr, View * view) } -inline void MainLoop(Document & doc, Screen & scr, View & view) +inline void MainLoop(Document & doc, Screen & scr, View & view, int max_frames = -1) { // order is important... segfaults occur when screen (which inits GL) is not constructed first -_- @@ -98,7 +98,8 @@ inline void MainLoop(Document & doc, Screen & scr, View & view) double data_rate = 0; // period between data output to stdout (if <= 0 there will be no output) uint64_t data_points = 0; setbuf(stdout, NULL); - while (scr.PumpEvents()) + int frame_number = 0; + while (scr.PumpEvents() && (max_frames < 0 || frame_number++ < max_frames)) { real_clock_prev = real_clock_now; ++frames; diff --git a/src/real.h b/src/real.h index 9ea302d..5d4e133 100644 --- a/src/real.h +++ b/src/real.h @@ -46,18 +46,23 @@ namespace IPDF #if REALTYPE == REAL_SINGLE typedef float Real; + inline Real RealFromStr(const char * str) {return strtof(str, NULL);} #elif REALTYPE == REAL_DOUBLE typedef double Real; + inline Real RealFromStr(const char * str) {return strtod(str, NULL);} #elif REALTYPE == REAL_LONG_DOUBLE typedef long double Real; + inline Real RealFromStr(const char * str) {return strtold(str, NULL);} #elif REALTYPE == REAL_VFPU typedef VFPU::VFloat Real; inline float Float(const Real & r) {return r.m_value;} inline double Double(const Real & r) {return r.m_value;} + inline Real RealFromStr(const char * str) {return Real(strtod(str, NULL));} #elif REALTYPE == REAL_RATIONAL typedef Rational Real; inline float Float(const Real & r) {return (float)r.ToDouble();} inline double Double(const Real & r) {return r.ToDouble();} + inline Real RealFromStr(const char * str) {return Real(strtod(str, NULL));} #elif REALTYPE == REAL_RATIONAL_ARBINT #define ARBINT Gmpint // Set to Gmpint or Arbint here @@ -73,12 +78,14 @@ namespace IPDF inline int64_t Int64(const Real & r) {return r.toLong();} inline Real Sqrt(const Real & r) {return mpfr::sqrt(r, mpfr::mpreal::get_default_rnd());} inline Real Abs(const Real & r) {return mpfr::abs(r, mpfr::mpreal::get_default_rnd());} + inline Real RealFromStr(const char * str) {return Real(strtod(str, NULL));} #elif REALTYPE == REAL_IRRAM typedef iRRAM::REAL Real; inline double Double(const Real & r) {return r.as_double(53);} inline float Float(const Real & r) {return r.as_double(53);} inline int64_t Int64(const Real & r) {return (int64_t)r.as_double(53);} inline Real Sqrt(const Real & r) {return iRRAM::sqrt(r);} + inline Real RealFromStr(const char * str) {return Real(strtod(str, NULL));} #else #error "Type of Real unspecified." #endif //REALTYPE @@ -137,6 +144,7 @@ namespace IPDF }; + inline Real RealFromStr(const std::string & str) {return RealFromStr(str.c_str());} diff --git a/src/tests/bmpdiff.cpp b/src/tests/bmpdiff.cpp new file mode 100644 index 0000000..02ab568 --- /dev/null +++ b/src/tests/bmpdiff.cpp @@ -0,0 +1,75 @@ +/** + * Get diff of bmps + */ + +#include +#include +#include "ipdf.h" + +using namespace std; +using namespace IPDF; + + +int main(int argc, char ** argv) +{ + SDL_Surface * orig = SDL_LoadBMP(argv[1]); + SDL_Surface * diff = SDL_LoadBMP(argv[2]); + + assert(orig->w == diff->w && orig->h == diff->h); + int w = diff->w; + int h = diff->h; + + uint8_t * a = (uint8_t*)orig->pixels; + uint8_t * b = (uint8_t*)diff->pixels; + + int total_diff = 0; + + int mean_dist = 0; + int count_a = 0; + int count_b = 0; + for (int x = 0; x < w; ++x) + { + for (int y = 0; y < h; ++y) + { + if (a[4*(x + w*y)] == 0) + ++count_a; + if (b[4*(x + w*y)] == 0) + ++count_b; + + if (a[4*(x + w*y)] != b[4*(x + w*y)] && b[4*(x+w*y)] == 0) + { + total_diff++; + // + + int r = 1; + for (r=1; r+x < w; ++r) + { + if (a[4*(x+r + w*y)] == 0) + break; + } + int l = 1; + for (l=1; x-l >= 0; ++l) + { + if (a[4*(x-l + w*y)] == 0) + break; + } + int u = 1; + for (u=1; y-u >= 0; ++u) + { + if (a[4*(x + w*(y-u))] == 0) + break; + } + + int d = 1; + for (d=1; y+d < h; ++d) + { + if (a[4*(x + w*(y+d))] == 0) + break; + } + + mean_dist += min(min(d,u), min(r,l)); + } + } + } + printf("%d\t%d\t%d\t%lf\n", count_a, count_b, total_diff, (double)mean_dist / (double)count_b); +} diff --git a/src/view.cpp b/src/view.cpp index 52a4c6d..47d28c1 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -160,7 +160,7 @@ void View::Render(int width, int height) } // View bounds have not changed; blit the FrameBuffer as it is - if (!m_bounds_dirty) + if (!m_bounds_dirty && m_lazy_rendering) { m_cached_display.UnBind(); m_cached_display.Blit(); @@ -171,7 +171,7 @@ void View::Render(int width, int height) m_cached_display.Clear(); #ifndef QUADTREE_DISABLED - if (m_bounds_dirty) + if (m_bounds_dirty || !m_lazy_rendering) { if ( false && (m_bounds.x > 1.0 || m_bounds.x < 0.0 || m_bounds.y > 1.0 || m_bounds.y < 0.0 || m_bounds.w > 1.0 || m_bounds.h > 1.0)) { @@ -363,7 +363,7 @@ void View::RenderRange(int width, int height, unsigned first_obj, unsigned last_ if (m_render_dirty) // document has changed PrepareRender(); - if (m_buffer_dirty || m_bounds_dirty) // object bounds have changed + if (m_buffer_dirty || m_bounds_dirty || !m_lazy_rendering) // object bounds have changed UpdateObjBoundsVBO(first_obj, last_obj); if (m_use_gpu_transform) @@ -498,3 +498,12 @@ void View::PrepareRender() dynamic_cast(m_object_renderers[BEZIER])->PrepareBezierGPUBuffer(m_document.m_objects); m_render_dirty = false; } + +void View::SaveCPUBMP(const char * filename) +{ + bool prev = UsingGPURendering(); + SetGPURendering(false); + Render(800, 600); + ObjectRenderer::SaveBMP({m_cpu_rendering_pixels, 800, 600}, filename); + SetGPURendering(prev); +} diff --git a/src/view.h b/src/view.h index d99db6e..e6b4dbb 100644 --- a/src/view.h +++ b/src/view.h @@ -39,6 +39,7 @@ namespace IPDF const bool UsingGPURendering() const { return m_use_gpu_rendering; } // whether GPU shaders are used or CPU rendering void ToggleGPUTransform() { m_use_gpu_transform = (!m_use_gpu_transform); m_bounds_dirty = true; m_buffer_dirty = true; } void ToggleGPURendering() { m_use_gpu_rendering = (!m_use_gpu_rendering); m_bounds_dirty = true; m_buffer_dirty = true; } + void SetGPUTransform(bool state) {m_use_gpu_transform = state; m_bounds_dirty = true; m_buffer_dirty = true;} void SetGPURendering(bool state) {m_use_gpu_rendering = state; m_bounds_dirty = true; m_buffer_dirty = true;} @@ -57,6 +58,11 @@ namespace IPDF void ForceBoundsDirty() {m_bounds_dirty = true;} void ForceBufferDirty() {m_buffer_dirty = true;} void ForceRenderDirty() {m_render_dirty = true;} + + void SetLazyRendering(bool state = true) {m_lazy_rendering = state;} + bool UsingLazyRendering() const {return m_lazy_rendering;} + + void SaveCPUBMP(const char * filename); private: struct GPUObjBounds @@ -100,6 +106,8 @@ namespace IPDF bool m_show_bezier_type; bool m_show_fill_points; bool m_show_fill_bounds; + + bool m_lazy_rendering;// don't redraw frames unless we need to #ifndef QUADTREE_DISABLED