1 /* This file is part of the Pangolin Project.
2  * http://github.com/stevenlovegrove/Pangolin
3  *
4  * Copyright (c) 2011 Steven Lovegrove
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include <pangolin/display/display.h>
29 #include <pangolin/display/display_internal.h>
30 #include <pangolin/display/opengl_render_state.h>
31 #include <pangolin/display/view.h>
32 #include <pangolin/display/viewport.h>
33 #include <pangolin/gl/gl.h>
34 #include <pangolin/platform.h>
35 
36 #include <stdexcept>
37 
38 namespace pangolin
39 {
40 
41 // Pointer to context defined in display.cpp
42 extern __thread PangolinGl* context;
43 
44 const int panal_v_margin = 6;
45 
AttachAbs(int low,int high,Attach a)46 int AttachAbs( int low, int high, Attach a)
47 {
48     if( a.unit == Pixel ) return low + (int)a.p;
49     if( a.unit == ReversePixel ) return high - (int)a.p;
50     return (int)(low + a.p * (high - low));
51 }
52 
AspectAreaWithinTarget(double target,double test)53 double AspectAreaWithinTarget(double target, double test)
54 {
55     if( test < target )
56         return test / target;
57     else
58         return target / test;
59 }
60 
SaveViewFromFbo(std::string prefix,View & view,float scale)61 void SaveViewFromFbo(std::string prefix, View& view, float scale)
62 {
63     PANGOLIN_UNUSED(prefix);
64 
65 #ifndef HAVE_GLES
66     const Viewport orig = view.v;
67     view.v.l = 0;
68     view.v.b = 0;
69     view.v.w = (int)(view.v.w * scale);
70     view.v.h = (int)(view.v.h * scale);
71 
72     const int w = view.v.w;
73     const int h = view.v.h;
74 
75     float origLineWidth;
76     glGetFloatv(GL_LINE_WIDTH, &origLineWidth);
77     glLineWidth(origLineWidth * scale);
78 
79     float origPointSize;
80     glGetFloatv(GL_POINT_SIZE, &origPointSize);
81     glPointSize(origPointSize * scale);
82 
83     // Create FBO
84     GlTexture color(w,h);
85     GlRenderBuffer depth(w,h);
86     GlFramebuffer fbo(color, depth);
87 
88     // Render into FBO
89     fbo.Bind();
90     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
91     view.Render();
92     glFlush();
93 
94 #ifdef HAVE_PNG
95     const PixelFormat fmt = PixelFormatFromString("RGBA32");
96     TypedImage buffer(w, h, fmt );
97     glReadBuffer(GL_BACK);
98     glPixelStorei(GL_PACK_ALIGNMENT, 1); // TODO: Avoid this?
99     glReadPixels(0,0,w,h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.ptr );
100     SaveImage(buffer, fmt, prefix + ".png", false);
101 #endif // HAVE_PNG
102 
103     // unbind FBO
104     fbo.Unbind();
105 
106     // restore viewport / line width
107     view.v = orig;
108     glLineWidth(origLineWidth);
109     glPointSize(origPointSize);
110 #endif // HAVE_GLES
111 }
112 
Resize(const Viewport & p)113 void View::Resize(const Viewport& p)
114 {
115     // Compute Bounds based on specification
116     v.l = AttachAbs(p.l,p.r(),left);
117     v.b = AttachAbs(p.b,p.t(),bottom);
118     int r = AttachAbs(p.l,p.r(),right);
119     int t = AttachAbs(p.b,p.t(),top);
120 
121     // Make sure left and right, top and bottom are correct order
122     if( t < v.b ) std::swap(t,v.b);
123     if( r < v.l ) std::swap(r,v.l);
124 
125     v.w = r - v.l;
126     v.h = t - v.b;
127 
128     vp = v;
129 
130     // Adjust based on aspect requirements
131     if( aspect != 0 )
132     {
133         const float current_aspect = (float)v.w / (float)v.h;
134         if( aspect > 0 )
135         {
136             // Fit to space
137             if( current_aspect < aspect )
138             {
139                 //Adjust height
140                 const int nh = (int)(v.w / aspect);
141                 v.b += vlock == LockBottom ? 0 : (vlock == LockCenter ? (v.h-nh)/2 : (v.h-nh) );
142                 v.h = nh;
143             }else if( current_aspect > aspect )
144             {
145                 //Adjust width
146                 const int nw = (int)(v.h * aspect);
147                 v.l += hlock == LockLeft? 0 : (hlock == LockCenter ? (v.w-nw)/2 : (v.w-nw) );
148                 v.w = nw;
149             }
150         }else{
151             // Overfit
152             double true_aspect = -aspect;
153             if( current_aspect < true_aspect )
154             {
155                 //Adjust width
156                 const int nw = (int)(v.h * true_aspect);
157                 v.l += hlock == LockLeft? 0 : (hlock == LockCenter ? (v.w-nw)/2 : (v.w-nw) );
158                 v.w = nw;
159             }else if( current_aspect > true_aspect )
160             {
161                 //Adjust height
162                 const int nh = (int)(v.w / true_aspect);
163                 v.b += vlock == LockBottom ? 0 : (vlock == LockCenter ? (v.h-nh)/2 : (v.h-nh) );
164                 v.h = nh;
165             }
166         }
167     }
168 
169     ResizeChildren();
170 }
171 
zcompare(const View * lhs,const View * rhs)172 inline int zcompare(const View* lhs, const View* rhs)
173 {
174     return lhs->zorder < rhs->zorder;
175 }
176 
ResizeChildren()177 void View::ResizeChildren()
178 {
179     if( layout == LayoutOverlay )
180     {
181         // Sort children into z-order
182         std::sort(views.begin(), views.end(), zcompare);
183 
184         for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv ) {
185             (*iv)->Resize(v);
186         }
187     }else if( layout == LayoutVertical )
188     {
189         // Allocate space incrementally
190         Viewport space = v.Inset(panal_v_margin);
191         int num_children = 0;
192         for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv )
193         {
194             if (!(*iv)->show) continue;
195             num_children++;
196             if(scroll_offset >= num_children ) {
197                 (*iv)->scroll_show = false;
198             }else{
199                 (*iv)->scroll_show = true;
200                 (*iv)->Resize(space);
201                 space.h = (*iv)->v.b - panal_v_margin - space.b;
202             }
203         }
204     }else if(layout == LayoutHorizontal )
205     {
206         // Allocate space incrementally
207         const int margin = 8;
208         Viewport space = v.Inset(margin);
209         for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv )
210         {
211             (*iv)->Resize(space);
212             space.w = (*iv)->v.l + margin + space.l;
213         }
214     }else if(layout == LayoutEqualVertical )
215     {
216         // Allocate vertical space equally
217         const size_t visiblechildren = NumVisibleChildren();
218         const float height = (float)v.h / (float)visiblechildren;
219 
220         for( size_t i=0; i < visiblechildren; ++i) {
221             Viewport space(v.l, (GLint)(v.b+(visiblechildren-1-i)*height), v.w, (GLint)(height) );
222             VisibleChild(i).Resize(space);
223         }
224     }else if(layout == LayoutEqualHorizontal )
225     {
226         // Allocate vertical space equally
227         const size_t visiblechildren = NumVisibleChildren();
228         const float width = (float)v.w / (float)visiblechildren;
229 
230         for( size_t i=0; i < visiblechildren; ++i) {
231             Viewport space( (GLint)(v.l+i*width), v.b, (GLint)width, v.h);
232             VisibleChild(i).Resize(space);
233         }
234     }else if(layout == LayoutEqual )
235     {
236         const size_t visiblechildren = NumVisibleChildren();
237         // TODO: Make this neater, and make fewer assumptions!
238         if( visiblechildren > 0 )
239         {
240             // This containers aspect
241             const double this_a = std::fabs(v.aspect());
242 
243             // Use first child with fixed aspect for all children
244             double child_a = std::fabs(VisibleChild(0).aspect);
245             for(size_t i=1; (child_a==0) && i < visiblechildren; ++i ) {
246                 child_a = std::fabs(VisibleChild(i).aspect);
247             }
248 
249             if(child_a == 0) {
250                 child_a = 1;
251             }
252 
253             double a = visiblechildren*child_a;
254             double area = AspectAreaWithinTarget(this_a, a);
255 
256             size_t cols = visiblechildren-1;
257             for(; cols > 0; --cols)
258             {
259                 const size_t rows = visiblechildren / cols + (visiblechildren % cols == 0 ? 0 : 1);
260                 const double na = cols * child_a / rows;
261                 const double new_area = visiblechildren*AspectAreaWithinTarget(this_a,na)/(rows*cols);
262                 if( new_area <= area )
263                     break;
264                 area = new_area;
265                 a = na;
266             }
267 
268             cols++;
269             const size_t rows = visiblechildren / cols + (visiblechildren % cols == 0 ? 0 : 1);
270             size_t cw, ch;
271             if( a > this_a )
272             {
273                 cw = v.w / cols;
274                 ch = (int)(cw / child_a); //v.h / rows;
275             }else{
276                 ch = v.h / rows;
277                 cw = (int)(ch * child_a);
278             }
279 
280             for(size_t i=0; i< visiblechildren; ++i )
281             {
282                 size_t c = i % cols;
283                 size_t r = i / cols;
284                 Viewport space( GLint(v.l + c*cw), GLint(v.t() - (r+1)*ch), GLint(cw), GLint(ch) );
285                 VisibleChild(i).Resize(space);
286             }
287         }
288     }
289 
290 }
291 
Render()292 void View::Render()
293 {
294     if(extern_draw_function && show && scroll_show) {
295         extern_draw_function(*this);
296     }
297     RenderChildren();
298 }
299 
RenderChildren()300 void View::RenderChildren()
301 {
302     for(std::vector<View*>::iterator iv = views.begin(); iv != views.end(); ++iv )
303     {
304         if((*iv)->show && (*iv)->scroll_show) (*iv)->Render();
305     }
306 }
307 
Activate() const308 void View::Activate() const
309 {
310     v.Activate();
311 }
312 
ActivateAndScissor() const313 void View::ActivateAndScissor() const
314 {
315     vp.Scissor();
316     v.Activate();
317 }
318 
ActivateScissorAndClear() const319 void View::ActivateScissorAndClear() const
320 {
321     vp.Scissor();
322     v.Activate();
323     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
324 }
325 
Activate(const OpenGlRenderState & state) const326 void View::Activate(const OpenGlRenderState& state ) const
327 {
328     v.Activate();
329     state.Apply();
330 }
331 
ActivateAndScissor(const OpenGlRenderState & state) const332 void View::ActivateAndScissor(const OpenGlRenderState& state) const
333 {
334     vp.Scissor();
335     v.Activate();
336     state.Apply();
337 }
338 
ActivateScissorAndClear(const OpenGlRenderState & state) const339 void View::ActivateScissorAndClear(const OpenGlRenderState& state ) const
340 {
341     vp.Scissor();
342     v.Activate();
343     state.Apply();
344     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
345 }
346 
ActivatePixelOrthographic() const347 void View::ActivatePixelOrthographic() const
348 {
349     v.ActivatePixelOrthographic();
350 }
351 
ActivateIdentity() const352 void View::ActivateIdentity() const
353 {
354     v.ActivateIdentity();
355 }
356 
GetClosestDepth(int x,int y,int radius) const357 GLfloat View::GetClosestDepth(int x, int y, int radius) const
358 {
359     // TODO: Get to work on android
360 
361 #ifdef _MSVC_
362     // MSVC Requires fixed sized arrays on stack
363     radius = 5;
364     const int zl = (5*2+1);
365 #else
366     const int zl = (radius*2+1);
367 #endif
368 
369     const int zsize = zl*zl;
370     GLfloat zs[zsize];
371 
372 #ifndef HAVE_GLES
373     glReadBuffer(GL_FRONT);
374     glReadPixels(x-radius,y-radius,zl,zl,GL_DEPTH_COMPONENT,GL_FLOAT,zs);
375 #else
376     std::fill(zs,zs+zsize, 0.8);
377 #endif
378 
379     const GLfloat mindepth = *(std::min_element(zs,zs+zsize));
380     return mindepth;
381 }
382 
GetObjectCoordinates(const OpenGlRenderState & cam_state,double winx,double winy,double winzdepth,GLdouble & x,GLdouble & y,GLdouble & z) const383 void View::GetObjectCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const
384 {
385     const GLint viewport[4] = {v.l,v.b,v.w,v.h};
386     const OpenGlMatrix proj = cam_state.GetProjectionMatrix();
387     const OpenGlMatrix mv = cam_state.GetModelViewMatrix();
388     glUnProject(winx, winy, winzdepth, mv.m, proj.m, viewport, &x, &y, &z);
389 }
390 
GetCamCoordinates(const OpenGlRenderState & cam_state,double winx,double winy,double winzdepth,GLdouble & x,GLdouble & y,GLdouble & z) const391 void View::GetCamCoordinates(const OpenGlRenderState& cam_state, double winx, double winy, double winzdepth, GLdouble& x, GLdouble& y, GLdouble& z) const
392 {
393     v.GetCamCoordinates(cam_state, winx, winy, winzdepth, x, y, z);
394 }
395 
SetFocus()396 View& View::SetFocus()
397 {
398     context->activeDisplay = this;
399     return *this;
400 }
401 
HasFocus() const402 bool View::HasFocus() const
403 {
404     return context->activeDisplay == this;
405 }
406 
SetBounds(Attach bottom,Attach top,Attach left,Attach right)407 View& View::SetBounds(Attach bottom, Attach top, Attach left, Attach right)
408 {
409     this->left = left;
410     this->top = top;
411     this->right = right;
412     this->bottom = bottom;
413     context->base.ResizeChildren();
414     return *this;
415 }
416 
SetBounds(Attach bottom,Attach top,Attach left,Attach right,bool keep_aspect)417 View& View::SetBounds(Attach bottom, Attach top,  Attach left, Attach right, bool keep_aspect)
418 {
419     aspect = keep_aspect ? v.aspect() : 0;
420     SetBounds(top,bottom,left,right);
421     return *this;
422 }
423 
SetBounds(Attach bottom,Attach top,Attach left,Attach right,double aspect)424 View& View::SetBounds(Attach bottom, Attach top,  Attach left, Attach right, double aspect)
425 {
426     this->aspect = aspect;
427     SetBounds(top,bottom,left,right);
428     return *this;
429 }
430 
SetAspect(double aspect)431 View& View::SetAspect(double aspect)
432 {
433     this->aspect = aspect;
434     context->base.ResizeChildren();
435     return *this;
436 }
437 
SetLock(Lock horizontal,Lock vertical)438 View& View::SetLock(Lock horizontal, Lock vertical )
439 {
440     vlock = vertical;
441     hlock = horizontal;
442     return *this;
443 }
444 
SetLayout(Layout l)445 View& View::SetLayout(Layout l)
446 {
447     layout = l;
448     return *this;
449 }
450 
451 
AddDisplay(View & child)452 View& View::AddDisplay(View& child)
453 {
454     // detach child from any other view, and add to this
455     std::vector<View*>::iterator f = std::find(
456                 context->base.views.begin(), context->base.views.end(), &child
457                 );
458 
459     if( f != context->base.views.end() )
460         context->base.views.erase(f);
461 
462     views.push_back(&child);
463     context->base.ResizeChildren();
464     return *this;
465 }
466 
Show(bool show)467 View& View::Show(bool show)
468 {
469     this->show = show;
470     context->base.ResizeChildren();
471     return *this;
472 }
473 
ToggleShow()474 void View::ToggleShow()
475 {
476     Show(!show);
477 }
478 
IsShown() const479 bool View::IsShown() const
480 {
481     return show;
482 }
483 
GetBounds() const484 Viewport View::GetBounds() const
485 {
486     return Viewport( std::max(v.l, vp.l), std::max(v.b, vp.b), std::min(v.w, vp.w), std::min(v.h, vp.h) );
487 }
488 
SaveOnRender(const std::string & filename_prefix)489 void View::SaveOnRender(const std::string& filename_prefix)
490 {
491     const Viewport tosave = this->v.Intersect(this->vp);
492     context->screen_capture.push(std::pair<std::string,Viewport>(filename_prefix,tosave ) );
493 }
494 
RecordOnRender(const std::string & record_uri)495 void View::RecordOnRender(const std::string& record_uri)
496 {
497     PANGOLIN_UNUSED(record_uri);
498 
499 #ifdef BUILD_PANGOLIN_VIDEO
500     if(!context->recorder.IsOpen()) {
501         Viewport area = GetBounds();
502         context->record_view = this;
503         try{
504             context->recorder.Open(record_uri);
505             std::vector<StreamInfo> streams;
506             const PixelFormat fmt = PixelFormatFromString("RGB24");
507             streams.push_back( StreamInfo(fmt, area.w, area.h, area.w * fmt.bpp / 8) );
508             context->recorder.SetStreams(streams);
509         }catch(const std::exception& e) {
510             pango_print_error("Unable to open VideoRecorder:\n\t%s\n", e.what());
511         }
512     }else{
513         context->recorder.Close();
514     }
515 #else
516     std::cerr << "Error: Video Support hasn't been built into this library." << std::endl;
517 #endif // BUILD_PANGOLIN_VIDEO
518 }
519 
SaveRenderNow(const std::string & filename_prefix,float scale)520 void View::SaveRenderNow(const std::string& filename_prefix, float scale)
521 {
522     SaveViewFromFbo(filename_prefix, *this, scale);
523 }
524 
operator [](size_t i)525 View& View::operator[](size_t i)
526 {
527     return *views[i];
528 }
529 
NumChildren() const530 size_t View::NumChildren() const
531 {
532     return views.size();
533 }
534 
NumVisibleChildren() const535 size_t View::NumVisibleChildren() const
536 {
537     int numvis = 0;
538     for(std::vector<View*>::const_iterator i=views.begin(); i!=views.end(); ++i)
539     {
540         if((*i)->show) {
541             numvis++;
542         }
543     }
544     return numvis;
545 }
546 
VisibleChild(size_t i)547 View& View::VisibleChild(size_t i)
548 {
549     size_t numvis = 0;
550     for(size_t v=0; v < views.size(); ++v ) {
551         if(views[v]->show) {
552             if( i == numvis ) {
553                 return *views[v];
554             }
555             numvis++;
556         }
557     }
558     // Shouldn't get here
559     throw std::out_of_range("No such child.");
560 }
561 
FindChild(int x,int y)562 View* View::FindChild(int x, int y)
563 {
564     // Find in reverse order to mirror draw order
565     for( std::vector<View*>::const_reverse_iterator i = views.rbegin(); i != views.rend(); ++i )
566         if( (*i)->show && (*i)->GetBounds().Contains(x,y) )
567             return (*i);
568     return 0;
569 }
570 
SetHandler(Handler * h)571 View& View::SetHandler(Handler* h)
572 {
573     handler = h;
574     return *this;
575 }
576 
SetDrawFunction(const std::function<void (View &)> & drawFunc)577 View& View::SetDrawFunction(const std::function<void(View&)>& drawFunc)
578 {
579     extern_draw_function = drawFunc;
580     return *this;
581 }
582 
583 }
584