1 | /*M/////////////////////////////////////////////////////////////////////////////////////// |
---|---|
2 | // |
3 | // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
4 | // |
5 | // By downloading, copying, installing or using the software you agree to this license. |
6 | // If you do not agree to this license, do not download, install, |
7 | // copy or use the software. |
8 | // |
9 | // |
10 | // Intel License Agreement |
11 | // For Open Source Computer Vision Library |
12 | // |
13 | // Copyright (C) 2000, Intel Corporation, all rights reserved. |
14 | // Third party copyrights are property of their respective owners. |
15 | // |
16 | // Redistribution and use in source and binary forms, with or without modification, |
17 | // are permitted provided that the following conditions are met: |
18 | // |
19 | // * Redistribution's of source code must retain the above copyright notice, |
20 | // this list of conditions and the following disclaimer. |
21 | // |
22 | // * Redistribution's in binary form must reproduce the above copyright notice, |
23 | // this list of conditions and the following disclaimer in the documentation |
24 | // and/or other materials provided with the distribution. |
25 | // |
26 | // * The name of Intel Corporation may not be used to endorse or promote products |
27 | // derived from this software without specific prior written permission. |
28 | // |
29 | // This software is provided by the copyright holders and contributors "as is" and |
30 | // any express or implied warranties, including, but not limited to, the implied |
31 | // warranties of merchantability and fitness for a particular purpose are disclaimed. |
32 | // In no event shall the Intel Corporation or contributors be liable for any direct, |
33 | // indirect, incidental, special, exemplary, or consequential damages |
34 | // (including, but not limited to, procurement of substitute goods or services; |
35 | // loss of use, data, or profits; or business interruption) however caused |
36 | // and on any theory of liability, whether in contract, strict liability, |
37 | // or tort (including negligence or otherwise) arising in any way out of |
38 | // the use of this software, even if advised of the possibility of such damage. |
39 | // |
40 | //M*/ |
41 | |
42 | #include "precomp.hpp" |
43 | #include "backend.hpp" |
44 | |
45 | #if defined (HAVE_GTK) |
46 | |
47 | #include <gtk/gtk.h> |
48 | |
49 | #if (GTK_MAJOR_VERSION == 2) && defined(HAVE_OPENGL) && !defined(HAVE_GTKGLEXT) |
50 | #undef HAVE_OPENGL // gtkglext is required |
51 | #endif |
52 | |
53 | #include <gdk/gdkkeysyms.h> |
54 | #include <gdk-pixbuf/gdk-pixbuf.h> |
55 | #include <stdio.h> |
56 | |
57 | #if (GTK_MAJOR_VERSION == 2) |
58 | #define GTK_VERSION2 1 |
59 | #endif //GTK_MAJOR_VERSION >= 2.0 |
60 | #if (GTK_MAJOR_VERSION == 3) |
61 | #define GTK_VERSION3 1 |
62 | #endif //GTK_MAJOR_VERSION >= 3 |
63 | #if (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4) |
64 | #define GTK_VERSION3_4 1 |
65 | #endif |
66 | |
67 | #ifdef HAVE_OPENGL |
68 | #ifdef GTK_VERSION3 |
69 | #include <gtk/gtkglarea.h> |
70 | #else |
71 | #include <gtk/gtkgl.h> |
72 | #include <GL/glu.h> |
73 | #endif |
74 | #include <GL/gl.h> |
75 | #endif |
76 | |
77 | #include <opencv2/core/utils/logger.hpp> |
78 | #include "opencv2/imgproc.hpp" |
79 | |
80 | using namespace cv; |
81 | |
82 | #ifndef BIT_ALLIN |
83 | #define BIT_ALLIN(x,y) ( ((x)&(y)) == (y) ) |
84 | #endif |
85 | #ifndef BIT_MAP |
86 | #define BIT_MAP(x,y,z) ( ((x)&(y)) ? (z) : 0 ) |
87 | #endif |
88 | |
89 | // TODO Fix the initial window size when flags=0. Right now the initial window is by default |
90 | // 320x240 size. A better default would be actual size of the image. Problem |
91 | // is determining desired window size with trackbars while still allowing resizing. |
92 | // |
93 | // Gnome Totem source may be of use here, see bacon_video_widget_set_scale_ratio |
94 | // in totem/src/backend/bacon-video-widget-xine.c |
95 | |
96 | //////////////////////////////////////////////////////////// |
97 | // CvImageWidget GTK Widget Public API |
98 | //////////////////////////////////////////////////////////// |
99 | typedef struct _CvImageWidget CvImageWidget; |
100 | typedef struct _CvImageWidgetClass CvImageWidgetClass; |
101 | |
102 | struct _CvImageWidget { |
103 | GtkWidget widget; |
104 | CvMat * original_image; |
105 | CvMat * scaled_image; |
106 | int flags; |
107 | }; |
108 | |
109 | struct _CvImageWidgetClass |
110 | { |
111 | GtkWidgetClass parent_class; |
112 | }; |
113 | |
114 | |
115 | /** Allocate new image viewer widget */ |
116 | GtkWidget* cvImageWidgetNew (int flags); |
117 | |
118 | // standard GTK object macros |
119 | #define CV_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, cvImageWidget_get_type (), CvImageWidget) |
120 | #define CV_IMAGE_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass) |
121 | #define CV_IS_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, cvImageWidget_get_type ()) |
122 | |
123 | ///////////////////////////////////////////////////////////////////////////// |
124 | // Private API //////////////////////////////////////////////////////// |
125 | ///////////////////////////////////////////////////////////////////////////// |
126 | GType cvImageWidget_get_type (void); |
127 | |
128 | static GtkWidgetClass * parent_class = NULL; |
129 | |
130 | // flag to help size initial window |
131 | #define CV_WINDOW_NO_IMAGE 2 |
132 | |
133 | /** Set the image to display in the widget */ |
134 | static |
135 | void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr) |
136 | { |
137 | CvMat * mat, stub; |
138 | int origin=0; |
139 | |
140 | //printf("cvImageWidgetSetImage\n"); |
141 | |
142 | if( CV_IS_IMAGE_HDR( arr )) |
143 | origin = ((IplImage*)arr)->origin; |
144 | |
145 | mat = cvGetMat(arr, header: &stub); |
146 | |
147 | if(widget->original_image && !CV_ARE_SIZES_EQ(mat, widget->original_image)){ |
148 | cvReleaseMat( mat: &widget->original_image ); |
149 | } |
150 | if(!widget->original_image){ |
151 | widget->original_image = cvCreateMat( rows: mat->rows, cols: mat->cols, CV_8UC3 ); |
152 | gtk_widget_queue_resize( GTK_WIDGET( widget ) ); |
153 | } |
154 | CV_Assert(origin == 0); |
155 | convertToShow(src: cv::cvarrToMat(arr), arr: widget->original_image); |
156 | if(widget->scaled_image){ |
157 | cvResize( src: widget->original_image, dst: widget->scaled_image, interpolation: CV_INTER_AREA ); |
158 | } |
159 | |
160 | // window does not refresh without this |
161 | gtk_widget_queue_draw( GTK_WIDGET(widget) ); |
162 | } |
163 | |
164 | GtkWidget* |
165 | cvImageWidgetNew (int flags) |
166 | { |
167 | CvImageWidget *image_widget; |
168 | |
169 | image_widget = CV_IMAGE_WIDGET( gtk_widget_new (cvImageWidget_get_type (), NULL) ); |
170 | CV_Assert(image_widget && "GTK widget creation is failed. Ensure that there is no GTK2/GTK3 libraries conflict"); |
171 | image_widget->original_image = 0; |
172 | image_widget->scaled_image = 0; |
173 | image_widget->flags = flags | CV_WINDOW_NO_IMAGE; |
174 | |
175 | return GTK_WIDGET (image_widget); |
176 | } |
177 | |
178 | static void |
179 | cvImageWidget_realize (GtkWidget *widget) |
180 | { |
181 | GdkWindowAttr attributes; |
182 | gint attributes_mask; |
183 | |
184 | #if defined(GTK_VERSION3) |
185 | GtkAllocation allocation; |
186 | gtk_widget_get_allocation(widget, allocation: &allocation); |
187 | #endif //GTK_VERSION3 |
188 | |
189 | //printf("cvImageWidget_realize\n"); |
190 | g_return_if_fail (widget != NULL); |
191 | g_return_if_fail (CV_IS_IMAGE_WIDGET (widget)); |
192 | |
193 | gtk_widget_set_realized(widget, TRUE); |
194 | |
195 | #if defined(GTK_VERSION3) |
196 | attributes.x = allocation.x; |
197 | attributes.y = allocation.y; |
198 | attributes.width = allocation.width; |
199 | attributes.height = allocation.height; |
200 | #elif defined(GTK_VERSION2) |
201 | attributes.x = widget->allocation.x; |
202 | attributes.y = widget->allocation.y; |
203 | attributes.width = widget->allocation.width; |
204 | attributes.height = widget->allocation.height; |
205 | #endif //GTK_VERSION3 |
206 | |
207 | attributes.wclass = GDK_INPUT_OUTPUT; |
208 | attributes.window_type = GDK_WINDOW_CHILD; |
209 | attributes.event_mask = gtk_widget_get_events (widget) | |
210 | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | |
211 | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK; |
212 | attributes.visual = gtk_widget_get_visual (widget); |
213 | |
214 | #if defined(GTK_VERSION3) |
215 | attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; |
216 | gtk_widget_set_window( |
217 | widget, |
218 | window: gdk_window_new( |
219 | parent: gtk_widget_get_parent_window(widget), |
220 | attributes: &attributes, |
221 | attributes_mask |
222 | ) |
223 | ); |
224 | |
225 | gtk_widget_set_style( |
226 | widget, |
227 | style: gtk_style_attach( |
228 | style: gtk_widget_get_style(widget), |
229 | window: gtk_widget_get_window(widget) |
230 | ) |
231 | ); |
232 | |
233 | gdk_window_set_user_data ( |
234 | window: gtk_widget_get_window(widget), |
235 | user_data: widget |
236 | ); |
237 | |
238 | gtk_style_set_background ( |
239 | style: gtk_widget_get_style(widget), |
240 | window: gtk_widget_get_window(widget), |
241 | state_type: GTK_STATE_ACTIVE |
242 | ); |
243 | #elif defined(GTK_VERSION2) |
244 | // The following lines are included to prevent breaking |
245 | // compatibility with older Gtk2 (<gtk+-2.18) libraries. |
246 | attributes.colormap = gtk_widget_get_colormap (widget); |
247 | attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; |
248 | widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask); |
249 | |
250 | widget->style = gtk_style_attach (widget->style, widget->window); |
251 | gdk_window_set_user_data (widget->window, widget); |
252 | |
253 | gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); |
254 | #endif // GTK_VERSION3 |
255 | } |
256 | |
257 | static CvSize cvImageWidget_calc_size( int im_width, int im_height, int max_width, int max_height ){ |
258 | float aspect = (float)im_width/(float)im_height; |
259 | float max_aspect = (float)max_width/(float)max_height; |
260 | if(aspect > max_aspect){ |
261 | return cvSize( width: max_width, height: cvRound(value: max_width/aspect) ); |
262 | } |
263 | return cvSize( width: cvRound(value: max_height*aspect), height: max_height ); |
264 | } |
265 | |
266 | #if defined (GTK_VERSION3) |
267 | static void |
268 | cvImageWidget_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width) |
269 | { |
270 | g_return_if_fail (widget != NULL); |
271 | g_return_if_fail (CV_IS_IMAGE_WIDGET (widget)); |
272 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget ); |
273 | |
274 | if(image_widget->original_image != NULL) { |
275 | *minimal_width = (image_widget->flags & cv::WINDOW_AUTOSIZE) != cv::WINDOW_AUTOSIZE ? |
276 | gdk_window_get_width(window: gtk_widget_get_window(widget)) : image_widget->original_image->cols; |
277 | } |
278 | else { |
279 | *minimal_width = 320; |
280 | } |
281 | |
282 | if(image_widget->scaled_image != NULL) { |
283 | *natural_width = *minimal_width < image_widget->scaled_image->cols ? |
284 | image_widget->scaled_image->cols : *minimal_width; |
285 | } |
286 | else { |
287 | *natural_width = *minimal_width; |
288 | } |
289 | } |
290 | |
291 | static void |
292 | cvImageWidget_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height) |
293 | { |
294 | g_return_if_fail (widget != NULL); |
295 | g_return_if_fail (CV_IS_IMAGE_WIDGET (widget)); |
296 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget ); |
297 | |
298 | if(image_widget->original_image != NULL) { |
299 | *minimal_height = (image_widget->flags & cv::WINDOW_AUTOSIZE) != cv::WINDOW_AUTOSIZE ? |
300 | gdk_window_get_height(window: gtk_widget_get_window(widget)) : image_widget->original_image->rows; |
301 | } |
302 | else { |
303 | *minimal_height = 240; |
304 | } |
305 | |
306 | if(image_widget->scaled_image != NULL) { |
307 | *natural_height = *minimal_height < image_widget->scaled_image->rows ? |
308 | image_widget->scaled_image->rows : *minimal_height; |
309 | } |
310 | else { |
311 | *natural_height = *minimal_height; |
312 | } |
313 | } |
314 | #endif //GTK_VERSION3 |
315 | |
316 | #if defined (GTK_VERSION2) |
317 | static void |
318 | cvImageWidget_size_request (GtkWidget *widget, |
319 | GtkRequisition *requisition) |
320 | { |
321 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget ); |
322 | |
323 | //printf("cvImageWidget_size_request "); |
324 | // the case the first time cvShowImage called or when AUTOSIZE |
325 | if( image_widget->original_image && |
326 | ((image_widget->flags & cv::WINDOW_AUTOSIZE) || |
327 | (image_widget->flags & CV_WINDOW_NO_IMAGE))) |
328 | { |
329 | //printf("original "); |
330 | requisition->width = image_widget->original_image->cols; |
331 | requisition->height = image_widget->original_image->rows; |
332 | } |
333 | // default case |
334 | else if(image_widget->scaled_image){ |
335 | //printf("scaled "); |
336 | requisition->width = image_widget->scaled_image->cols; |
337 | requisition->height = image_widget->scaled_image->rows; |
338 | } |
339 | // the case before cvShowImage called |
340 | else{ |
341 | //printf("default "); |
342 | requisition->width = 320; |
343 | requisition->height = 240; |
344 | } |
345 | //printf("%d %d\n",requisition->width, requisition->height); |
346 | } |
347 | #endif //GTK_VERSION2 |
348 | |
349 | static void cvImageWidget_set_size(GtkWidget * widget, int max_width, int max_height){ |
350 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget ); |
351 | |
352 | //printf("cvImageWidget_set_size %d %d\n", max_width, max_height); |
353 | |
354 | // don't allow to set the size |
355 | if(image_widget->flags & cv::WINDOW_AUTOSIZE) return; |
356 | if(!image_widget->original_image) return; |
357 | |
358 | CvSize scaled_image_size = cvImageWidget_calc_size( im_width: image_widget->original_image->cols, |
359 | im_height: image_widget->original_image->rows, max_width, max_height ); |
360 | |
361 | if( image_widget->scaled_image && |
362 | ( image_widget->scaled_image->cols != scaled_image_size.width || |
363 | image_widget->scaled_image->rows != scaled_image_size.height )) |
364 | { |
365 | cvReleaseMat( mat: &image_widget->scaled_image ); |
366 | } |
367 | if( !image_widget->scaled_image ){ |
368 | image_widget->scaled_image = cvCreateMat( rows: scaled_image_size.height, cols: scaled_image_size.width, CV_8UC3 ); |
369 | |
370 | |
371 | } |
372 | CV_Assert(image_widget->scaled_image); |
373 | } |
374 | |
375 | static void |
376 | cvImageWidget_size_allocate (GtkWidget *widget, |
377 | GtkAllocation *allocation) |
378 | { |
379 | CvImageWidget *image_widget; |
380 | |
381 | //printf("cvImageWidget_size_allocate\n"); |
382 | g_return_if_fail (widget != NULL); |
383 | g_return_if_fail (CV_IS_IMAGE_WIDGET (widget)); |
384 | g_return_if_fail (allocation != NULL); |
385 | |
386 | #if defined (GTK_VERSION3) |
387 | gtk_widget_set_allocation(widget, allocation); |
388 | #elif defined (GTK_VERSION2) |
389 | widget->allocation = *allocation; |
390 | #endif //GTK_VERSION3 |
391 | image_widget = CV_IMAGE_WIDGET (widget); |
392 | |
393 | |
394 | if( (image_widget->flags & cv::WINDOW_AUTOSIZE)==0 && image_widget->original_image ){ |
395 | // (re) allocated scaled image |
396 | if( image_widget->flags & CV_WINDOW_NO_IMAGE ){ |
397 | cvImageWidget_set_size( widget, max_width: image_widget->original_image->cols, |
398 | max_height: image_widget->original_image->rows); |
399 | } |
400 | else{ |
401 | cvImageWidget_set_size( widget, max_width: allocation->width, max_height: allocation->height ); |
402 | } |
403 | cvResize( src: image_widget->original_image, dst: image_widget->scaled_image, interpolation: CV_INTER_AREA ); |
404 | } |
405 | |
406 | if (gtk_widget_get_realized (widget)) |
407 | { |
408 | image_widget = CV_IMAGE_WIDGET (widget); |
409 | |
410 | if( image_widget->original_image && |
411 | ((image_widget->flags & cv::WINDOW_AUTOSIZE) || |
412 | (image_widget->flags & CV_WINDOW_NO_IMAGE)) ) |
413 | { |
414 | #if defined (GTK_VERSION3) |
415 | allocation->width = image_widget->original_image->cols; |
416 | allocation->height = image_widget->original_image->rows; |
417 | gtk_widget_set_allocation(widget, allocation); |
418 | #elif defined (GTK_VERSION2) |
419 | widget->allocation.width = image_widget->original_image->cols; |
420 | widget->allocation.height = image_widget->original_image->rows; |
421 | #endif //GTK_VERSION3 |
422 | gdk_window_move_resize( window: gtk_widget_get_window(widget), |
423 | x: allocation->x, y: allocation->y, |
424 | width: image_widget->original_image->cols, height: image_widget->original_image->rows ); |
425 | if(image_widget->flags & CV_WINDOW_NO_IMAGE){ |
426 | image_widget->flags &= ~CV_WINDOW_NO_IMAGE; |
427 | gtk_widget_queue_resize( GTK_WIDGET(widget) ); |
428 | } |
429 | } |
430 | else{ |
431 | gdk_window_move_resize (window: gtk_widget_get_window(widget), |
432 | x: allocation->x, y: allocation->y, |
433 | width: allocation->width, height: allocation->height ); |
434 | } |
435 | } |
436 | } |
437 | |
438 | #if defined (GTK_VERSION3) |
439 | static void |
440 | cvImageWidget_destroy (GtkWidget *object) |
441 | #else |
442 | static void |
443 | cvImageWidget_destroy (GtkObject *object) |
444 | #endif //GTK_VERSION3 |
445 | { |
446 | CvImageWidget *image_widget; |
447 | |
448 | g_return_if_fail (object != NULL); |
449 | g_return_if_fail (CV_IS_IMAGE_WIDGET (object)); |
450 | |
451 | image_widget = CV_IMAGE_WIDGET (object); |
452 | |
453 | cvReleaseMat( mat: &image_widget->scaled_image ); |
454 | cvReleaseMat( mat: &image_widget->original_image ); |
455 | |
456 | #if defined (GTK_VERSION3) |
457 | if (GTK_WIDGET_CLASS (parent_class)->destroy) |
458 | (* GTK_WIDGET_CLASS (parent_class)->destroy) (object); |
459 | #else |
460 | if (GTK_OBJECT_CLASS (parent_class)->destroy) |
461 | (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); |
462 | #endif //GTK_VERSION3 |
463 | } |
464 | |
465 | static void cvImageWidget_class_init (gpointer g_class, gpointer /*class_data*/) |
466 | { |
467 | CvImageWidgetClass* klass = (CvImageWidgetClass*)g_class; |
468 | #if defined (GTK_VERSION3) |
469 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
470 | #endif //GTK_VERSION3 |
471 | #if defined (GTK_VERSION2) |
472 | GtkObjectClass *object_class = (GtkObjectClass*) klass; |
473 | GtkWidgetClass *widget_class = (GtkWidgetClass*) klass; |
474 | #endif //GTK_VERSION2 |
475 | |
476 | parent_class = GTK_WIDGET_CLASS( g_type_class_peek (gtk_widget_get_type ()) ); |
477 | |
478 | #if defined (GTK_VERSION3) |
479 | widget_class->destroy = cvImageWidget_destroy; |
480 | widget_class->get_preferred_width = cvImageWidget_get_preferred_width; |
481 | widget_class->get_preferred_height = cvImageWidget_get_preferred_height; |
482 | #endif //GTK_VERSION3 |
483 | #if defined (GTK_VERSION2) |
484 | object_class->destroy = cvImageWidget_destroy; |
485 | widget_class->size_request = cvImageWidget_size_request; |
486 | #endif //GTK_VERSION2 |
487 | |
488 | widget_class->realize = cvImageWidget_realize; |
489 | widget_class->size_allocate = cvImageWidget_size_allocate; |
490 | widget_class->button_press_event = NULL; |
491 | widget_class->button_release_event = NULL; |
492 | widget_class->motion_notify_event = NULL; |
493 | } |
494 | |
495 | static void |
496 | cvImageWidget_init(GTypeInstance* instance, gpointer /*g_class*/) |
497 | { |
498 | CvImageWidget* image_widget = (CvImageWidget*)instance; |
499 | image_widget->original_image=0; |
500 | image_widget->scaled_image=0; |
501 | image_widget->flags=0; |
502 | } |
503 | |
504 | GType cvImageWidget_get_type (void){ |
505 | static GType image_type = 0; |
506 | |
507 | if (!image_type) |
508 | { |
509 | image_type = g_type_register_static_simple( |
510 | GTK_TYPE_WIDGET, |
511 | type_name: (gchar*) "CvImageWidget", |
512 | class_size: sizeof(CvImageWidgetClass), |
513 | class_init: cvImageWidget_class_init, |
514 | instance_size: sizeof(CvImageWidget), |
515 | instance_init: cvImageWidget_init, |
516 | flags: (GTypeFlags)0 |
517 | ); |
518 | } |
519 | |
520 | return image_type; |
521 | } |
522 | ///////////////////////////////////////////////////////////////////////////// |
523 | // End CvImageWidget |
524 | ///////////////////////////////////////////////////////////////////////////// |
525 | |
526 | |
527 | struct CvWindow; |
528 | |
529 | struct CvUIBase { |
530 | CvUIBase(int signature_) : signature(signature_) { } |
531 | |
532 | int signature; |
533 | }; |
534 | |
535 | struct CvTrackbar : CvUIBase |
536 | { |
537 | CvTrackbar(const std::string& trackbar_name) : |
538 | CvUIBase(CV_TRACKBAR_MAGIC_VAL), |
539 | widget(NULL), name(trackbar_name), |
540 | parent(NULL), data(NULL), |
541 | pos(0), maxval(0), minval(0), |
542 | notify(NULL), notify2(NULL), // deprecated |
543 | onChangeCallback(NULL), userdata(NULL) |
544 | { |
545 | // nothing |
546 | } |
547 | ~CvTrackbar() |
548 | { |
549 | // destroyed by parent window |
550 | } |
551 | |
552 | GtkWidget* widget; |
553 | std::string name; |
554 | CvWindow* parent; // TODO weak_ptr |
555 | int* data; |
556 | int pos; |
557 | int maxval; |
558 | int minval; |
559 | CvTrackbarCallback notify; // deprecated |
560 | CvTrackbarCallback2 notify2; // deprecated |
561 | TrackbarCallback onChangeCallback; |
562 | void* userdata; |
563 | }; |
564 | |
565 | |
566 | struct CvWindow : CvUIBase |
567 | { |
568 | CvWindow(const std::string& window_name) : |
569 | CvUIBase(CV_WINDOW_MAGIC_VAL), |
570 | widget(NULL), frame(NULL), paned(NULL), name(window_name), |
571 | last_key(0), flags(0), status(0), |
572 | on_mouse(NULL), on_mouse_param(NULL) |
573 | #ifdef HAVE_OPENGL |
574 | ,useGl(false), glDrawCallback(NULL), glDrawData(NULL), glArea(NULL) |
575 | #endif |
576 | { |
577 | CV_LOG_INFO(NULL, "OpenCV/UI: creating GTK window: "<< window_name); |
578 | } |
579 | ~CvWindow(); |
580 | void destroy(); |
581 | |
582 | GtkWidget* widget; |
583 | GtkWidget* frame; |
584 | GtkWidget* paned; |
585 | std::string name; |
586 | |
587 | int last_key; |
588 | int flags; |
589 | int status;//0 normal, 1 fullscreen (YV) |
590 | |
591 | CvMouseCallback on_mouse; |
592 | void* on_mouse_param; |
593 | |
594 | std::vector< std::shared_ptr<CvTrackbar> > trackbars; |
595 | |
596 | #ifdef HAVE_OPENGL |
597 | bool useGl; |
598 | |
599 | CvOpenGlDrawCallback glDrawCallback; |
600 | void* glDrawData; |
601 | GtkWidget* glArea; |
602 | #endif |
603 | }; |
604 | |
605 | |
606 | static gboolean icvOnClose( GtkWidget* widget, GdkEvent* event, gpointer user_data ); |
607 | static gboolean icvOnKeyPress( GtkWidget* widget, GdkEventKey* event, gpointer user_data ); |
608 | static void icvOnTrackbar( GtkWidget* widget, gpointer user_data ); |
609 | static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data ); |
610 | |
611 | int thread_started=0; |
612 | static gpointer icvWindowThreadLoop(gpointer data); |
613 | GMutex* last_key_mutex = NULL; |
614 | GCond* cond_have_key = NULL; |
615 | GThread* window_thread = NULL; |
616 | |
617 | static int last_key = -1; |
618 | |
619 | static |
620 | std::vector< std::shared_ptr<CvWindow> >& getGTKWindows() |
621 | { |
622 | static std::vector< std::shared_ptr<CvWindow> > g_windows; |
623 | return g_windows; |
624 | } |
625 | |
626 | CV_IMPL int cvInitSystem( int argc, char** argv ) |
627 | { |
628 | static int wasInitialized = 0; |
629 | static bool hasError = false; |
630 | |
631 | // check initialization status |
632 | if( !wasInitialized ) |
633 | { |
634 | if (!gtk_init_check(argc: &argc, argv: &argv)) |
635 | { |
636 | hasError = true; |
637 | wasInitialized = true; |
638 | CV_Error(Error::StsError, "Can't initialize GTK backend"); |
639 | } |
640 | |
641 | setlocale(LC_NUMERIC,locale: "C"); |
642 | |
643 | #if defined(HAVE_OPENGL) && not defined(GTK_VERSION3) // GTK3+ uses GtkGLArea so no need to check for GtkGLExt |
644 | if (!gtk_gl_init_check(&argc, &argv)) |
645 | { |
646 | hasError = true; |
647 | wasInitialized = true; |
648 | CV_Error(Error::StsError, "Can't initialize GTK-OpenGL backend"); |
649 | } |
650 | #endif |
651 | |
652 | wasInitialized = 1; |
653 | } |
654 | if (hasError) |
655 | CV_Error(Error::StsError, "GTK backend is not available"); |
656 | |
657 | return 0; |
658 | } |
659 | |
660 | CV_IMPL int cvStartWindowThread(){ |
661 | cvInitSystem(argc: 0,NULL); |
662 | if (!thread_started) |
663 | { |
664 | (void)getWindowMutex(); // force mutex initialization |
665 | |
666 | // protects the 'last key pressed' variable |
667 | last_key_mutex = g_mutex_new(); |
668 | |
669 | // conditional that indicates a key has been pressed |
670 | cond_have_key = g_cond_new(); |
671 | |
672 | window_thread = g_thread_new(name: "OpenCV window update", func: icvWindowThreadLoop, NULL); |
673 | } |
674 | thread_started = window_thread!=NULL; |
675 | return thread_started; |
676 | } |
677 | |
678 | gpointer icvWindowThreadLoop(gpointer /*data*/) |
679 | { |
680 | while(1){ |
681 | { |
682 | cv::AutoLock lock(getWindowMutex()); |
683 | gtk_main_iteration_do(FALSE); |
684 | } |
685 | |
686 | // little sleep |
687 | g_usleep(microseconds: 500); |
688 | |
689 | g_thread_yield(); |
690 | } |
691 | return NULL; |
692 | } |
693 | |
694 | #define CV_LOCK_MUTEX() cv::AutoLock lock(getWindowMutex()) |
695 | |
696 | static |
697 | std::shared_ptr<CvWindow> icvFindWindowByName(const std::string& name) |
698 | { |
699 | auto& g_windows = getGTKWindows(); |
700 | for(size_t i = 0; i < g_windows.size(); ++i) |
701 | { |
702 | auto window = g_windows[i]; |
703 | if (!window) |
704 | continue; |
705 | if (window->name == name) |
706 | return window; |
707 | } |
708 | return std::shared_ptr<CvWindow>(); |
709 | } |
710 | |
711 | static inline |
712 | std::shared_ptr<CvWindow> icvFindWindowByName(const char* name) |
713 | { |
714 | CV_Assert(name); |
715 | return icvFindWindowByName(name: std::string(name)); |
716 | } |
717 | |
718 | |
719 | static CvWindow* icvWindowByWidget( GtkWidget* widget ) |
720 | { |
721 | auto& g_windows = getGTKWindows(); |
722 | for (size_t i = 0; i < g_windows.size(); ++i) |
723 | { |
724 | CvWindow* window = g_windows[i].get(); |
725 | if (window->widget == widget || window->frame == widget || window->paned == widget) |
726 | return window; |
727 | } |
728 | return NULL; |
729 | } |
730 | |
731 | static Rect getImageRect_(const std::shared_ptr<CvWindow>& window); |
732 | |
733 | CvRect cvGetWindowRect_GTK(const char* name) |
734 | { |
735 | CV_Assert(name && "NULL name string"); |
736 | |
737 | CV_LOCK_MUTEX(); |
738 | const auto window = icvFindWindowByName(name); |
739 | if (!window) |
740 | CV_Error( cv::Error::StsNullPtr, "NULL window"); |
741 | |
742 | return cvRect(rc: getImageRect_(window)); |
743 | } |
744 | |
745 | #if defined(GTK_VERSION2) |
746 | #define gtk_widget_get_allocated_width(widget) (widget->allocation.width) |
747 | #define gtk_widget_get_allocated_height(widget) (widget->allocation.height) |
748 | #endif |
749 | |
750 | static Rect getImageRect_(const std::shared_ptr<CvWindow>& window) |
751 | { |
752 | CV_Assert(window); |
753 | |
754 | gint wx, wy; |
755 | #ifdef HAVE_OPENGL |
756 | if (window->useGl) { |
757 | gtk_widget_translate_coordinates(window->widget, gtk_widget_get_toplevel(window->widget), 0, 0, &wx, &wy); |
758 | return Rect(wx, wy, gtk_widget_get_allocated_width(window->widget), gtk_widget_get_allocated_height(window->widget)); |
759 | } |
760 | #endif |
761 | |
762 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget ); |
763 | gtk_widget_translate_coordinates(src_widget: &image_widget->widget, dest_widget: gtk_widget_get_toplevel(widget: &image_widget->widget), src_x: 0, src_y: 0, dest_x: &wx, dest_y: &wy); |
764 | if (image_widget->scaled_image) { |
765 | return Rect(wx, wy, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(window->widget)), |
766 | MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(window->widget))); |
767 | } else if (image_widget->original_image) { |
768 | return Rect(wx, wy, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(window->widget)), |
769 | MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(window->widget))); |
770 | } |
771 | |
772 | return Rect(-1, -1, -1, -1); |
773 | } |
774 | |
775 | double cvGetModeWindow_GTK(const char* name)//YV |
776 | { |
777 | CV_Assert(name && "NULL name string"); |
778 | |
779 | CV_LOCK_MUTEX(); |
780 | const auto window = icvFindWindowByName(name); |
781 | if (!window) |
782 | CV_Error( cv::Error::StsNullPtr, "NULL window"); |
783 | |
784 | double result = window->status; |
785 | return result; |
786 | } |
787 | |
788 | static bool setModeWindow_(const std::shared_ptr<CvWindow>& window, int mode); |
789 | void cvSetModeWindow_GTK( const char* name, double prop_value)//Yannick Verdie |
790 | { |
791 | CV_Assert(name && "NULL name string"); |
792 | |
793 | CV_LOCK_MUTEX(); |
794 | |
795 | const auto window = icvFindWindowByName(name); |
796 | if (!window) |
797 | CV_Error( cv::Error::StsNullPtr, "NULL window"); |
798 | |
799 | setModeWindow_(window, mode: (int)prop_value); |
800 | } |
801 | |
802 | static bool setModeWindow_(const std::shared_ptr<CvWindow>& window, int mode) |
803 | { |
804 | if (window->flags & cv::WINDOW_AUTOSIZE) //if the flag cv::WINDOW_AUTOSIZE is set |
805 | return false; |
806 | |
807 | //so easy to do fullscreen here, Linux rocks ! |
808 | |
809 | if (window->status == mode) |
810 | return true; |
811 | |
812 | if (window->status==cv::WINDOW_FULLSCREEN && mode==cv::WINDOW_NORMAL) |
813 | { |
814 | gtk_window_unfullscreen(GTK_WINDOW(window->frame)); |
815 | window->status=cv::WINDOW_NORMAL; |
816 | return true; |
817 | } |
818 | |
819 | if (window->status==cv::WINDOW_NORMAL && mode==cv::WINDOW_FULLSCREEN) |
820 | { |
821 | gtk_window_fullscreen(GTK_WINDOW(window->frame)); |
822 | window->status=cv::WINDOW_FULLSCREEN; |
823 | return true; |
824 | } |
825 | |
826 | return false; |
827 | } |
828 | |
829 | void setWindowTitle_GTK(const String& winname, const String& title) |
830 | { |
831 | CV_LOCK_MUTEX(); |
832 | |
833 | auto window = icvFindWindowByName(name: winname.c_str()); |
834 | |
835 | if (!window) |
836 | { |
837 | namedWindow(winname); |
838 | window = icvFindWindowByName(name: winname.c_str()); |
839 | CV_Assert(window); |
840 | } |
841 | |
842 | gtk_window_set_title(GTK_WINDOW(window->frame), title: title.c_str()); |
843 | } |
844 | |
845 | double cvGetPropWindowAutoSize_GTK(const char* name) |
846 | { |
847 | CV_Assert(name && "NULL name string"); |
848 | |
849 | CV_LOCK_MUTEX(); |
850 | |
851 | const auto window = icvFindWindowByName(name); |
852 | if (!window) |
853 | return -1; // keep silence here |
854 | |
855 | double result = window->flags & cv::WINDOW_AUTOSIZE; |
856 | return result; |
857 | } |
858 | |
859 | static double getRatioWindow_(const std::shared_ptr<CvWindow>& window); |
860 | double cvGetRatioWindow_GTK(const char* name) |
861 | { |
862 | CV_Assert(name && "NULL name string"); |
863 | |
864 | CV_LOCK_MUTEX(); |
865 | |
866 | const auto window = icvFindWindowByName(name); |
867 | if (!window) |
868 | return -1; // keep silence here |
869 | |
870 | return getRatioWindow_(window); |
871 | } |
872 | |
873 | static double getRatioWindow_(const std::shared_ptr<CvWindow>& window) |
874 | { |
875 | double result = static_cast<double>( |
876 | gtk_widget_get_allocated_width(widget: window->widget)) / gtk_widget_get_allocated_height(widget: window->widget); |
877 | return result; |
878 | } |
879 | |
880 | double cvGetOpenGlProp_GTK(const char* name) |
881 | { |
882 | #ifdef HAVE_OPENGL |
883 | CV_Assert(name && "NULL name string"); |
884 | |
885 | CV_LOCK_MUTEX(); |
886 | |
887 | const auto window = icvFindWindowByName(name); |
888 | if (!window) |
889 | return -1; // keep silence here |
890 | |
891 | double result = window->useGl; |
892 | return result; |
893 | #else |
894 | (void)name; |
895 | return -1; |
896 | #endif |
897 | } |
898 | |
899 | |
900 | // OpenGL support |
901 | |
902 | #ifdef HAVE_OPENGL |
903 | namespace |
904 | { |
905 | |
906 | #ifdef GTK_VERSION3 |
907 | |
908 | void glRealizeCallback(GtkGLArea* area, gpointer user_data) { |
909 | CV_UNUSED(user_data); |
910 | gtk_gl_area_make_current(area); |
911 | if (gtk_gl_area_get_error(area) != NULL) |
912 | CV_Error(cv::Error::OpenGlApiCallError, "OpenGL context is not initialized"); |
913 | } |
914 | |
915 | gboolean glRenderCallback(GtkGLArea* area, GdkGLContext* context, gpointer user_data) { |
916 | CV_UNUSED(context); |
917 | CvWindow* window = (CvWindow*)user_data; |
918 | gtk_gl_area_make_current(area); |
919 | if (gtk_gl_area_get_error(area) != NULL) { |
920 | CV_Error(cv::Error::OpenGlApiCallError, "OpenGL context is not initialized"); |
921 | return FALSE; |
922 | } |
923 | if(window->glDrawCallback) { |
924 | window->glDrawCallback(window->glDrawData); |
925 | } |
926 | // gtk_gl_area_queue_render(area); |
927 | return TRUE; |
928 | } |
929 | |
930 | #endif |
931 | |
932 | void createGlContext(CvWindow* window) |
933 | { |
934 | #ifdef GTK_VERSION3 |
935 | g_signal_connect(window->glArea, "realize", G_CALLBACK(glRealizeCallback), window); |
936 | g_signal_connect(window->glArea, "render", G_CALLBACK(glRenderCallback), window); |
937 | #else |
938 | |
939 | GdkGLConfig* glconfig; |
940 | |
941 | // Try double-buffered visual |
942 | glconfig = gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE)); |
943 | if (!glconfig) |
944 | CV_Error( cv::Error::OpenGlApiCallError, "Can't Create A GL Device Context"); |
945 | |
946 | // Set OpenGL-capability to the widget |
947 | if (!gtk_widget_set_gl_capability(window->widget, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE)) |
948 | CV_Error( cv::Error::OpenGlApiCallError, "Can't Create A GL Device Context"); |
949 | |
950 | #endif |
951 | |
952 | window->useGl = true; |
953 | } |
954 | |
955 | void drawGl(CvWindow* window) |
956 | { |
957 | #ifdef GTK_VERSION3 |
958 | |
959 | GtkGLArea* gtkGlArea = GTK_GL_AREA(window->glArea); |
960 | if (gtk_gl_area_get_error(gtkGlArea) != NULL) |
961 | CV_Error(cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context"); |
962 | |
963 | if (window->glDrawCallback) |
964 | window->glDrawCallback(window->glDrawData); |
965 | |
966 | #else |
967 | |
968 | GdkGLContext* glcontext = gtk_widget_get_gl_context(window->widget); |
969 | GdkGLDrawable* gldrawable = gtk_widget_get_gl_drawable(window->widget); |
970 | |
971 | if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) |
972 | CV_Error( cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context"); |
973 | |
974 | glViewport(0, 0, gtk_widget_get_allocated_width(window->widget), gtk_widget_get_allocated_height(window->widget)); |
975 | |
976 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
977 | |
978 | if (window->glDrawCallback) |
979 | window->glDrawCallback(window->glDrawData); |
980 | |
981 | if (gdk_gl_drawable_is_double_buffered (gldrawable)) |
982 | gdk_gl_drawable_swap_buffers(gldrawable); |
983 | else |
984 | glFlush(); |
985 | |
986 | gdk_gl_drawable_gl_end(gldrawable); |
987 | |
988 | #endif |
989 | } |
990 | } |
991 | |
992 | #endif // HAVE_OPENGL |
993 | |
994 | #if defined (GTK_VERSION2) |
995 | static gboolean cvImageWidget_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data) |
996 | #elif defined (GTK_VERSION3) |
997 | static gboolean cvImageWidget_draw(GtkWidget* widget, cairo_t *cr, gpointer data) |
998 | #endif |
999 | { |
1000 | #ifdef HAVE_OPENGL |
1001 | CvWindow* window = (CvWindow*)data; |
1002 | |
1003 | if (window->useGl) |
1004 | { |
1005 | drawGl(window); |
1006 | return TRUE; |
1007 | } |
1008 | #else |
1009 | (void)data; |
1010 | #endif |
1011 | #if defined (GTK_VERSION2) |
1012 | (void)event; |
1013 | #endif |
1014 | |
1015 | CvImageWidget *image_widget = NULL; |
1016 | GdkPixbuf *pixbuf = NULL; |
1017 | |
1018 | g_return_val_if_fail (widget != NULL, FALSE); |
1019 | g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE); |
1020 | |
1021 | image_widget = CV_IMAGE_WIDGET (widget); |
1022 | #if defined (GTK_VERSION2) |
1023 | cairo_t *cr = gdk_cairo_create(widget->window); |
1024 | #endif |
1025 | |
1026 | if( image_widget->scaled_image ){ |
1027 | // center image in available region |
1028 | int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2; |
1029 | int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2; |
1030 | |
1031 | pixbuf = gdk_pixbuf_new_from_data(data: image_widget->scaled_image->data.ptr, colorspace: GDK_COLORSPACE_RGB, has_alpha: false, |
1032 | bits_per_sample: 8, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(widget)), |
1033 | MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(widget)), |
1034 | rowstride: image_widget->scaled_image->step, NULL, NULL); |
1035 | |
1036 | gdk_cairo_set_source_pixbuf(cr, pixbuf, pixbuf_x: x0, pixbuf_y: y0); |
1037 | } |
1038 | else if( image_widget->original_image ){ |
1039 | pixbuf = gdk_pixbuf_new_from_data(data: image_widget->original_image->data.ptr, colorspace: GDK_COLORSPACE_RGB, has_alpha: false, |
1040 | bits_per_sample: 8, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(widget)), |
1041 | MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(widget)), |
1042 | rowstride: image_widget->original_image->step, NULL, NULL); |
1043 | gdk_cairo_set_source_pixbuf(cr, pixbuf, pixbuf_x: 0, pixbuf_y: 0); |
1044 | } |
1045 | |
1046 | cairo_paint(cr); |
1047 | if(pixbuf) |
1048 | g_object_unref(object: pixbuf); |
1049 | #if defined (GTK_VERSION2) |
1050 | cairo_destroy(cr); |
1051 | #endif |
1052 | return TRUE; |
1053 | } |
1054 | |
1055 | static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags); |
1056 | CV_IMPL int cvNamedWindow( const char* name, int flags ) |
1057 | { |
1058 | cvInitSystem(argc: name ? 1 : 0,argv: (char**)&name); |
1059 | CV_Assert(name && "NULL name string"); |
1060 | |
1061 | CV_LOCK_MUTEX(); |
1062 | |
1063 | // Check the name in the storage |
1064 | if (icvFindWindowByName(name)) |
1065 | { |
1066 | return 1; |
1067 | } |
1068 | auto window = namedWindow_(name, flags); |
1069 | return window ? 1 : 0; |
1070 | } |
1071 | |
1072 | static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags) |
1073 | { |
1074 | cvInitSystem(argc: 0, NULL); |
1075 | |
1076 | auto window_ptr = std::make_shared<CvWindow>(args: name); |
1077 | CvWindow* window = window_ptr.get(); |
1078 | window->flags = flags; |
1079 | window->status = cv::WINDOW_NORMAL;//YV |
1080 | |
1081 | window->frame = gtk_window_new( type: GTK_WINDOW_TOPLEVEL ); |
1082 | |
1083 | window->widget = cvImageWidgetNew( flags ); |
1084 | |
1085 | #if defined(HAVE_OPENGL) && defined(GTK_VERSION3) |
1086 | if (flags & cv::WINDOW_OPENGL) { |
1087 | window->glArea = gtk_gl_area_new(); |
1088 | gtk_container_add(GTK_CONTAINER(window->frame), window->glArea); |
1089 | gtk_widget_show(window->glArea); |
1090 | } else { |
1091 | window->paned = gtk_vbox_new( FALSE, 0 ); |
1092 | gtk_box_pack_end( GTK_BOX(window->paned), window->widget, TRUE, TRUE, 0 ); |
1093 | gtk_widget_show( window->widget ); |
1094 | gtk_container_add( GTK_CONTAINER(window->frame), window->paned ); |
1095 | gtk_widget_show( window->paned ); |
1096 | } |
1097 | #else |
1098 | window->paned = gtk_vbox_new( FALSE, spacing: 0 ); |
1099 | gtk_box_pack_end( GTK_BOX(window->paned), child: window->widget, TRUE, TRUE, padding: 0 ); |
1100 | gtk_widget_show( widget: window->widget ); |
1101 | gtk_container_add( GTK_CONTAINER(window->frame), widget: window->paned ); |
1102 | gtk_widget_show( widget: window->paned ); |
1103 | #endif |
1104 | |
1105 | #ifndef HAVE_OPENGL |
1106 | if (flags & cv::WINDOW_OPENGL) |
1107 | CV_Error( cv::Error::OpenGlNotSupported, "Library was built without OpenGL support"); |
1108 | #else |
1109 | if (flags & cv::WINDOW_OPENGL) |
1110 | createGlContext(window); |
1111 | |
1112 | window->glDrawCallback = 0; |
1113 | window->glDrawData = 0; |
1114 | #endif |
1115 | |
1116 | // |
1117 | // configure event handlers |
1118 | // TODO -- move this to CvImageWidget ? |
1119 | g_signal_connect( window->frame, "key-press-event", |
1120 | G_CALLBACK(icvOnKeyPress), window ); |
1121 | g_signal_connect( window->widget, "button-press-event", |
1122 | G_CALLBACK(icvOnMouse), window ); |
1123 | g_signal_connect( window->widget, "button-release-event", |
1124 | G_CALLBACK(icvOnMouse), window ); |
1125 | g_signal_connect( window->widget, "motion-notify-event", |
1126 | G_CALLBACK(icvOnMouse), window ); |
1127 | g_signal_connect( window->widget, "scroll-event", |
1128 | G_CALLBACK(icvOnMouse), window ); |
1129 | g_signal_connect( window->frame, "delete-event", |
1130 | G_CALLBACK(icvOnClose), window ); |
1131 | #if defined(GTK_VERSION3) |
1132 | g_signal_connect( window->widget, "draw", |
1133 | G_CALLBACK(cvImageWidget_draw), window ); |
1134 | #else |
1135 | g_signal_connect( window->widget, "expose-event", |
1136 | G_CALLBACK(cvImageWidget_expose), window ); |
1137 | #endif //GTK_VERSION3 |
1138 | |
1139 | |
1140 | #if defined(GTK_VERSION3_4) |
1141 | gtk_widget_add_events (widget: window->widget, events: GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK) ; |
1142 | #else |
1143 | gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK) ; |
1144 | #endif //GTK_VERSION3_4 |
1145 | |
1146 | gtk_widget_show( widget: window->frame ); |
1147 | gtk_window_set_title(GTK_WINDOW(window->frame), title: name.c_str()); |
1148 | |
1149 | { |
1150 | AutoLock lock(getWindowMutex()); |
1151 | getGTKWindows().push_back(x: window_ptr); |
1152 | } |
1153 | |
1154 | bool b_nautosize = ((flags & cv::WINDOW_AUTOSIZE) == 0); |
1155 | gtk_window_set_resizable( GTK_WINDOW(window->frame), resizable: b_nautosize ); |
1156 | |
1157 | // allow window to be resized |
1158 | if( b_nautosize ){ |
1159 | GdkGeometry geometry; |
1160 | geometry.min_width = 50; |
1161 | geometry.min_height = 50; |
1162 | gtk_window_set_geometry_hints( GTK_WINDOW( window->frame ), GTK_WIDGET( window->widget ), |
1163 | geometry: &geometry, geom_mask: (GdkWindowHints) (GDK_HINT_MIN_SIZE)); |
1164 | } |
1165 | |
1166 | #ifdef HAVE_OPENGL |
1167 | if (window->useGl) |
1168 | cvSetOpenGlContext(name.c_str()); |
1169 | #endif |
1170 | |
1171 | return window_ptr; |
1172 | } |
1173 | |
1174 | |
1175 | #ifdef HAVE_OPENGL |
1176 | |
1177 | CV_IMPL void cvSetOpenGlContext(const char* name) |
1178 | { |
1179 | CV_Assert(name && "NULL name string"); |
1180 | |
1181 | CV_LOCK_MUTEX(); |
1182 | |
1183 | auto window = icvFindWindowByName(name); |
1184 | if (!window) |
1185 | CV_Error( cv::Error::StsNullPtr, "NULL window"); |
1186 | |
1187 | if (!window->useGl) |
1188 | CV_Error( cv::Error::OpenGlNotSupported, "Window doesn't support OpenGL"); |
1189 | |
1190 | #ifdef GTK_VERSION3 |
1191 | |
1192 | if(gtk_gl_area_get_error(GTK_GL_AREA(window->glArea)) != NULL) |
1193 | CV_Error( cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context"); |
1194 | |
1195 | #else |
1196 | |
1197 | GdkGLContext* glcontext; |
1198 | GdkGLDrawable* gldrawable; |
1199 | |
1200 | glcontext = gtk_widget_get_gl_context(window->widget); |
1201 | gldrawable = gtk_widget_get_gl_drawable(window->widget); |
1202 | |
1203 | if (!gdk_gl_drawable_make_current(gldrawable, glcontext)) |
1204 | CV_Error( cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context"); |
1205 | |
1206 | #endif |
1207 | |
1208 | } |
1209 | |
1210 | CV_IMPL void cvUpdateWindow(const char* name) |
1211 | { |
1212 | CV_Assert(name && "NULL name string"); |
1213 | |
1214 | CV_LOCK_MUTEX(); |
1215 | |
1216 | auto window = icvFindWindowByName(name); |
1217 | if (!window) |
1218 | return; |
1219 | |
1220 | // window does not refresh without this |
1221 | #ifdef GTK_VERSION3 |
1222 | |
1223 | if ( GTK_IS_GL_AREA(window->glArea) ){ |
1224 | gtk_gl_area_queue_render(GTK_GL_AREA(window->glArea)); |
1225 | } else { |
1226 | gtk_widget_queue_draw( GTK_WIDGET(window->widget)); |
1227 | } |
1228 | |
1229 | #else |
1230 | |
1231 | gtk_widget_queue_draw( GTK_WIDGET(window->widget) ); |
1232 | |
1233 | #endif |
1234 | |
1235 | } |
1236 | |
1237 | CV_IMPL void cvSetOpenGlDrawCallback(const char* name, CvOpenGlDrawCallback callback, void* userdata) |
1238 | { |
1239 | CV_Assert(name && "NULL name string"); |
1240 | |
1241 | CV_LOCK_MUTEX(); |
1242 | |
1243 | auto window = icvFindWindowByName(name); |
1244 | if( !window ) |
1245 | return; |
1246 | |
1247 | if (!window->useGl) |
1248 | CV_Error( cv::Error::OpenGlNotSupported, "Window was created without OpenGL context"); |
1249 | |
1250 | window->glDrawCallback = callback; |
1251 | window->glDrawData = userdata; |
1252 | } |
1253 | |
1254 | #endif // HAVE_OPENGL |
1255 | |
1256 | |
1257 | |
1258 | CvWindow::~CvWindow() |
1259 | { |
1260 | if (frame) |
1261 | destroy(); |
1262 | } |
1263 | |
1264 | inline void CvWindow::destroy() |
1265 | { |
1266 | CV_LOG_INFO(NULL, "OpenCV/UI: destroying GTK window: "<< name); |
1267 | gtk_widget_destroy(widget: frame); |
1268 | frame = nullptr; |
1269 | } |
1270 | |
1271 | static void checkLastWindow() |
1272 | { |
1273 | // if last window... |
1274 | if (getGTKWindows().empty()) |
1275 | { |
1276 | if( thread_started ) |
1277 | { |
1278 | // send key press signal to jump out of any waiting cvWaitKey's |
1279 | g_cond_broadcast( cond: cond_have_key ); |
1280 | } |
1281 | else |
1282 | { |
1283 | // Some GTK+ modules (like the Unity module) use GDBusConnection, |
1284 | // which has a habit of postponing cleanup by performing it via |
1285 | // idle sources added to the main loop. Since this was the last window, |
1286 | // we can assume that no event processing is going to happen in the |
1287 | // nearest future, so we should force that cleanup (by handling all pending |
1288 | // events) while we still have the chance. |
1289 | // This is not needed if thread_started is true, because the background |
1290 | // thread will process events continuously. |
1291 | while( gtk_events_pending() ) |
1292 | gtk_main_iteration(); |
1293 | } |
1294 | } |
1295 | } |
1296 | |
1297 | static |
1298 | void icvDeleteWindow_( CvWindow* window ) |
1299 | { |
1300 | AutoLock lock(getWindowMutex()); |
1301 | auto& g_windows = getGTKWindows(); |
1302 | bool found = false; |
1303 | for (auto i = g_windows.begin(); i != g_windows.end(); ++i) |
1304 | { |
1305 | if (i->get() == window) |
1306 | { |
1307 | g_windows.erase(position: i); |
1308 | found = true; |
1309 | break; |
1310 | } |
1311 | } |
1312 | CV_LOG_IF_WARNING(NULL, !found, "OpenCV/GTK: Can't destroy non-registered window"); |
1313 | checkLastWindow(); |
1314 | } |
1315 | |
1316 | CV_IMPL void cvDestroyWindow( const char* name ) |
1317 | { |
1318 | CV_Assert(name && "NULL name string"); |
1319 | |
1320 | CV_LOCK_MUTEX(); |
1321 | auto& g_windows = getGTKWindows(); |
1322 | |
1323 | bool found = false; |
1324 | for (auto i = g_windows.begin(); i != g_windows.end(); ++i) |
1325 | { |
1326 | if (i->get()->name == name) |
1327 | { |
1328 | g_windows.erase(position: i); |
1329 | found = true; |
1330 | break; |
1331 | } |
1332 | } |
1333 | CV_LOG_IF_ERROR(NULL, !found, "OpenCV/GTK: Can't destroy non-registered window: '"<< name << "'"); |
1334 | |
1335 | checkLastWindow(); |
1336 | } |
1337 | |
1338 | |
1339 | CV_IMPL void |
1340 | cvDestroyAllWindows( void ) |
1341 | { |
1342 | CV_LOCK_MUTEX(); |
1343 | |
1344 | getGTKWindows().clear(); |
1345 | checkLastWindow(); |
1346 | } |
1347 | |
1348 | // CvSize icvCalcOptimalWindowSize( CvWindow * window, CvSize new_image_size){ |
1349 | // CvSize window_size; |
1350 | // GtkWidget * toplevel = gtk_widget_get_toplevel( window->frame ); |
1351 | // gdk_drawable_get_size( GDK_DRAWABLE(toplevel->window), |
1352 | // &window_size.width, &window_size.height ); |
1353 | |
1354 | // window_size.width = window_size.width + new_image_size.width - window->widget->allocation.width; |
1355 | // window_size.height = window_size.height + new_image_size.height - window->widget->allocation.height; |
1356 | |
1357 | // return window_size; |
1358 | // } |
1359 | |
1360 | CV_IMPL void |
1361 | cvShowImage( const char* name, const CvArr* arr ) |
1362 | { |
1363 | CV_Assert(name && "NULL name string"); |
1364 | |
1365 | CV_LOCK_MUTEX(); |
1366 | |
1367 | auto window = icvFindWindowByName(name); |
1368 | if(!window) |
1369 | { |
1370 | cvNamedWindow(name, flags: 1); |
1371 | window = icvFindWindowByName(name); |
1372 | } |
1373 | CV_Assert(window); |
1374 | |
1375 | if (arr) |
1376 | { |
1377 | #ifdef HAVE_OPENGL |
1378 | if (window->useGl) |
1379 | { |
1380 | cv::imshow(name, cv::cvarrToMat(arr)); |
1381 | return; |
1382 | } |
1383 | #endif |
1384 | |
1385 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget ); |
1386 | cvImageWidgetSetImage( widget: image_widget, arr ); |
1387 | } |
1388 | } |
1389 | |
1390 | static void resizeWindow_(const std::shared_ptr<CvWindow>& window, int width, int height); |
1391 | CV_IMPL void cvResizeWindow(const char* name, int width, int height ) |
1392 | { |
1393 | CV_Assert(name && "NULL name string"); |
1394 | |
1395 | CV_LOCK_MUTEX(); |
1396 | |
1397 | auto window = icvFindWindowByName(name); |
1398 | if(!window) |
1399 | return; |
1400 | |
1401 | return resizeWindow_(window, width, height); |
1402 | } |
1403 | |
1404 | static |
1405 | void resizeWindow_(const std::shared_ptr<CvWindow>& window, int width, int height) |
1406 | { |
1407 | CV_Assert(window); |
1408 | CvImageWidget* image_widget = CV_IMAGE_WIDGET( window->widget ); |
1409 | //if(image_widget->flags & cv::WINDOW_AUTOSIZE) |
1410 | //EXIT; |
1411 | |
1412 | gtk_window_set_resizable( GTK_WINDOW(window->frame), resizable: 1 ); |
1413 | gtk_window_resize( GTK_WINDOW(window->frame), width, height ); |
1414 | |
1415 | // disable initial resize since presumably user wants to keep |
1416 | // this window size |
1417 | image_widget->flags &= ~CV_WINDOW_NO_IMAGE; |
1418 | } |
1419 | |
1420 | |
1421 | CV_IMPL void cvMoveWindow( const char* name, int x, int y ) |
1422 | { |
1423 | CV_Assert(name && "NULL name string"); |
1424 | |
1425 | CV_LOCK_MUTEX(); |
1426 | |
1427 | const auto window = icvFindWindowByName(name); |
1428 | if(!window) |
1429 | return; |
1430 | |
1431 | gtk_window_move( GTK_WINDOW(window->frame), x, y ); |
1432 | } |
1433 | |
1434 | static |
1435 | std::shared_ptr<CvTrackbar> icvFindTrackbarByName(const std::shared_ptr<CvWindow>& window, const std::string& name) |
1436 | { |
1437 | CV_Assert(window); |
1438 | auto& trackbars = window->trackbars; |
1439 | for(size_t i = 0; i < trackbars.size(); ++i) |
1440 | { |
1441 | auto trackbar = trackbars[i]; |
1442 | if (!trackbar) |
1443 | continue; |
1444 | if (trackbar->name == name) |
1445 | return trackbar; |
1446 | } |
1447 | return std::shared_ptr<CvTrackbar>(); |
1448 | } |
1449 | |
1450 | |
1451 | static int |
1452 | icvCreateTrackbar( const char* trackbar_name, const char* window_name, |
1453 | int* val, int count, CvTrackbarCallback on_notify, |
1454 | CvTrackbarCallback2 on_notify2, void* userdata ) |
1455 | { |
1456 | CV_Assert(window_name && "NULL window name"); |
1457 | CV_Assert(trackbar_name && "NULL trackbar name"); |
1458 | |
1459 | if( count <= 0 ) |
1460 | CV_Error( cv::Error::StsOutOfRange, "Bad trackbar maximal value"); |
1461 | |
1462 | CV_LOCK_MUTEX(); |
1463 | |
1464 | const auto window = icvFindWindowByName(name: window_name); |
1465 | if(!window) |
1466 | return 0; |
1467 | |
1468 | auto trackbar_ = icvFindTrackbarByName(window, name: trackbar_name); |
1469 | if (!trackbar_) |
1470 | { |
1471 | trackbar_ = std::make_shared<CvTrackbar>(args&: trackbar_name); |
1472 | CvTrackbar* trackbar = trackbar_.get(); |
1473 | trackbar->parent = window.get(); |
1474 | window->trackbars.push_back(x: trackbar_); |
1475 | |
1476 | GtkWidget* hscale_box = gtk_hbox_new( FALSE, spacing: 10 ); |
1477 | GtkWidget* hscale_label = gtk_label_new( str: trackbar_name ); |
1478 | GtkWidget* hscale = gtk_hscale_new_with_range( min: 0, max: count, step: 1 ); |
1479 | gtk_scale_set_digits( GTK_SCALE(hscale), digits: 0 ); |
1480 | //gtk_scale_set_value_pos( hscale, GTK_POS_TOP ); |
1481 | gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE ); |
1482 | |
1483 | trackbar->widget = hscale; |
1484 | gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale_label, FALSE, FALSE, padding: 5 ); |
1485 | gtk_widget_show( widget: hscale_label ); |
1486 | gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale, TRUE, TRUE, padding: 5 ); |
1487 | gtk_widget_show( widget: hscale ); |
1488 | gtk_box_pack_start( GTK_BOX(window->paned), child: hscale_box, FALSE, FALSE, padding: 5 ); |
1489 | gtk_widget_show( widget: hscale_box ); |
1490 | } |
1491 | |
1492 | CvTrackbar* trackbar = trackbar_.get(); CV_DbgAssert(trackbar); |
1493 | |
1494 | if( val ) |
1495 | { |
1496 | int value = *val; |
1497 | if( value < 0 ) |
1498 | value = 0; |
1499 | if( value > count ) |
1500 | value = count; |
1501 | gtk_range_set_value( GTK_RANGE(trackbar->widget), value ); |
1502 | trackbar->pos = value; |
1503 | trackbar->data = val; |
1504 | } |
1505 | |
1506 | trackbar->maxval = count; |
1507 | trackbar->notify = on_notify; |
1508 | trackbar->notify2 = on_notify2; |
1509 | trackbar->userdata = userdata; |
1510 | g_signal_connect( trackbar->widget, "value-changed", |
1511 | G_CALLBACK(icvOnTrackbar), trackbar ); |
1512 | |
1513 | // queue a widget resize to trigger a window resize to |
1514 | // compensate for the addition of trackbars |
1515 | gtk_widget_queue_resize( GTK_WIDGET(window->widget) ); |
1516 | |
1517 | return 1; |
1518 | } |
1519 | |
1520 | CV_IMPL int |
1521 | cvCreateTrackbar( const char* trackbar_name, const char* window_name, |
1522 | int* val, int count, CvTrackbarCallback on_notify ) |
1523 | { |
1524 | return icvCreateTrackbar(trackbar_name, window_name, val, count, |
1525 | on_notify, on_notify2: 0, userdata: 0); |
1526 | } |
1527 | |
1528 | CV_IMPL int |
1529 | cvCreateTrackbar2( const char* trackbar_name, const char* window_name, |
1530 | int* val, int count, CvTrackbarCallback2 on_notify2, |
1531 | void* userdata ) |
1532 | { |
1533 | return icvCreateTrackbar(trackbar_name, window_name, val, count, |
1534 | on_notify: 0, on_notify2, userdata); |
1535 | } |
1536 | |
1537 | static |
1538 | std::shared_ptr<CvTrackbar> createTrackbar_( |
1539 | const std::shared_ptr<CvWindow>& window, const std::string& name, |
1540 | int count, |
1541 | TrackbarCallback onChange, void* userdata |
1542 | ) |
1543 | { |
1544 | CV_Assert(window); |
1545 | CV_Assert(!name.empty()); |
1546 | |
1547 | if (count <= 0) |
1548 | CV_Error(Error::StsOutOfRange, "Bad trackbar maximal value"); |
1549 | |
1550 | auto trackbar_ = std::make_shared<CvTrackbar>(args: name); |
1551 | CvTrackbar* trackbar = trackbar_.get(); |
1552 | trackbar->parent = window.get(); |
1553 | window->trackbars.push_back(x: trackbar_); |
1554 | |
1555 | GtkWidget* hscale_box = gtk_hbox_new( FALSE, spacing: 10 ); |
1556 | GtkWidget* hscale_label = gtk_label_new(str: name.c_str()); |
1557 | GtkWidget* hscale = gtk_hscale_new_with_range( min: 0, max: count, step: 1 ); |
1558 | gtk_scale_set_digits( GTK_SCALE(hscale), digits: 0 ); |
1559 | //gtk_scale_set_value_pos( hscale, GTK_POS_TOP ); |
1560 | gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE ); |
1561 | |
1562 | trackbar->widget = hscale; |
1563 | gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale_label, FALSE, FALSE, padding: 5 ); |
1564 | gtk_widget_show( widget: hscale_label ); |
1565 | gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale, TRUE, TRUE, padding: 5 ); |
1566 | gtk_widget_show( widget: hscale ); |
1567 | gtk_box_pack_start( GTK_BOX(window->paned), child: hscale_box, FALSE, FALSE, padding: 5 ); |
1568 | gtk_widget_show( widget: hscale_box ); |
1569 | |
1570 | trackbar->maxval = count; |
1571 | trackbar->onChangeCallback = onChange; |
1572 | trackbar->userdata = userdata; |
1573 | g_signal_connect(trackbar->widget, "value-changed", |
1574 | G_CALLBACK(icvOnTrackbar), trackbar); |
1575 | |
1576 | // queue a widget resize to trigger a window resize to |
1577 | // compensate for the addition of trackbars |
1578 | gtk_widget_queue_resize(GTK_WIDGET(window->widget)); |
1579 | |
1580 | return trackbar_; |
1581 | } |
1582 | |
1583 | |
1584 | CV_IMPL void |
1585 | cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param ) |
1586 | { |
1587 | CV_Assert(window_name && "NULL window name"); |
1588 | |
1589 | CV_LOCK_MUTEX(); |
1590 | |
1591 | const auto window = icvFindWindowByName(name: window_name); |
1592 | if (!window) |
1593 | return; |
1594 | |
1595 | window->on_mouse = on_mouse; |
1596 | window->on_mouse_param = param; |
1597 | } |
1598 | |
1599 | |
1600 | CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name ) |
1601 | { |
1602 | CV_Assert(window_name && "NULL window name"); |
1603 | CV_Assert(trackbar_name && "NULL trackbar name"); |
1604 | |
1605 | CV_LOCK_MUTEX(); |
1606 | |
1607 | const auto window = icvFindWindowByName(name: window_name); |
1608 | if (!window) |
1609 | return -1; |
1610 | |
1611 | const auto trackbar = icvFindTrackbarByName(window,name: trackbar_name); |
1612 | if (!trackbar) |
1613 | return -1; |
1614 | |
1615 | return trackbar->pos; |
1616 | } |
1617 | |
1618 | static void setTrackbarPos_(const std::shared_ptr<CvTrackbar>& trackbar, int pos); |
1619 | CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos ) |
1620 | { |
1621 | CV_Assert(window_name && "NULL window name"); |
1622 | CV_Assert(trackbar_name && "NULL trackbar name"); |
1623 | |
1624 | CV_LOCK_MUTEX(); |
1625 | |
1626 | const auto window = icvFindWindowByName(name: window_name); |
1627 | if(!window) |
1628 | return; |
1629 | |
1630 | const auto trackbar = icvFindTrackbarByName(window, name: trackbar_name); |
1631 | if (!trackbar) |
1632 | { |
1633 | CV_Error( cv::Error::StsNullPtr, "No trackbar found"); |
1634 | } |
1635 | |
1636 | return setTrackbarPos_(trackbar, pos); |
1637 | } |
1638 | |
1639 | static void setTrackbarPos_(const std::shared_ptr<CvTrackbar>& trackbar, int pos) |
1640 | { |
1641 | CV_Assert(trackbar); |
1642 | CV_CheckLE(trackbar->minval, trackbar->maxval, ""); |
1643 | |
1644 | pos = std::max(a: pos, b: trackbar->minval); |
1645 | pos = std::min(a: pos, b: trackbar->maxval); |
1646 | |
1647 | gtk_range_set_value( GTK_RANGE(trackbar->widget), value: pos ); |
1648 | } |
1649 | |
1650 | |
1651 | CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval) |
1652 | { |
1653 | CV_Assert(window_name && "NULL window name"); |
1654 | CV_Assert(trackbar_name && "NULL trackbar name"); |
1655 | |
1656 | CV_LOCK_MUTEX(); |
1657 | |
1658 | const auto window = icvFindWindowByName(name: window_name); |
1659 | if(!window) |
1660 | return; |
1661 | |
1662 | const auto trackbar = icvFindTrackbarByName(window,name: trackbar_name); |
1663 | if(!trackbar) |
1664 | return; |
1665 | |
1666 | trackbar->maxval = maxval; |
1667 | if (trackbar->maxval >= trackbar->minval) |
1668 | gtk_range_set_range(GTK_RANGE(trackbar->widget), min: trackbar->minval, max: trackbar->maxval); |
1669 | } |
1670 | |
1671 | |
1672 | CV_IMPL void cvSetTrackbarMin(const char* trackbar_name, const char* window_name, int minval) |
1673 | { |
1674 | CV_Assert(window_name && "NULL window name"); |
1675 | CV_Assert(trackbar_name && "NULL trackbar name"); |
1676 | |
1677 | CV_LOCK_MUTEX(); |
1678 | |
1679 | const auto window = icvFindWindowByName(name: window_name); |
1680 | if(!window) |
1681 | return; |
1682 | |
1683 | const auto trackbar = icvFindTrackbarByName(window,name: trackbar_name); |
1684 | if(!trackbar) |
1685 | return; |
1686 | |
1687 | trackbar->minval = minval; |
1688 | if (trackbar->maxval >= trackbar->minval) |
1689 | gtk_range_set_range(GTK_RANGE(trackbar->widget), min: trackbar->minval, max: trackbar->maxval); |
1690 | } |
1691 | |
1692 | |
1693 | CV_IMPL void* cvGetWindowHandle( const char* window_name ) |
1694 | { |
1695 | CV_Assert(window_name && "NULL window name"); |
1696 | |
1697 | CV_LOCK_MUTEX(); |
1698 | |
1699 | const auto window = icvFindWindowByName(name: window_name); |
1700 | if(!window) |
1701 | return NULL; |
1702 | |
1703 | return (void*)window->widget; |
1704 | } |
1705 | |
1706 | |
1707 | CV_IMPL const char* cvGetWindowName( void* window_handle ) |
1708 | { |
1709 | CV_Assert(window_handle && "NULL window handle"); |
1710 | |
1711 | CV_LOCK_MUTEX(); |
1712 | |
1713 | CvWindow* window = icvWindowByWidget( widget: (GtkWidget*)window_handle ); |
1714 | if (window) |
1715 | return window->name.c_str(); |
1716 | |
1717 | return ""; // FIXME: NULL? |
1718 | } |
1719 | |
1720 | static GtkFileFilter* icvMakeGtkFilter(const char* name, const char* patterns, GtkFileFilter* images) |
1721 | { |
1722 | GtkFileFilter* filter = gtk_file_filter_new(); |
1723 | gtk_file_filter_set_name(filter, name); |
1724 | |
1725 | while(patterns[0]) |
1726 | { |
1727 | gtk_file_filter_add_pattern(filter, pattern: patterns); |
1728 | gtk_file_filter_add_pattern(filter: images, pattern: patterns); |
1729 | patterns += strlen(s: patterns) + 1; |
1730 | } |
1731 | |
1732 | return filter; |
1733 | } |
1734 | |
1735 | static void icvShowSaveAsDialog(GtkWidget* widget, CvWindow* window) |
1736 | { |
1737 | if (!window || !widget) |
1738 | return; |
1739 | |
1740 | CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget); |
1741 | if (!image_widget || !image_widget->original_image) |
1742 | return; |
1743 | |
1744 | GtkWidget* dialog = gtk_file_chooser_dialog_new(title: "Save As...", |
1745 | GTK_WINDOW(widget), |
1746 | action: GTK_FILE_CHOOSER_ACTION_SAVE, |
1747 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
1748 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, |
1749 | NULL); |
1750 | gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); |
1751 | |
1752 | cv::String sname = gtk_window_get_title(GTK_WINDOW(window->frame)); |
1753 | sname = sname.substr(pos: sname.find_last_of(s: "\\/") + 1) + ".png"; |
1754 | gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), name: sname.c_str()); |
1755 | |
1756 | GtkFileFilter* filter_all = gtk_file_filter_new(); |
1757 | gtk_file_filter_set_name(filter: filter_all, name: "All Files"); |
1758 | gtk_file_filter_add_pattern(filter: filter_all, pattern: "*"); |
1759 | |
1760 | GtkFileFilter* filter_images = gtk_file_filter_new(); |
1761 | gtk_file_filter_set_name(filter: filter_images, name: "All Images"); |
1762 | |
1763 | GtkFileFilter* file_filters[] = { |
1764 | icvMakeGtkFilter(name: "Portable Network Graphics files (*.png)", patterns: "*.png\0", images: filter_images), |
1765 | icvMakeGtkFilter(name: "JPEG files (*.jpeg;*.jpg;*.jpe)", patterns: "*.jpeg\0*.jpg\0*.jpe\0", images: filter_images), |
1766 | icvMakeGtkFilter(name: "Windows bitmap (*.bmp;*.dib)", patterns: "*.bmp\0*.dib\0", images: filter_images), |
1767 | icvMakeGtkFilter(name: "TIFF Files (*.tiff;*.tif)", patterns: "*.tiff\0*.tif\0", images: filter_images), |
1768 | icvMakeGtkFilter(name: "JPEG-2000 files (*.jp2)", patterns: "*.jp2\0", images: filter_images), |
1769 | icvMakeGtkFilter(name: "WebP files (*.webp)", patterns: "*.webp\0", images: filter_images), |
1770 | icvMakeGtkFilter(name: "Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)", patterns: "*.pbm\0*.pgm\0*.ppm\0*.pxm\0*.pnm\0", images: filter_images), |
1771 | icvMakeGtkFilter(name: "OpenEXR Image files (*.exr)", patterns: "*.exr\0", images: filter_images), |
1772 | icvMakeGtkFilter(name: "Radiance HDR (*.hdr;*.pic)", patterns: "*.hdr\0*.pic\0", images: filter_images), |
1773 | icvMakeGtkFilter(name: "Sun raster files (*.sr;*.ras)", patterns: "*.sr\0*.ras\0", images: filter_images), |
1774 | filter_images, |
1775 | filter_all |
1776 | }; |
1777 | |
1778 | for (size_t idx = 0; idx < sizeof(file_filters)/sizeof(file_filters[0]); ++idx) |
1779 | gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter: file_filters[idx]); // filter ownership is transferred to dialog |
1780 | gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter: filter_images); |
1781 | |
1782 | cv::String filename; |
1783 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) |
1784 | { |
1785 | char* fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); |
1786 | filename = fname; |
1787 | g_free(mem: fname); |
1788 | } |
1789 | gtk_widget_destroy(widget: dialog); |
1790 | |
1791 | if (!filename.empty()) |
1792 | { |
1793 | cv::Mat bgr; |
1794 | cv::cvtColor(src: cv::cvarrToMat(arr: image_widget->original_image), dst: bgr, code: cv::COLOR_RGB2BGR); |
1795 | cv::imwrite(filename, img: bgr); |
1796 | } |
1797 | } |
1798 | |
1799 | #if defined(GTK_VERSION2) && !defined (GDK_KEY_Escape) |
1800 | #define GDK_KEY_Escape GDK_Escape |
1801 | #define GDK_KEY_Return GDK_Return |
1802 | #define GDK_KEY_Linefeed GDK_Linefeed |
1803 | #define GDK_KEY_Tab GDK_Tab |
1804 | #define GDK_KEY_s GDK_s |
1805 | #define GDK_KEY_S GDK_S |
1806 | #endif //GDK_KEY_Escape |
1807 | |
1808 | static gboolean icvOnKeyPress(GtkWidget* widget, GdkEventKey* event, gpointer user_data) |
1809 | { |
1810 | int code = 0; |
1811 | |
1812 | if ( BIT_ALLIN(event->state, GDK_CONTROL_MASK) && (event->keyval == GDK_KEY_s || event->keyval == GDK_KEY_S)) |
1813 | { |
1814 | try |
1815 | { |
1816 | icvShowSaveAsDialog(widget, window: (CvWindow*)user_data); |
1817 | } |
1818 | catch(...) |
1819 | { |
1820 | // suppress all exceptions here |
1821 | } |
1822 | } |
1823 | |
1824 | switch( event->keyval ) |
1825 | { |
1826 | case GDK_KEY_Escape: |
1827 | code = 27; |
1828 | break; |
1829 | case GDK_KEY_Return: |
1830 | case GDK_KEY_Linefeed: |
1831 | code = 13; |
1832 | break; |
1833 | case GDK_KEY_Tab: |
1834 | code = '\t'; |
1835 | break; |
1836 | default: |
1837 | code = event->keyval; |
1838 | } |
1839 | |
1840 | code |= event->state << 16; |
1841 | |
1842 | if(thread_started) |
1843 | { |
1844 | g_mutex_lock(mutex: last_key_mutex); |
1845 | last_key = code; |
1846 | // signal any waiting threads |
1847 | g_cond_broadcast(cond: cond_have_key); |
1848 | g_mutex_unlock(mutex: last_key_mutex); |
1849 | } |
1850 | else |
1851 | { |
1852 | last_key = code; |
1853 | } |
1854 | |
1855 | return FALSE; |
1856 | } |
1857 | |
1858 | |
1859 | static void icvOnTrackbar( GtkWidget* widget, gpointer user_data ) |
1860 | { |
1861 | int pos = cvRound( value: gtk_range_get_value(GTK_RANGE(widget))); |
1862 | CvTrackbar* trackbar = (CvTrackbar*)user_data; |
1863 | |
1864 | if( trackbar && trackbar->signature == CV_TRACKBAR_MAGIC_VAL && |
1865 | trackbar->widget == widget ) |
1866 | { |
1867 | trackbar->pos = pos; |
1868 | if (trackbar->onChangeCallback) |
1869 | trackbar->onChangeCallback(pos, trackbar->userdata); |
1870 | |
1871 | // deprecated |
1872 | if( trackbar->data ) |
1873 | *trackbar->data = pos; |
1874 | if( trackbar->notify2 ) |
1875 | trackbar->notify2(pos, trackbar->userdata); |
1876 | else if( trackbar->notify ) |
1877 | trackbar->notify(pos); |
1878 | } |
1879 | } |
1880 | |
1881 | static gboolean icvOnClose( GtkWidget* widget, GdkEvent* /*event*/, gpointer user_data ) |
1882 | { |
1883 | CvWindow* window = (CvWindow*)user_data; |
1884 | if( window->signature == CV_WINDOW_MAGIC_VAL && |
1885 | window->frame == widget ) |
1886 | { |
1887 | try |
1888 | { |
1889 | icvDeleteWindow_(window); |
1890 | } |
1891 | catch (...) |
1892 | { |
1893 | CV_LOG_WARNING(NULL, "OpenCV/GTK: unexpected C++ exception in icvDeleteWindow_"); |
1894 | } |
1895 | } |
1896 | return TRUE; |
1897 | } |
1898 | |
1899 | |
1900 | static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data ) |
1901 | { |
1902 | // TODO move this logic to CvImageWidget |
1903 | // TODO add try-catch wrappers into all callbacks |
1904 | CvWindow* window = (CvWindow*)user_data; |
1905 | if (!window || !widget || |
1906 | window->signature != CV_WINDOW_MAGIC_VAL || |
1907 | window->widget != widget || |
1908 | !window->on_mouse) |
1909 | return FALSE; |
1910 | |
1911 | CvPoint2D32f pt32f = {.x: -1., .y: -1.}; |
1912 | CvPoint pt = {.x: -1,.y: -1}; |
1913 | int cv_event = -1, state = 0, flags = 0; |
1914 | CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget ); |
1915 | |
1916 | if( event->type == GDK_MOTION_NOTIFY ) |
1917 | { |
1918 | GdkEventMotion* event_motion = (GdkEventMotion*)event; |
1919 | |
1920 | cv_event = CV_EVENT_MOUSEMOVE; |
1921 | pt32f.x = cvFloor(value: event_motion->x); |
1922 | pt32f.y = cvFloor(value: event_motion->y); |
1923 | state = event_motion->state; |
1924 | } |
1925 | else if( event->type == GDK_BUTTON_PRESS || |
1926 | event->type == GDK_BUTTON_RELEASE || |
1927 | event->type == GDK_2BUTTON_PRESS ) |
1928 | { |
1929 | GdkEventButton* event_button = (GdkEventButton*)event; |
1930 | pt32f.x = cvFloor(value: event_button->x); |
1931 | pt32f.y = cvFloor(value: event_button->y); |
1932 | |
1933 | |
1934 | if( event_button->type == GDK_BUTTON_PRESS ) |
1935 | { |
1936 | cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN : |
1937 | event_button->button == 2 ? CV_EVENT_MBUTTONDOWN : |
1938 | event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0; |
1939 | } |
1940 | else if( event_button->type == GDK_BUTTON_RELEASE ) |
1941 | { |
1942 | cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP : |
1943 | event_button->button == 2 ? CV_EVENT_MBUTTONUP : |
1944 | event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0; |
1945 | } |
1946 | else if( event_button->type == GDK_2BUTTON_PRESS ) |
1947 | { |
1948 | cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK : |
1949 | event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK : |
1950 | event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0; |
1951 | } |
1952 | state = event_button->state; |
1953 | } |
1954 | else if( event->type == GDK_SCROLL ) |
1955 | { |
1956 | GdkEventScroll* event_scroll = (GdkEventScroll*)event; |
1957 | pt32f.x = cvFloor(value: event_scroll->x); |
1958 | pt32f.y = cvFloor(value: event_scroll->y); |
1959 | |
1960 | #if defined(GTK_VERSION3_4) |
1961 | // NOTE: in current implementation doesn't possible to put into callback function delta_x and delta_y separately |
1962 | double delta = (event->scroll.delta_x + event->scroll.delta_y); |
1963 | cv_event = (event->scroll.delta_x==0) ? CV_EVENT_MOUSEWHEEL : CV_EVENT_MOUSEHWHEEL; |
1964 | #else |
1965 | cv_event = CV_EVENT_MOUSEWHEEL; |
1966 | #endif //GTK_VERSION3_4 |
1967 | |
1968 | state = event->scroll.state; |
1969 | |
1970 | switch(event->scroll.direction) { |
1971 | #if defined(GTK_VERSION3_4) |
1972 | case GDK_SCROLL_SMOOTH: flags |= (((int)delta << 16)); |
1973 | break; |
1974 | #endif //GTK_VERSION3_4 |
1975 | case GDK_SCROLL_LEFT: cv_event = CV_EVENT_MOUSEHWHEEL; |
1976 | /* FALLTHRU */ |
1977 | case GDK_SCROLL_UP: flags |= ~0xffff; |
1978 | break; |
1979 | case GDK_SCROLL_RIGHT: cv_event = CV_EVENT_MOUSEHWHEEL; |
1980 | /* FALLTHRU */ |
1981 | case GDK_SCROLL_DOWN: flags |= (((int)1 << 16)); |
1982 | break; |
1983 | default: ; |
1984 | }; |
1985 | } |
1986 | |
1987 | if( cv_event >= 0 ) |
1988 | { |
1989 | // scale point if image is scaled |
1990 | if( (image_widget->flags & cv::WINDOW_AUTOSIZE)==0 && |
1991 | image_widget->original_image && |
1992 | image_widget->scaled_image ) |
1993 | { |
1994 | // image origin is not necessarily at (0,0) |
1995 | int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2; |
1996 | int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2; |
1997 | pt.x = cvFloor( value: ((pt32f.x-x0)*image_widget->original_image->cols)/ |
1998 | image_widget->scaled_image->cols ); |
1999 | pt.y = cvFloor( value: ((pt32f.y-y0)*image_widget->original_image->rows)/ |
2000 | image_widget->scaled_image->rows ); |
2001 | } |
2002 | else |
2003 | { |
2004 | pt = cvPointFrom32f( point: pt32f ); |
2005 | } |
2006 | |
2007 | if (!image_widget->original_image/*OpenGL*/ || ( |
2008 | (unsigned)pt.x < (unsigned)(image_widget->original_image->width) && |
2009 | (unsigned)pt.y < (unsigned)(image_widget->original_image->height) |
2010 | )) |
2011 | { |
2012 | // handle non-keyboard (mouse) modifiers first |
2013 | flags |= |
2014 | BIT_MAP(state, GDK_BUTTON1_MASK, CV_EVENT_FLAG_LBUTTON) | |
2015 | BIT_MAP(state, GDK_BUTTON2_MASK, CV_EVENT_FLAG_MBUTTON) | |
2016 | BIT_MAP(state, GDK_BUTTON3_MASK, CV_EVENT_FLAG_RBUTTON); |
2017 | // keyboard modifiers |
2018 | state &= gtk_accelerator_get_default_mod_mask(); |
2019 | flags |= |
2020 | BIT_MAP(state, GDK_SHIFT_MASK, CV_EVENT_FLAG_SHIFTKEY) | |
2021 | BIT_MAP(state, GDK_CONTROL_MASK, CV_EVENT_FLAG_CTRLKEY) | |
2022 | BIT_MAP(state, GDK_MOD1_MASK, CV_EVENT_FLAG_ALTKEY) | |
2023 | BIT_MAP(state, GDK_MOD2_MASK, CV_EVENT_FLAG_ALTKEY); |
2024 | window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param ); |
2025 | } |
2026 | } |
2027 | |
2028 | return FALSE; |
2029 | } |
2030 | |
2031 | |
2032 | static gboolean icvAlarm( gpointer user_data ) |
2033 | { |
2034 | *(int*)user_data = 1; |
2035 | return FALSE; |
2036 | } |
2037 | |
2038 | |
2039 | CV_IMPL int cvWaitKey( int delay ) |
2040 | { |
2041 | if (thread_started && g_thread_self() != window_thread) |
2042 | { |
2043 | gboolean expired = true; |
2044 | int my_last_key; |
2045 | |
2046 | g_mutex_lock(mutex: last_key_mutex); |
2047 | // wait for signal or timeout if delay > 0 |
2048 | if(delay>0){ |
2049 | GTimeVal timer; |
2050 | g_get_current_time(result: &timer); |
2051 | g_time_val_add(time_: &timer, microseconds: delay*1000); |
2052 | expired = !g_cond_timed_wait(cond: cond_have_key, mutex: last_key_mutex, timeval: &timer); |
2053 | } |
2054 | else{ |
2055 | if (getGTKWindows().empty()) |
2056 | { |
2057 | CV_LOG_WARNING(NULL, "cv::waitKey() is called without timeout and missing active windows. Ignoring"); |
2058 | } |
2059 | else |
2060 | { |
2061 | g_cond_wait(cond: cond_have_key, mutex: last_key_mutex); |
2062 | expired=false; |
2063 | } |
2064 | } |
2065 | my_last_key = last_key; |
2066 | g_mutex_unlock(mutex: last_key_mutex); |
2067 | if (expired || getGTKWindows().empty()) |
2068 | { |
2069 | return -1; |
2070 | } |
2071 | return my_last_key; |
2072 | } |
2073 | else |
2074 | { |
2075 | int expired = 0; |
2076 | guint timer = 0; |
2077 | if( delay > 0 ) |
2078 | timer = g_timeout_add( interval: delay, function: icvAlarm, data: &expired ); |
2079 | last_key = -1; |
2080 | while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && (delay > 0 || !getGTKWindows().empty())) |
2081 | ; |
2082 | |
2083 | if( delay > 0 && !expired ) |
2084 | g_source_remove(tag: timer); |
2085 | } |
2086 | return last_key; |
2087 | } |
2088 | |
2089 | namespace cv { namespace impl { |
2090 | |
2091 | using namespace cv::highgui_backend; |
2092 | |
2093 | class GTKTrackbar; |
2094 | |
2095 | class GTKWindow |
2096 | : public UIWindow |
2097 | , public std::enable_shared_from_this<GTKWindow> |
2098 | { |
2099 | protected: |
2100 | const std::string name_; |
2101 | std::weak_ptr<CvWindow> window_; |
2102 | std::map<std::string, std::shared_ptr<GTKTrackbar> > trackbars_; |
2103 | public: |
2104 | GTKWindow(const std::string& name, const std::shared_ptr<CvWindow>& window) |
2105 | : name_(name) |
2106 | , window_(window) |
2107 | { |
2108 | // nothing |
2109 | } |
2110 | |
2111 | ~GTKWindow() CV_OVERRIDE |
2112 | { |
2113 | if (!window_.expired()) |
2114 | destroy(); |
2115 | CV_LOG_DEBUG(NULL, "OpenCV/UI/GTK: GTKWindow("<< name_ << ") is disposed"); |
2116 | } |
2117 | |
2118 | const std::string& getID() const CV_OVERRIDE { return name_; } |
2119 | |
2120 | bool isActive() const CV_OVERRIDE { return !window_.expired(); } |
2121 | |
2122 | void destroy() CV_OVERRIDE |
2123 | { |
2124 | cv::AutoLock lock(getWindowMutex()); |
2125 | if (!window_.expired()) |
2126 | { |
2127 | auto window = window_.lock(); |
2128 | if (window) |
2129 | window->destroy(); |
2130 | window_.reset(); |
2131 | } |
2132 | } |
2133 | |
2134 | void imshow(InputArray image) CV_OVERRIDE |
2135 | { |
2136 | auto window = window_.lock(); |
2137 | CV_Assert(window); |
2138 | CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget); |
2139 | CV_Assert(image_widget); |
2140 | Mat img = image.getMat(); |
2141 | CvMat c_img = cvMat(m: img); // TODO Drop C-API |
2142 | cvImageWidgetSetImage(widget: image_widget, arr: &c_img); |
2143 | } |
2144 | |
2145 | double getProperty(int prop) const CV_OVERRIDE |
2146 | { |
2147 | auto window = window_.lock(); |
2148 | CV_Assert(window); |
2149 | // see cvGetWindowProperty |
2150 | switch (prop) |
2151 | { |
2152 | case cv::WND_PROP_FULLSCREEN: |
2153 | return (double)window->status; |
2154 | |
2155 | case cv::WND_PROP_AUTOSIZE: |
2156 | return (window->flags & cv::WINDOW_AUTOSIZE) ? 1.0 : 0.0; |
2157 | |
2158 | case cv::WND_PROP_ASPECT_RATIO: |
2159 | return getRatioWindow_(window); |
2160 | |
2161 | #ifdef HAVE_OPENGL |
2162 | case cv::WND_PROP_OPENGL: |
2163 | return window->useGl ? 1.0 : 0.0; |
2164 | #endif |
2165 | |
2166 | default: |
2167 | break; |
2168 | } |
2169 | return std::numeric_limits<double>::quiet_NaN(); |
2170 | } |
2171 | |
2172 | bool setProperty(int prop, double value) CV_OVERRIDE |
2173 | { |
2174 | auto window = window_.lock(); |
2175 | CV_Assert(window); |
2176 | // see cvSetWindowProperty |
2177 | switch (prop) |
2178 | { |
2179 | case cv::WND_PROP_FULLSCREEN: |
2180 | if ((int)value != cv::WINDOW_NORMAL && (int)value != cv::WINDOW_FULLSCREEN) // bad arg |
2181 | break; |
2182 | setModeWindow_(window, mode: value); |
2183 | return true; |
2184 | |
2185 | default: |
2186 | break; |
2187 | } |
2188 | return false; |
2189 | } |
2190 | |
2191 | void resize(int width, int height) CV_OVERRIDE |
2192 | { |
2193 | auto window = window_.lock(); |
2194 | CV_Assert(window); |
2195 | resizeWindow_(window, width, height); |
2196 | } |
2197 | |
2198 | void move(int x, int y) CV_OVERRIDE |
2199 | { |
2200 | auto window = window_.lock(); |
2201 | CV_Assert(window); |
2202 | gtk_window_move(GTK_WINDOW(window->frame), x, y); |
2203 | } |
2204 | |
2205 | Rect getImageRect() const CV_OVERRIDE |
2206 | { |
2207 | auto window = window_.lock(); |
2208 | CV_Assert(window); |
2209 | return getImageRect_(window); |
2210 | } |
2211 | |
2212 | void setTitle(const std::string& title) CV_OVERRIDE |
2213 | { |
2214 | auto window = window_.lock(); |
2215 | CV_Assert(window); |
2216 | gtk_window_set_title(GTK_WINDOW(window->frame), title: title.c_str()); |
2217 | } |
2218 | |
2219 | void setMouseCallback(MouseCallback onMouse, void* userdata /*= 0*/) CV_OVERRIDE |
2220 | { |
2221 | auto window = window_.lock(); |
2222 | CV_Assert(window); |
2223 | window->on_mouse = onMouse; |
2224 | window->on_mouse_param = userdata; |
2225 | } |
2226 | |
2227 | std::shared_ptr<UITrackbar> createTrackbar( |
2228 | const std::string& name, |
2229 | int count, |
2230 | TrackbarCallback onChange /*= 0*/, |
2231 | void* userdata /*= 0*/ |
2232 | ) CV_OVERRIDE |
2233 | { |
2234 | auto window = window_.lock(); |
2235 | CV_Assert(window); |
2236 | CV_LOG_INFO(NULL, "OpenCV/UI: Creating GTK trackbar at '"<< name_ << "': '"<< name << "'"); |
2237 | auto trackbar = createTrackbar_(window, name, count, onChange, userdata); |
2238 | auto ui_trackbar = std::make_shared<GTKTrackbar>(args: name, args&: trackbar, args: shared_from_this()); |
2239 | { |
2240 | cv::AutoLock lock(getWindowMutex()); |
2241 | trackbars_.emplace(args: name, args&: ui_trackbar); |
2242 | } |
2243 | return std::static_pointer_cast<UITrackbar>(r: ui_trackbar); |
2244 | } |
2245 | |
2246 | std::shared_ptr<UITrackbar> findTrackbar(const std::string& name) CV_OVERRIDE |
2247 | { |
2248 | cv::AutoLock lock(getWindowMutex()); |
2249 | auto i = trackbars_.find(x: name); |
2250 | if (i != trackbars_.end()) |
2251 | { |
2252 | return std::static_pointer_cast<UITrackbar>(r: i->second); |
2253 | } |
2254 | return std::shared_ptr<UITrackbar>(); |
2255 | } |
2256 | }; // GTKWindow |
2257 | |
2258 | |
2259 | class GTKTrackbar : public UITrackbar |
2260 | { |
2261 | protected: |
2262 | /*const*/ std::string name_; |
2263 | std::weak_ptr<CvTrackbar> trackbar_; |
2264 | std::weak_ptr<GTKWindow> parent_; |
2265 | std::map<std::string, std::shared_ptr<GTKTrackbar> > trackbars_; |
2266 | public: |
2267 | GTKTrackbar(const std::string& name, const std::shared_ptr<CvTrackbar>& trackbar, const std::shared_ptr<GTKWindow>& parent) |
2268 | : trackbar_(trackbar) |
2269 | , parent_(parent) |
2270 | { |
2271 | name_ = std::string("<") + name + ">@"+ parent->getID(); |
2272 | } |
2273 | |
2274 | ~GTKTrackbar() CV_OVERRIDE |
2275 | { |
2276 | if (!trackbar_.expired()) |
2277 | destroy(); |
2278 | CV_LOG_DEBUG(NULL, "OpenCV/UI/GTK: GTKTrackbar("<< name_ << ") is disposed"); |
2279 | } |
2280 | |
2281 | const std::string& getID() const CV_OVERRIDE { return name_; } |
2282 | |
2283 | bool isActive() const CV_OVERRIDE { return !trackbar_.expired(); } |
2284 | |
2285 | void destroy() CV_OVERRIDE |
2286 | { |
2287 | // nothing (destroyed with parent window, dedicated trackbar removal is not supported) |
2288 | } |
2289 | |
2290 | int getPos() const CV_OVERRIDE |
2291 | { |
2292 | auto trackbar = trackbar_.lock(); |
2293 | CV_Assert(trackbar); |
2294 | return trackbar->pos; |
2295 | } |
2296 | void setPos(int pos) CV_OVERRIDE |
2297 | { |
2298 | auto trackbar = trackbar_.lock(); |
2299 | CV_Assert(trackbar); |
2300 | return setTrackbarPos_(trackbar, pos); |
2301 | } |
2302 | |
2303 | cv::Range getRange() const CV_OVERRIDE |
2304 | { |
2305 | auto trackbar = trackbar_.lock(); |
2306 | CV_Assert(trackbar); |
2307 | return cv::Range(trackbar->minval, trackbar->maxval); |
2308 | } |
2309 | |
2310 | void setRange(const cv::Range& range) CV_OVERRIDE |
2311 | { |
2312 | auto trackbar = trackbar_.lock(); |
2313 | CV_Assert(trackbar); |
2314 | CV_CheckLE(range.start, range.end, "Invalid trackbar range"); |
2315 | gtk_range_set_range(GTK_RANGE(trackbar->widget), min: range.start, max: range.end); |
2316 | } |
2317 | }; // GTKTrackbar |
2318 | |
2319 | |
2320 | class GTKBackendUI : public UIBackend |
2321 | { |
2322 | public: |
2323 | GTKBackendUI() |
2324 | { |
2325 | // NB: avoid static initialization order fiasco |
2326 | (void)getGTKWindows(); |
2327 | } |
2328 | ~GTKBackendUI() CV_OVERRIDE |
2329 | { |
2330 | destroyAllWindows(); |
2331 | } |
2332 | |
2333 | void destroyAllWindows() CV_OVERRIDE |
2334 | { |
2335 | cvDestroyAllWindows(); |
2336 | } |
2337 | |
2338 | // namedWindow |
2339 | virtual std::shared_ptr<UIWindow> createWindow( |
2340 | const std::string& winname, |
2341 | int flags |
2342 | ) CV_OVERRIDE |
2343 | { |
2344 | CV_LOG_INFO(NULL, "OpenCV/UI: Creating GTK window: "<< winname << " ("<< flags << ")"); |
2345 | auto window = namedWindow_(name: winname, flags); |
2346 | auto ui_window = std::make_shared<GTKWindow>(args: winname, args&: window); |
2347 | return ui_window; |
2348 | } |
2349 | |
2350 | int waitKeyEx(int delay) CV_OVERRIDE |
2351 | { |
2352 | return cvWaitKey(delay); |
2353 | } |
2354 | int pollKey() CV_OVERRIDE |
2355 | { |
2356 | return cvWaitKey(delay: 1); // TODO |
2357 | } |
2358 | |
2359 | const std::string getName() const CV_OVERRIDE |
2360 | { |
2361 | #if GTK_MAJOR_VERSION == 2 |
2362 | return "GTK2"; |
2363 | #elif GTK_MAJOR_VERSION == 3 |
2364 | return "GTK3"; |
2365 | #elif GTK_MAJOR_VERSION == 4 |
2366 | return "GTK4"; |
2367 | #else |
2368 | #error "Unsupported GTK version" |
2369 | #endif |
2370 | } |
2371 | }; // GTKBackendUI |
2372 | |
2373 | static |
2374 | std::shared_ptr<GTKBackendUI>& getInstance() |
2375 | { |
2376 | static std::shared_ptr<GTKBackendUI> g_instance = std::make_shared<GTKBackendUI>(); |
2377 | return g_instance; |
2378 | } |
2379 | |
2380 | } // namespace impl |
2381 | |
2382 | #ifndef BUILD_PLUGIN |
2383 | namespace highgui_backend { |
2384 | |
2385 | std::shared_ptr<UIBackend> createUIBackendGTK() |
2386 | { |
2387 | return impl::getInstance(); |
2388 | } |
2389 | |
2390 | } // namespace highgui_backend |
2391 | #endif |
2392 | |
2393 | } // namespace |
2394 | |
2395 | #ifdef BUILD_PLUGIN |
2396 | |
2397 | #define ABI_VERSION 0 |
2398 | #define API_VERSION 0 |
2399 | #include "plugin_api.hpp" |
2400 | |
2401 | static |
2402 | CvResult cv_getInstance(CV_OUT CvPluginUIBackend* handle) CV_NOEXCEPT |
2403 | { |
2404 | try |
2405 | { |
2406 | if (!handle) |
2407 | return CV_ERROR_FAIL; |
2408 | *handle = cv::impl::getInstance().get(); |
2409 | return CV_ERROR_OK; |
2410 | } |
2411 | catch (...) |
2412 | { |
2413 | return CV_ERROR_FAIL; |
2414 | } |
2415 | } |
2416 | |
2417 | static const OpenCV_UI_Plugin_API plugin_api = |
2418 | { |
2419 | { |
2420 | sizeof(OpenCV_UI_Plugin_API), ABI_VERSION, API_VERSION, |
2421 | CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS, |
2422 | "GTK"CVAUX_STR(GTK_MAJOR_VERSION) " OpenCV UI plugin" |
2423 | }, |
2424 | { |
2425 | /* 1*/cv_getInstance |
2426 | } |
2427 | }; |
2428 | |
2429 | const OpenCV_UI_Plugin_API* CV_API_CALL opencv_ui_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT |
2430 | { |
2431 | if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION) |
2432 | return &plugin_api; |
2433 | return NULL; |
2434 | } |
2435 | |
2436 | #endif // BUILD_PLUGIN |
2437 | |
2438 | #endif // HAVE_GTK |
2439 |
Definitions
- _CvImageWidget
- _CvImageWidgetClass
- parent_class
- cvImageWidgetSetImage
- cvImageWidgetNew
- cvImageWidget_realize
- cvImageWidget_calc_size
- cvImageWidget_get_preferred_width
- cvImageWidget_get_preferred_height
- cvImageWidget_set_size
- cvImageWidget_size_allocate
- cvImageWidget_destroy
- cvImageWidget_class_init
- cvImageWidget_init
- cvImageWidget_get_type
- CvUIBase
- CvUIBase
- CvTrackbar
- CvTrackbar
- ~CvTrackbar
- CvWindow
- CvWindow
- thread_started
- last_key_mutex
- cond_have_key
- window_thread
- last_key
- getGTKWindows
- cvInitSystem
- cvStartWindowThread
- icvWindowThreadLoop
- icvFindWindowByName
- icvFindWindowByName
- icvWindowByWidget
- cvGetWindowRect_GTK
- getImageRect_
- cvGetModeWindow_GTK
- cvSetModeWindow_GTK
- setModeWindow_
- setWindowTitle_GTK
- cvGetPropWindowAutoSize_GTK
- cvGetRatioWindow_GTK
- getRatioWindow_
- cvGetOpenGlProp_GTK
- cvImageWidget_draw
- cvNamedWindow
- namedWindow_
- ~CvWindow
- destroy
- checkLastWindow
- icvDeleteWindow_
- cvDestroyWindow
- cvDestroyAllWindows
- cvShowImage
- cvResizeWindow
- resizeWindow_
- cvMoveWindow
- icvFindTrackbarByName
- icvCreateTrackbar
- cvCreateTrackbar
- cvCreateTrackbar2
- createTrackbar_
- cvSetMouseCallback
- cvGetTrackbarPos
- cvSetTrackbarPos
- setTrackbarPos_
- cvSetTrackbarMax
- cvSetTrackbarMin
- cvGetWindowHandle
- cvGetWindowName
- icvMakeGtkFilter
- icvShowSaveAsDialog
- icvOnKeyPress
- icvOnTrackbar
- icvOnClose
- icvOnMouse
- icvAlarm
- cvWaitKey
- GTKWindow
- GTKWindow
- ~GTKWindow
- getID
- isActive
- destroy
- imshow
- getProperty
- setProperty
- resize
- move
- getImageRect
- setTitle
- setMouseCallback
- createTrackbar
- findTrackbar
- GTKTrackbar
- GTKTrackbar
- ~GTKTrackbar
- getID
- isActive
- destroy
- getPos
- setPos
- getRange
- setRange
- GTKBackendUI
- GTKBackendUI
- ~GTKBackendUI
- destroyAllWindows
- createWindow
- waitKeyEx
- pollKey
- getName
- getInstance
Improve your Profiling and Debugging skills
Find out more