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