2 #include "bufferbuilder.h"
6 #ifndef CONTROLPANEL_DISABLED
7 #include "controlpanel.h"
8 #endif //CONTROLPANEL_DISABLED
11 #ifdef TRANSFORM_BEZIERS_TO_PATH
12 #ifndef TRANSFORM_OBJECTS_NOT_VIEW
13 //#error Cannot TRANSFORM_BEZIERS_TO_PATH _without_ TRANSFORM_OBJECTS_NOT_VIEW
22 * Allocates memory for ObjectRenderers
23 * @param document - The document to associate the View with
24 * @param bounds - Initial bounds of the View
25 * @param colour - Colour to use for rendering this view. TODO: Make sure this actually works, or just remove it
27 View::View(Document & document, Screen & screen, const VRect & bounds, const Colour & colour)
28 : m_use_gpu_transform(false), m_use_gpu_rendering(USE_GPU_RENDERING), m_bounds_dirty(true), m_buffer_dirty(true),
29 m_render_dirty(true), m_document(document), m_screen(screen), m_cached_display(), m_bounds(bounds), m_colour(colour), m_bounds_ubo(),
30 m_objbounds_vbo(), m_object_renderers(NUMBER_OF_OBJECT_TYPES), m_cpu_rendering_pixels(NULL),
31 m_perform_shading(USE_SHADING), m_show_bezier_bounds(false), m_show_bezier_type(false),
32 m_show_fill_points(false), m_show_fill_bounds(false), m_lazy_rendering(true),
33 m_query_gpu_bounds_on_next_frame(NULL)
35 Debug("View Created - Bounds => {%s}", m_bounds.Str().c_str());
37 screen.SetView(this); // oh dear...
41 // Create ObjectRenderers - new's match delete's in View::~View
42 //TODO: Don't forget to put new renderers here or things will be segfaultastic
45 m_object_renderers[RECT_FILLED] = new RectFilledRenderer();
46 m_object_renderers[RECT_OUTLINE] = new RectOutlineRenderer();
47 m_object_renderers[CIRCLE_FILLED] = new CircleFilledRenderer();
48 m_object_renderers[BEZIER] = new BezierRenderer();
49 m_object_renderers[PATH] = new PathRenderer();
53 for (int i = RECT_FILLED; i <= PATH; ++i)
54 m_object_renderers[i] = new FakeRenderer();
57 // To add rendering for a new type of object;
58 // 1. Add enum to ObjectType in ipdf.h
59 // 2. Implement class inheriting from ObjectRenderer using that type in objectrenderer.h and objectrenderer.cpp
64 #ifndef QUADTREE_DISABLED
65 m_quadtree_max_depth = 2;
66 m_current_quadtree_node = document.GetQuadTree().root_id;
72 * Frees memory used by ObjectRenderers
76 for (unsigned i = 0; i < m_object_renderers.size(); ++i)
78 delete m_object_renderers[i]; // delete's match new's in constructor
80 m_object_renderers.clear();
81 delete [] m_cpu_rendering_pixels;
86 * @param x, y - Amount to translate
88 void View::Translate(Real x, Real y)
90 if (!m_use_gpu_transform)
91 m_buffer_dirty = true;
92 m_bounds_dirty = true;
93 #ifdef TRANSFORM_OBJECTS_NOT_VIEW
94 ObjectType type = NUMBER_OF_OBJECT_TYPES;
95 #ifdef TRANSFORM_BEZIERS_TO_PATH
98 m_document.TranslateObjects(-x, -y, type);
100 m_bounds.x += m_bounds.w*VReal(x);
101 m_bounds.y += m_bounds.h*VReal(y);
102 //Debug("View Bounds => %s", m_bounds.Str().c_str());
109 * @param bounds - New bounds
111 void View::SetBounds(const Rect & bounds)
113 #ifdef TRANSFORM_OBJECTS_NOT_VIEW
114 ObjectType type = NUMBER_OF_OBJECT_TYPES;
115 #ifdef TRANSFORM_BEZIERS_TO_PATH
118 SVGMatrix transform = {Real(m_bounds.w)/bounds.w, 0, Real(m_bounds.x) - bounds.x, 0,Real(m_bounds.h)/bounds.h, Real(m_bounds.y) - bounds.y};
119 m_document.TransformObjectBounds(transform, type);
121 m_bounds.x = bounds.x;
122 m_bounds.y = bounds.y;
123 m_bounds.w = bounds.w;
124 m_bounds.h = bounds.h;
125 if (!m_use_gpu_transform)
126 m_buffer_dirty = true;
127 m_bounds_dirty = true;
131 * Scale the View at a point
132 * @param x, y - Coordinates to scale at (eg: Mouse cursor position)
133 * @param scale_amount - Amount to scale by
135 void View::ScaleAroundPoint(Real x, Real y, Real scale_amount)
138 // (x0, y0, w, h) -> (x*w - (x*w - x0)*s, y*h - (y*h - y0)*s, w*s, h*s)
139 // x and y are coordinates in the window
140 // Convert to local coords.
141 if (!m_use_gpu_transform)
142 m_buffer_dirty = true;
143 m_bounds_dirty = true;
146 #ifdef TRANSFORM_OBJECTS_NOT_VIEW
147 ObjectType type = NUMBER_OF_OBJECT_TYPES;
148 #ifdef TRANSFORM_BEZIERS_TO_PATH
151 m_document.ScaleObjectsAboutPoint(x, y, scale_amount, type);
153 VReal vx = m_bounds.w * VReal(x);
154 VReal vy = m_bounds.h * VReal(y);
158 VReal top = vy - m_bounds.y;
159 VReal left = vx - m_bounds.x;
162 left *= scale_amount;
164 m_bounds.x = vx - left;
165 m_bounds.y = vy - top;
166 m_bounds.w *= scale_amount;
167 m_bounds.h *= scale_amount;
168 if (m_bounds.w == VReal(0))
170 Debug("Scaled to zero!!!");
172 //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());
178 * Transform a point in the document to a point relative to the top left corner of the view
179 * This is the CPU coordinate transform code; used only if the CPU is doing coordinate transforms
180 * @param inp - Input Rect {x,y,w,h} in the document
181 * @returns output Rect {x,y,w,h} in the View
183 Rect View::TransformToViewCoords(const Rect& inp) const
185 #ifdef TRANSFORM_OBJECTS_NOT_VIEW
188 return TransformRectCoordinates(m_bounds.Convert<Real>(), inp);
193 * Updates FrameBuffer if the document, object bounds, or view bounds have changed, then Blits it
194 * Otherwise just Blits the cached FrameBuffer
195 * @param width - Width of View to render
196 * @param height - Height of View to render
198 void View::Render(int width, int height)
200 if (!m_screen.Valid()) return;
201 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION,42,-1, "Beginning View::Render()");
202 // View dimensions have changed (ie: Window was resized)
203 int prev_width = m_cached_display.GetWidth();
204 int prev_height = m_cached_display.GetHeight();
205 if (width != prev_width || height != prev_height)
207 m_cached_display.Create(width, height);
208 m_bounds_dirty = true;
211 // View bounds have not changed; blit the FrameBuffer as it is
212 if (!m_bounds_dirty && m_lazy_rendering)
214 m_cached_display.UnBind();
215 m_cached_display.Blit();
219 m_cached_display.Bind(); //NOTE: This is redundant; Clear already calls Bind
220 m_cached_display.Clear();
222 #ifndef QUADTREE_DISABLED
223 // I'm going to write this out in comments, so hopefully then I'll understand it. :/
225 // This code looks at the current bounds and tries to work out how they need to change
226 // to keep the view looking at the correct quadtree node.
228 // The idea is that the width/height of the view bounds are always 0.5<=wh<=1.0. We then always
229 // try to keep the bottom-right corner of the node on-screen, changing nodes to suit. Why bottom-right,
230 // you may ask. It's an excellent question, with a dubious, hand-wavey answer: because we're manipulating
231 // the bounds, it was easier to do it that way. (The top-left corner of the bounds are within the main
233 if (m_bounds_dirty || !m_lazy_rendering)
235 // If we're too far zoomed out, become the parent of the current node.
236 if ( m_bounds.w > 1.0 || m_bounds.h > 1.0)
238 // If a parent node exists, we'll become it.
239 //TODO: Generate a new parent node if none exists, and work out when to change child_type
240 // away from QTC_UNKNOWN
241 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].parent != QUADTREE_EMPTY)
243 m_bounds = TransformFromQuadChild(m_bounds, m_document.GetQuadTree().nodes[m_current_quadtree_node].child_type);
244 m_current_quadtree_node = m_document.GetQuadTree().nodes[m_current_quadtree_node].parent;
248 // If we have a parent... (This prevents some crashes, but should disappear.)
249 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].parent != QUADTREE_EMPTY)
251 // If the current node is off the left-hand side of the screen...
252 while (m_bounds.x > 1)
254 //... the current node becomes the node to its right.
255 m_bounds = Rect(m_bounds.x - 1, m_bounds.y, m_bounds.w, m_bounds.h);
256 m_current_quadtree_node = m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, 1, 0, &m_document);
258 while (m_bounds.y > 1)
260 m_bounds = Rect(m_bounds.x, m_bounds.y - 1, m_bounds.w, m_bounds.h);
261 m_current_quadtree_node = m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, 0, 1, &m_document);
263 while (m_bounds.x < 0)
265 m_bounds = Rect(m_bounds.x + 1, m_bounds.y, m_bounds.w, m_bounds.h);
266 m_current_quadtree_node = m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, -1, 0, &m_document);
268 while (m_bounds.y < 0)
270 m_bounds = Rect(m_bounds.x, m_bounds.y + 1, m_bounds.w, m_bounds.h);
271 m_current_quadtree_node = m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, 0, -1, &m_document);
275 // Recurse into a node if we are completely within it. (If we're okay with having an invalid frame or two, we can remove this.)
276 if (ContainedInQuadChild(m_bounds, QTC_TOP_LEFT))
278 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].top_left == QUADTREE_EMPTY)
280 // We want to reparent into a child node, but none exist. Get the document to create one.
281 m_document.GenQuadChild(m_current_quadtree_node, QTC_TOP_LEFT);
282 m_render_dirty = true;
284 m_bounds = TransformToQuadChild(m_bounds, QTC_TOP_LEFT);
285 m_current_quadtree_node = m_document.GetQuadTree().nodes[m_current_quadtree_node].top_left;
287 if (ContainedInQuadChild(m_bounds, QTC_TOP_RIGHT))
289 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].top_right == QUADTREE_EMPTY)
291 // We want to reparent into a child node, but none exist. Get the document to create one.
292 m_document.GenQuadChild(m_current_quadtree_node, QTC_TOP_RIGHT);
293 m_render_dirty = true;
295 m_bounds = TransformToQuadChild(m_bounds, QTC_TOP_RIGHT);
296 m_current_quadtree_node = m_document.GetQuadTree().nodes[m_current_quadtree_node].top_right;
298 if (ContainedInQuadChild(m_bounds, QTC_BOTTOM_LEFT))
300 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].bottom_left == QUADTREE_EMPTY)
302 // We want to reparent into a child node, but none exist. Get the document to create one.
303 m_document.GenQuadChild(m_current_quadtree_node, QTC_BOTTOM_LEFT);
304 m_render_dirty = true;
306 m_bounds = TransformToQuadChild(m_bounds, QTC_BOTTOM_LEFT);
307 m_current_quadtree_node = m_document.GetQuadTree().nodes[m_current_quadtree_node].bottom_left;
309 if (ContainedInQuadChild(m_bounds, QTC_BOTTOM_RIGHT))
311 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].bottom_right == QUADTREE_EMPTY)
313 // We want to reparent into a child node, but none exist. Get the document to create one.
314 m_document.GenQuadChild(m_current_quadtree_node, QTC_BOTTOM_RIGHT);
315 m_render_dirty = true;
317 m_bounds = TransformToQuadChild(m_bounds, QTC_BOTTOM_RIGHT);
318 m_current_quadtree_node = m_document.GetQuadTree().nodes[m_current_quadtree_node].bottom_right;
321 // Otherwise, we'll arbitrarily select the bottom-right.
322 // TODO: Perhaps select based on greatest area?
323 if (m_bounds.w < 0.5 || m_bounds.h < 0.5)
325 if (m_document.GetQuadTree().nodes[m_current_quadtree_node].bottom_right == QUADTREE_EMPTY)
327 // We want to reparent into a child node, but none exist. Get the document to create one.
328 m_document.GenQuadChild(m_current_quadtree_node, QTC_BOTTOM_RIGHT);
329 m_render_dirty = true;
331 m_bounds = TransformToQuadChild(m_bounds, QTC_BOTTOM_RIGHT);
332 m_current_quadtree_node = m_document.GetQuadTree().nodes[m_current_quadtree_node].bottom_right;
336 m_screen.DebugFontPrintF("Current View QuadTree");
337 QuadTreeIndex overlay = m_current_quadtree_node;
338 while (overlay != -1)
340 m_screen.DebugFontPrintF(" Node: %d (objs: %d -> %d)", overlay, m_document.GetQuadTree().nodes[overlay].object_begin,
341 m_document.GetQuadTree().nodes[overlay].object_end);
342 overlay = m_document.GetQuadTree().nodes[overlay].next_overlay;
344 m_screen.DebugFontPrintF("\n");
345 m_screen.DebugFontPrintF("Left: %d, Right: %d, Up: %d, Down: %d\n",
346 m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, -1, 0, 0),
347 m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, 1, 0, 0),
348 m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, 0, -1, 0),
349 m_document.GetQuadTree().GetNeighbour(m_current_quadtree_node, 0, 1, 0));
352 Rect view_top_bounds = m_bounds;
353 QuadTreeIndex tmp = m_current_quadtree_node;
356 view_top_bounds = TransformFromQuadChild(view_top_bounds, m_document.GetQuadTree().nodes[tmp].child_type);
357 tmp = m_document.GetQuadTree().nodes[tmp].parent;
359 m_screen.DebugFontPrintF("Equivalent View Bounds: %s\n", view_top_bounds.Str().c_str());
362 if (!m_use_gpu_rendering)
364 // Dynamically resize CPU rendering target pixels if needed
365 if (m_cpu_rendering_pixels == NULL || width*height > prev_width*prev_height)
367 delete [] m_cpu_rendering_pixels;
368 m_cpu_rendering_pixels = new uint8_t[width*height*4];
369 if (m_cpu_rendering_pixels == NULL)
370 Fatal("Could not allocate %d*%d*4 = %d bytes for cpu rendered pixels", width, height, width*height*4);
372 // Clear CPU rendering pixels
373 for (int i = 0; i < width*height*4; ++i)
374 m_cpu_rendering_pixels[i] = 255;
376 #ifdef QUADTREE_DISABLED
377 RenderRange(width, height, 0, m_document.ObjectCount());
379 RenderQuadtreeNode(width, height, m_current_quadtree_node, m_quadtree_max_depth);
381 if (!m_use_gpu_rendering)
383 m_screen.RenderPixels(0,0,width, height, m_cpu_rendering_pixels); //TODO: Make this work :(
384 // Debug for great victory (do something similar for GPU and compare?)
385 //ObjectRenderer::SaveBMP({m_cpu_rendering_pixels, width, height}, "cpu_rendering_last_frame.bmp");
387 m_cached_display.UnBind(); // resets render target to the screen
388 m_cached_display.Blit(); // blit FrameBuffer to screen
389 m_buffer_dirty = false;
392 #ifndef CONTROLPANEL_DISABLED
393 // The powers that be suggest that this may be causing of the segfaults.
394 //ControlPanel::Update();
395 #endif //CONTROLPANEL_DISABLED
396 //Debug("Completed Render");
400 #ifndef QUADTREE_DISABLED
401 void View::RenderQuadtreeNode(int width, int height, QuadTreeIndex node, int remaining_depth)
403 Rect old_bounds = m_bounds;
404 if (node == QUADTREE_EMPTY) return;
405 if (!remaining_depth) return;
406 //Debug("Rendering QT node %d, (objs: %d -- %d)\n", node, m_document.GetQuadTree().nodes[node].object_begin, m_document.GetQuadTree().nodes[node].object_end);
407 m_bounds_dirty = true;
408 m_render_dirty = m_buffer_dirty = true;
409 QuadTreeIndex overlay = node;
412 RenderRange(width, height, m_document.GetQuadTree().nodes[overlay].object_begin, m_document.GetQuadTree().nodes[overlay].object_end);
413 overlay = m_document.GetQuadTree().nodes[overlay].next_overlay;
416 if (m_bounds.Intersects(Rect(1,1,1,1)))
418 m_bounds = Rect(m_bounds.x - 1, m_bounds.y - 1, m_bounds.w, m_bounds.h);
419 m_bounds_dirty = true;
420 RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 1, 1, &m_document), remaining_depth - 1);
422 m_bounds = old_bounds;
423 if (m_bounds.Intersects(Rect(1,0,1,1)))
425 m_bounds = Rect(m_bounds.x - 1, m_bounds.y, m_bounds.w, m_bounds.h);
426 m_bounds_dirty = true;
427 RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 1, 0, &m_document), remaining_depth - 1);
429 m_bounds = old_bounds;
430 if (m_bounds.Intersects(Rect(0,1,1,1)))
432 m_bounds = Rect(m_bounds.x, m_bounds.y - 1, m_bounds.w, m_bounds.h);
433 m_bounds_dirty = true;
434 RenderQuadtreeNode(width, height, m_document.GetQuadTree().GetNeighbour(node, 0, 1, &m_document), remaining_depth - 1);
436 m_bounds = old_bounds;
437 m_bounds_dirty = true;
440 m_bounds = TransformToQuadChild(old_bounds, QTC_TOP_LEFT);
441 m_bounds_dirty = true;
442 RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].top_left, remaining_depth-1);
443 m_bounds = TransformToQuadChild(old_bounds, QTC_TOP_RIGHT);
444 m_bounds_dirty = true;
445 RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].top_right, remaining_depth-1);
446 m_bounds = TransformToQuadChild(old_bounds, QTC_BOTTOM_LEFT);
447 m_bounds_dirty = true;
448 RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].bottom_left, remaining_depth-1);
449 m_bounds = TransformToQuadChild(old_bounds, QTC_BOTTOM_RIGHT);
450 m_bounds_dirty = true;
451 RenderQuadtreeNode(width, height, m_document.GetQuadTree().nodes[node].bottom_right, remaining_depth-1);
452 m_bounds = old_bounds;
453 m_bounds_dirty = true;
458 void View::RenderRange(int width, int height, unsigned first_obj, unsigned last_obj)
460 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 43, -1, "View::RenderRange()");
461 if (m_render_dirty) // document has changed
464 if (m_buffer_dirty || m_bounds_dirty || !m_lazy_rendering) // object bounds have changed
466 if (m_use_gpu_rendering)
467 UpdateObjBoundsVBO(first_obj, last_obj);
470 if (m_use_gpu_transform)
472 #ifdef TRANSFORM_OBJECTS_NOT_VIEW
473 //Debug("Transform objects, not view");
474 GLfloat glbounds[] = {0.0f, 0.0f, 1.0f, 1.0f,
475 0.0f, 0.0f, float(width), float(height)};
477 GLfloat glbounds[] = {static_cast<GLfloat>(Float(m_bounds.x)), static_cast<GLfloat>(Float(m_bounds.y)), static_cast<GLfloat>(Float(m_bounds.w)), static_cast<GLfloat>(Float(m_bounds.h)),
478 0.0, 0.0, static_cast<GLfloat>(width), static_cast<GLfloat>(height)};
480 m_bounds_ubo.Upload(sizeof(float)*8, glbounds);
484 GLfloat glbounds[] = {0.0f, 0.0f, 1.0f, 1.0f,
485 0.0f, 0.0f, float(width), float(height)};
486 m_bounds_ubo.Upload(sizeof(float)*8, glbounds);
488 m_bounds_dirty = false;
492 if (m_use_gpu_rendering)
494 if (m_colour.a < 1.0f)
497 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
499 m_objbounds_vbo.Bind();
501 glEnableVertexAttribArray(0);
502 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
504 for (unsigned i = 0; i < m_object_renderers.size(); ++i)
506 m_object_renderers[i]->RenderUsingGPU(first_obj, last_obj);
509 glDisableVertexAttribArray(0);
510 if (m_colour.a < 1.0f)
515 else // Rasterise on CPU then blit texture to GPU
518 for (unsigned i = 0; i < m_object_renderers.size(); ++i)
520 m_object_renderers[i]->RenderUsingCPU(m_document.m_objects, *this, {m_cpu_rendering_pixels, width, height}, first_obj, last_obj);
526 void View::UpdateObjBoundsVBO(unsigned first_obj, unsigned last_obj)
528 if (m_query_gpu_bounds_on_next_frame != NULL)
530 fprintf(m_query_gpu_bounds_on_next_frame,"# View: %s\t%s\t%s\t%s\n", Str(m_bounds.x).c_str(), Str(m_bounds.y).c_str(), Str(m_bounds.w).c_str(), Str(m_bounds.h).c_str());
533 //m_objbounds_vbo.Invalidate();
534 m_objbounds_vbo.SetType(GraphicsBuffer::BufferTypeVertex);
535 m_objbounds_vbo.SetName("Object Bounds VBO");
537 #ifndef TRANSFORM_OBJECTS_NOT_VIEW
538 if (m_use_gpu_transform)
540 m_objbounds_vbo.SetUsage(GraphicsBuffer::BufferUsageStaticDraw);
543 #endif //TRANSFORM_OBJECTS_NOT_VIEW
545 m_objbounds_vbo.SetUsage(GraphicsBuffer::BufferUsageDynamicCopy);
547 m_objbounds_vbo.Resize(m_document.ObjectCount()*sizeof(GPUObjBounds));
549 BufferBuilder<GPUObjBounds> obj_bounds_builder(m_objbounds_vbo.MapRange(first_obj*sizeof(GPUObjBounds), (last_obj-first_obj)*sizeof(GPUObjBounds), false, true, true), m_objbounds_vbo.GetSize());
551 #ifndef TRANSFORM_BEZIERS_TO_PATH
552 for (unsigned id = first_obj; id < last_obj; ++id)
555 if (m_use_gpu_transform)
557 obj_bounds = m_document.m_objects.bounds[id];
561 obj_bounds = TransformToViewCoords(m_document.m_objects.bounds[id]);
563 GPUObjBounds gpu_bounds = {
566 Float(obj_bounds.x + obj_bounds.w),
567 Float(obj_bounds.y + obj_bounds.h)
570 if (m_query_gpu_bounds_on_next_frame != NULL)
572 fprintf(m_query_gpu_bounds_on_next_frame,"%d\t%f\t%f\t%f\t%f\n", id, Float(obj_bounds.x), Float(obj_bounds.y), Float(obj_bounds.w), Float(obj_bounds.h));
575 obj_bounds_builder.Add(gpu_bounds);
578 for (unsigned i = 0; i < m_document.m_objects.paths.size(); ++i)
580 Path & path = m_document.m_objects.paths[i];
581 Rect & pbounds = path.GetBounds(m_document.m_objects); // Not very efficient...
582 //TODO: Add clipping here
583 //if (!pbounds.Intersects(Rect(0,0,1,1)) || pbounds.w < Real(1)/Real(800))
586 for (unsigned id = path.m_start; id <= path.m_end; ++id)
588 if (id < first_obj || id >= last_obj)
591 Rect obj_bounds = m_document.m_objects.bounds[id];
592 obj_bounds.x *= pbounds.w;
593 obj_bounds.x += pbounds.x;
594 obj_bounds.y *= pbounds.h;
595 obj_bounds.y += pbounds.y;
596 obj_bounds.w *= pbounds.w;
597 obj_bounds.h *= pbounds.h;
599 if (!m_use_gpu_transform)
600 obj_bounds = TransformToViewCoords(obj_bounds);
601 GPUObjBounds gpu_bounds = {
602 ClampFloat(obj_bounds.x),
603 ClampFloat(obj_bounds.y),
604 ClampFloat(obj_bounds.x + obj_bounds.w),
605 ClampFloat(obj_bounds.y + obj_bounds.h)
607 obj_bounds_builder.Add(gpu_bounds);
608 //Debug("Path %d %s -> %s via %s", id, m_document.m_objects.bounds[id].Str().c_str(), obj_bounds.Str().c_str(), pbounds.Str().c_str());
610 if (m_query_gpu_bounds_on_next_frame != NULL)
612 fprintf(m_query_gpu_bounds_on_next_frame,"%d\t%f\t%f\t%f\t%f\n", id, ClampFloat(obj_bounds.x), ClampFloat(obj_bounds.y), ClampFloat(obj_bounds.w), ClampFloat(obj_bounds.h));
615 GPUObjBounds p_gpu_bounds = {
616 ClampFloat(pbounds.x),
617 ClampFloat(pbounds.y),
618 ClampFloat(pbounds.x + pbounds.w),
619 ClampFloat(pbounds.y + pbounds.h)
621 obj_bounds_builder.Add(p_gpu_bounds);
624 if (m_query_gpu_bounds_on_next_frame != NULL)
626 if (m_query_gpu_bounds_on_next_frame != stdout && m_query_gpu_bounds_on_next_frame != stderr)
627 fclose(m_query_gpu_bounds_on_next_frame);
628 m_query_gpu_bounds_on_next_frame = NULL;
630 m_objbounds_vbo.UnMap();
633 * Prepare the document for rendering
634 * Will be called on View::Render if m_render_dirty is set
635 * (Called at least once, on the first Render)
637 void View::PrepareRender()
639 Debug("Recreate buffers with %u objects", m_document.ObjectCount());
640 // Prepare bounds vbo
641 if (UsingGPURendering())
643 m_bounds_ubo.Invalidate();
644 m_bounds_ubo.SetType(GraphicsBuffer::BufferTypeUniform);
645 m_bounds_ubo.SetUsage(GraphicsBuffer::BufferUsageStreamDraw);
646 m_bounds_ubo.SetName("m_bounds_ubo: Screen bounds.");
649 // Instead of having each ObjectRenderer go through the whole document
650 // we initialise them, go through the document once adding to the appropriate Renderers
651 // and then finalise them
652 // This will totally be efficient if we have like, a lot of distinct ObjectTypes. Which could totally happen. You never know.
654 // Prepare the buffers
655 for (unsigned i = 0; i < m_object_renderers.size(); ++i)
657 m_object_renderers[i]->PrepareBuffers(m_document.ObjectCount());
660 // Add objects from Document to buffers
661 for (unsigned id = 0; id < m_document.ObjectCount(); ++id)
663 ObjectType type = m_document.m_objects.types[id];
664 m_object_renderers.at(type)->AddObjectToBuffers(id); // Use at() in case the document is corrupt TODO: Better error handling?
665 // (Also, Wow I just actually used std::vector::at())
666 // (Also, I just managed to make it throw an exception because I'm a moron)
667 //Debug("Object of type %d", type);
671 // Finish the buffers
672 for (unsigned i = 0; i < m_object_renderers.size(); ++i)
674 m_object_renderers[i]->FinaliseBuffers();
676 if (UsingGPURendering())
678 dynamic_cast<BezierRenderer*>(m_object_renderers[BEZIER])->PrepareBezierGPUBuffer(m_document.m_objects);
680 m_render_dirty = false;
683 void View::SaveCPUBMP(const char * filename)
685 bool prev = UsingGPURendering();
686 SetGPURendering(false);
688 ObjectRenderer::SaveBMP({m_cpu_rendering_pixels, 800, 600}, filename);
689 SetGPURendering(prev);
692 void View::SaveGPUBMP(const char * filename)
694 bool prev = UsingGPURendering();
695 SetGPURendering(true);
697 m_screen.ScreenShot(filename);
698 SetGPURendering(prev);
701 void View::QueryGPUBounds(const char * filename, const char * mode)
703 m_query_gpu_bounds_on_next_frame = fopen(filename, mode);
704 Debug("File: %s", filename);
705 if (m_query_gpu_bounds_on_next_frame == NULL)
706 Error("Couldn't open file \"%s\" : %s", filename, strerror(errno));