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