1 #include <pangolin/handler/handler_image.h>
2 #include <pangolin/gl/glfont.h>
3 
4 namespace pangolin
5 {
6 
ImageViewHandler()7 ImageViewHandler::ImageViewHandler()
8     : linked_view_handler(0),
9       use_nn(false), flipTextureX(false), flipTextureY(false)
10 {
11     SetDimensions(1, 1);
12 }
13 
ImageViewHandler(size_t w,size_t h)14 ImageViewHandler::ImageViewHandler(size_t w, size_t h)
15     : linked_view_handler(0),
16       use_nn(false), flipTextureX(false), flipTextureY(false)
17 {
18     SetDimensions(w,h);
19 }
20 
SetDimensions(size_t w,size_t h)21 void ImageViewHandler::SetDimensions(size_t w, size_t h)
22 {
23     rview_default = pangolin::XYRangef(-0.5f, w-0.5f, -0.5f, h-0.5f),
24     rview_max = pangolin::XYRangef(-0.5f, w-0.5f, -0.5f, h-0.5f),
25     rview = rview_default;
26     target = rview;
27 }
28 
UpdateView()29 void ImageViewHandler::UpdateView()
30 {
31     // TODO: Base this on current framerate.
32     const float animate_factor = 1.0f / 5.0f;
33 
34     if( linked_view_handler ) {
35         // Synchronise rview and target with linked plotter
36         rview = linked_view_handler->rview;
37         target = linked_view_handler->target;
38         selection = linked_view_handler->selection;
39     }else{
40         // Clamp target to image dimensions.
41         AdjustScale();
42         AdjustTranslation();
43 
44         // Animate view window toward target
45         pangolin::XYRangef d = target - rview;
46         rview += d * animate_factor;
47     }
48 }
49 
glSetViewOrtho()50 void ImageViewHandler::glSetViewOrtho()
51 {
52     const pangolin::XYRangef& xy = GetViewToRender();
53 
54     glMatrixMode(GL_PROJECTION);
55     glLoadIdentity();
56     glOrtho(xy.x.min, xy.x.max, xy.y.max, xy.y.min, -1, 1);
57     glMatrixMode(GL_MODELVIEW);
58     glLoadIdentity();
59 }
60 
glRenderTexture(pangolin::GlTexture & tex)61 void ImageViewHandler::glRenderTexture(pangolin::GlTexture& tex)
62 {
63     glRenderTexture(tex.tid, tex.width, tex.height);
64 }
65 
glRenderTexture(GLuint tex,GLint width,GLint height)66 void ImageViewHandler::glRenderTexture(GLuint tex, GLint width, GLint height)
67 {
68     if(tex != 0) {
69         const pangolin::XYRangef& xy = GetViewToRender();
70         const float w = (float)width;
71         const float h = (float)height;
72 
73         // discrete coords, (-0.5, -0.5) - (w-0.5, h-0.5)
74         const GLfloat l = xy.x.min;
75         const GLfloat r = xy.x.max;
76         const GLfloat b = xy.y.max;
77         const GLfloat t = xy.y.min;
78 
79         // continuous coords, (0,0) - (1,1)
80         GLfloat ln = (l + 0.5f) / w;
81         GLfloat rn = (r + 0.5f) / w;
82         GLfloat bn = (b + 0.5f) / h;
83         GLfloat tn = (t + 0.5f) / h;
84 
85         if(flipTextureX) {
86             ln = 1-ln;
87             rn = 1-rn;
88         }
89 
90         if(flipTextureY) {
91             bn = 1-bn;
92             tn = 1-tn;
93         }
94 
95         const GLfloat sq_vert[]  = { l,t,  r,t,  r,b,  l,b };
96         const GLfloat sq_tex[]  = { ln,tn,  rn,tn,  rn,bn,  ln,bn };
97 
98         glBindTexture(GL_TEXTURE_2D, tex);
99         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, UseNN() ? GL_NEAREST : GL_LINEAR);
100         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, UseNN() ? GL_NEAREST : GL_LINEAR);
101 
102         glEnable(GL_TEXTURE_2D);
103         glEnableClientState(GL_VERTEX_ARRAY);
104         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
105         glTexCoordPointer(2, GL_FLOAT, 0, sq_tex);
106         glVertexPointer(2, GL_FLOAT, 0, sq_vert);
107         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
108         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
109         glDisableClientState(GL_VERTEX_ARRAY);
110         glDisable(GL_TEXTURE_2D);
111         glBindTexture(GL_TEXTURE_2D, 0);
112     }
113 }
114 
glRenderOverlay()115 void ImageViewHandler::glRenderOverlay()
116 {
117     const pangolin::XYRangef& selxy = GetSelection();
118     const GLfloat sq_select[] = {
119         selxy.x.min, selxy.y.min,
120         selxy.x.max, selxy.y.min,
121         selxy.x.max, selxy.y.max,
122         selxy.x.min, selxy.y.max
123     };
124     glColor4f(1.0,0.0,0.0,1.0);
125     glEnableClientState(GL_VERTEX_ARRAY);
126     glVertexPointer(2, GL_FLOAT, 0, sq_select);
127     glDrawArrays(GL_LINE_LOOP, 0, 4);
128     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
129     glColor4f(1.0,1.0,1.0,1.0);
130 
131     if( std::abs(selxy.Area()) > 0) {
132         // Render text
133         pangolin::Viewport v;
134         glGetIntegerv( GL_VIEWPORT, &v.l );
135         float xpix, ypix;
136         ImageToScreen(v, selxy.x.max, selxy.y.max, xpix, ypix);
137 
138         // Save previous value
139         GLboolean gl_blend_enabled;
140         glGetBooleanv(GL_BLEND, &gl_blend_enabled);
141 
142         // Ensure that blending is enabled for rendering text.
143         glEnable(GL_BLEND);
144         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
145 
146         pangolin::GlFont::I().Text(
147             "%.2f x %.2f",
148             selxy.x.Size(), selxy.y.Size()
149         ).DrawWindow(xpix,ypix);
150 
151         pangolin::GlFont::I().Text(
152             "(%.1f,%.1f)->(%.1f,%.1f)",
153             selxy.x.min, selxy.y.min,
154             selxy.x.max, selxy.y.max
155         ).DrawWindow(xpix, ypix - 1.0f * pangolin::GlFont::I().Height());
156 
157         // Restore previous value
158         if(!gl_blend_enabled) glDisable(GL_BLEND);
159     }
160 }
161 
ScreenToImage(Viewport & v,float xpix,float ypix,float & ximg,float & yimg)162 void ImageViewHandler::ScreenToImage(Viewport& v, float xpix, float ypix, float& ximg, float& yimg)
163 {
164     ximg = rview.x.min + rview.x.Size() * (xpix - v.l) / (float)v.w;
165     yimg = rview.y.min + rview.y.Size() * ( 1.0f - (ypix - v.b) / (float)v.h);
166 }
167 
ImageToScreen(Viewport & v,float ximg,float yimg,float & xpix,float & ypix)168 void ImageViewHandler::ImageToScreen(Viewport& v, float ximg, float yimg, float& xpix, float& ypix)
169 {
170     xpix = (ximg -rview.x.min) * (float)v.w / rview.x.Size() + v.l;
171     ypix = v.b - (float)v.h * ((yimg - rview.y.min) / rview.y.Size() - 1.0f);
172 }
173 
UseNN() const174 bool ImageViewHandler::UseNN() const
175 {
176     return use_nn;
177 }
178 
UseNN()179 bool& ImageViewHandler::UseNN()
180 {
181     return use_nn;
182 }
183 
FlipTextureX()184 bool& ImageViewHandler::FlipTextureX()
185 {
186     return flipTextureX;
187 }
188 
FlipTextureY()189 bool& ImageViewHandler::FlipTextureY()
190 {
191     return flipTextureY;
192 }
193 
GetViewToRender()194 pangolin::XYRangef& ImageViewHandler::GetViewToRender()
195 {
196     return rview;
197 }
198 
GetViewScale()199 float ImageViewHandler::GetViewScale()
200 {
201     return rview_max.x.Size() / rview.x.Size();
202 }
203 
GetView()204 pangolin::XYRangef& ImageViewHandler::GetView()
205 {
206     return target;
207 }
208 
GetDefaultView()209 pangolin::XYRangef& ImageViewHandler::GetDefaultView()
210 {
211     return rview_default;
212 }
213 
GetSelection()214 pangolin::XYRangef& ImageViewHandler::GetSelection()
215 {
216     return selection;
217 }
218 
GetHover(float & x,float & y)219 void ImageViewHandler::GetHover(float& x, float& y)
220 {
221     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
222     x = tv.hover_img[0];
223     y = tv.hover_img[1];
224 }
225 
SetView(const pangolin::XYRangef & range)226 void ImageViewHandler::SetView(const pangolin::XYRangef& range)
227 {
228     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
229     tv.rview = range;
230     tv.target = range;
231 }
232 
SetViewSmooth(const pangolin::XYRangef & range)233 void ImageViewHandler::SetViewSmooth(const pangolin::XYRangef& range)
234 {
235     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
236     tv.target = range;
237 }
238 
ScrollView(float x,float y)239 void ImageViewHandler::ScrollView(float x, float y)
240 {
241     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
242     ScrollViewSmooth(x,y);
243     tv.rview.x += x;
244     tv.rview.y += y;
245 }
246 
ScrollViewSmooth(float x,float y)247 void ImageViewHandler::ScrollViewSmooth(float x, float y)
248 {
249     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
250     tv.target.x += x;
251     tv.target.y += y;
252 }
253 
ScaleView(float x,float y,float cx,float cy)254 void ImageViewHandler::ScaleView(float x, float y, float cx, float cy)
255 {
256     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
257     ScaleViewSmooth(x,y,cx,cy);
258     tv.rview.x.Scale(x,cx);
259     tv.rview.y.Scale(y,cy);
260 }
261 
ScaleViewSmooth(float x,float y,float cx,float cy)262 void ImageViewHandler::ScaleViewSmooth(float x, float y, float cx, float cy)
263 {
264     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
265     tv.target.x.Scale(x,cx);
266     tv.target.y.Scale(y,cy);
267 }
268 
ResetView()269 void ImageViewHandler::ResetView()
270 {
271     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
272     tv.target = tv.rview_default;
273 }
274 
275 ///////////////////////////////////////////////////////
276 /// pangolin::Handler
277 ///////////////////////////////////////////////////////
278 
Keyboard(View &,unsigned char key,int,int,bool pressed)279 void ImageViewHandler::Keyboard(View&, unsigned char key, int /*x*/, int /*y*/, bool pressed)
280 {
281     XYRangef& sel = linked_view_handler ? linked_view_handler->selection : selection;
282     const float mvfactor = 1.0f / 10.0f;
283     const float c[2] = { rview.x.Mid(), rview.y.Mid() };
284 
285 
286     if(pressed) {
287         if(key == '\r') {
288             if( sel.Area() != 0.0f && std::isfinite(sel.Area()) ) {
289                 // Set view to equal selection
290                 SetViewSmooth(sel);
291 
292                 // Reset selection
293                 sel.x.max = sel.x.min;
294                 sel.y.max = sel.y.min;
295             }
296         }else if(key == 'n') {
297             use_nn = !use_nn;
298         }else if(key == 'l') {
299             if(to_link) {
300                 linked_view_handler = to_link;
301                 to_link = 0;
302             }else{
303                 to_link = this;
304             }
305         }else if(key == PANGO_SPECIAL + PANGO_KEY_LEFT) {
306             const float w = target.x.Size();
307             const float dx = mvfactor*w;
308             ScrollViewSmooth(-dx, 0);
309         }else if(key == PANGO_SPECIAL + PANGO_KEY_RIGHT) {
310             const float w = target.x.Size();
311             const float dx = mvfactor*w;
312             ScrollViewSmooth(+dx, 0);
313         }else if(key == PANGO_SPECIAL + PANGO_KEY_DOWN) {
314             const float h = target.y.Size();
315             const float dy = mvfactor*h;
316             ScrollViewSmooth(0, -dy);
317         }else if(key == PANGO_SPECIAL + PANGO_KEY_UP) {
318             const float h = target.y.Size();
319             const float dy = mvfactor*h;
320             ScrollViewSmooth(0, +dy);
321         }else if(key == '=') {
322             ScaleViewSmooth(0.5, 0.5, c[0], c[1]);
323         }else if(key == '-') {
324             ScaleViewSmooth(2.0, 2.0, c[0], c[1]);
325         }else if(key == '#') {
326             ResetView();
327         }else if(key == 1) {
328             // ctrl-a: select all.
329             sel = rview;
330         }else{
331             pango_print_debug("Unhandled ImageViewHandler::Keyboard. Key: %u\n", (unsigned int)key);
332         }
333     }
334 }
335 
Mouse(View & view,pangolin::MouseButton button,int x,int y,bool pressed,int button_state)336 void ImageViewHandler::Mouse(View& view, pangolin::MouseButton button, int x, int y, bool pressed, int button_state)
337 {
338     XYRangef& sel = linked_view_handler ? linked_view_handler->selection : selection;
339     ScreenToImage(view.v, (float)x, (float)y, hover_img[0], hover_img[1]);
340 
341     const float scinc = 1.05f;
342     const float scdec = 1.0f/scinc;
343 
344     if(button_state & KeyModifierCtrl) {
345         const float mvfactor = 1.0f/20.0f;
346 
347         if(button == MouseWheelUp) {
348             ScrollViewSmooth(0.0f, +mvfactor*rview.y.Size() );
349         }else if(button == MouseWheelDown) {
350             ScrollViewSmooth(0.0f, -mvfactor*rview.y.Size() );
351         }else if(button == MouseWheelLeft) {
352             ScrollViewSmooth(+mvfactor*rview.x.Size(), 0.0f );
353         }else if(button == MouseWheelRight) {
354             ScrollViewSmooth(-mvfactor*rview.x.Size(), 0.0f );
355         }
356     }else{
357         if(button == MouseButtonLeft) {
358             // Update selected range
359             if(pressed) {
360                 sel.x.min = hover_img[0];
361                 sel.y.min = hover_img[1];
362             }
363             sel.x.max = hover_img[0];
364             sel.y.max = hover_img[1];
365         }else if(button == MouseWheelUp) {
366             ScaleViewSmooth(scdec, scdec, hover_img[0], hover_img[1]);
367         }else if(button == MouseWheelDown) {
368             ScaleViewSmooth(scinc, scinc, hover_img[0], hover_img[1]);
369         }
370     }
371 
372     FixSelection(sel);
373     last_mouse_pos[0] = x;
374     last_mouse_pos[1] = y;
375 
376     if(OnSelectionCallback) {
377         OnSelectionCallback( OnSelectionEventData(view,*this,pressed) );
378     }
379 }
380 
MouseMotion(View & view,int x,int y,int button_state)381 void ImageViewHandler::MouseMotion(View& view, int x, int y, int button_state)
382 {
383     XYRangef& sel = linked_view_handler ? linked_view_handler->selection : selection;
384     const int d[2] = {x-last_mouse_pos[0], y-last_mouse_pos[1]};
385 
386     // Update hover status (after potential resizing)
387     ScreenToImage(view.v, (float)x, (float)y, hover_img[0], hover_img[1]);
388 
389     if( button_state == MouseButtonLeft )
390     {
391         // Update selected range
392         sel.x.max = hover_img[0];
393         sel.y.max = hover_img[1];
394     }else if(button_state == MouseButtonRight )
395     {
396         Special(view, InputSpecialScroll, (float)x, (float)y, (float)d[0], (float)d[1], 0.0f, 0.0f, button_state);
397     }
398 
399     last_mouse_pos[0] = x;
400     last_mouse_pos[1] = y;
401 
402     if( button_state == MouseButtonLeft && OnSelectionCallback) {
403         OnSelectionCallback( OnSelectionEventData(view,*this,true) );
404     }
405 }
406 
PassiveMouseMotion(View &,int,int,int)407 void ImageViewHandler::PassiveMouseMotion(View&, int /*x*/, int /*y*/, int /*button_state*/)
408 {
409 }
410 
Special(View & view,pangolin::InputSpecial inType,float x,float y,float p1,float p2,float,float,int)411 void ImageViewHandler::Special(View& view, pangolin::InputSpecial inType, float x, float y, float p1, float p2, float /*p3*/, float /*p4*/, int /*button_state*/)
412 {
413     ScreenToImage(view.v, x, y, hover_img[0], hover_img[1]);
414 
415     if(inType == InputSpecialScroll) {
416         const float d[2] = {p1,p2};
417         const float is[2] = {rview.x.Size(),rview.y.Size() };
418         const float df[2] = {is[0]*d[0]/(float)view.v.w, is[1]*d[1]/(float)view.v.h};
419         ScrollView(-df[0], df[1]);
420     } else if(inType == InputSpecialZoom) {
421         float scale = 1.0f - p1;
422         ScaleView(scale, scale, hover_img[0], hover_img[1]);
423     }
424 
425     // Update hover status (after potential resizing)
426     ScreenToImage( view.v, x, y, hover_img[0], hover_img[1]);
427 }
428 
FixSelection(XYRangef & sel)429 void ImageViewHandler::FixSelection(XYRangef& sel)
430 {
431     // Make sure selection matches sign of current viewport
432     if( (sel.x.min<sel.x.max) != (rview.x.min<rview.x.max) ) {
433         std::swap(sel.x.min, sel.x.max);
434     }
435     if( (sel.y.min<sel.y.max) != (rview.y.min<rview.y.max) ) {
436         std::swap(sel.y.min, sel.y.max);
437     }
438 }
439 
AdjustScale()440 void ImageViewHandler::AdjustScale()
441 {
442     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
443     if(tv.target.x.AbsSize() > tv.rview_max.x.AbsSize())
444         tv.target.x.Scale(tv.rview_max.x.AbsSize() / tv.target.x.AbsSize(), tv.target.x.Mid());
445     if(tv.target.y.AbsSize() > tv.rview_max.y.AbsSize())
446         tv.target.y.Scale(tv.rview_max.y.AbsSize() / tv.target.y.AbsSize(), tv.target.y.Mid());
447 }
448 
AdjustTranslation()449 void ImageViewHandler::AdjustTranslation()
450 {
451     ImageViewHandler& tv = linked_view_handler ? *linked_view_handler : *this;
452     if( tv.target.x.max > tv.rview_max.x.max) tv.target.x -= tv.target.x.max - tv.rview_max.x.max;
453     if( tv.target.x.min < tv.rview_max.x.min) tv.target.x -= tv.target.x.min - tv.rview_max.x.min;
454 
455     if( tv.target.y.max > tv.rview_max.y.max) tv.target.y -= tv.target.y.max - tv.rview_max.y.max;
456     if( tv.target.y.min < tv.rview_max.y.min) tv.target.y -= tv.target.y.min - tv.rview_max.y.min;
457 }
458 
459 float ImageViewHandler::animate_factor = 1.0f / 2.0f;
460 ImageViewHandler* ImageViewHandler::to_link = 0;
461 
462 }
463