Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/highgui/src/window_gtk.cpp
16337 views
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
44
#ifndef _WIN32
45
46
#if defined (HAVE_GTK)
47
48
#include <gtk/gtk.h>
49
#include <gdk/gdkkeysyms.h>
50
#include <gdk-pixbuf/gdk-pixbuf.h>
51
#include <stdio.h>
52
53
#if (GTK_MAJOR_VERSION == 3)
54
#define GTK_VERSION3 1
55
#endif //GTK_MAJOR_VERSION >= 3
56
#if (GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4))
57
#define GTK_VERSION3_4 1
58
#endif
59
60
#ifdef HAVE_OPENGL
61
#include <gtk/gtkgl.h>
62
#include <GL/gl.h>
63
#include <GL/glu.h>
64
#endif
65
66
#include <opencv2/core/utils/logger.hpp>
67
#include "opencv2/imgproc.hpp"
68
69
using namespace cv;
70
71
#ifndef BIT_ALLIN
72
#define BIT_ALLIN(x,y) ( ((x)&(y)) == (y) )
73
#endif
74
#ifndef BIT_MAP
75
#define BIT_MAP(x,y,z) ( ((x)&(y)) ? (z) : 0 )
76
#endif
77
78
// TODO Fix the initial window size when flags=0. Right now the initial window is by default
79
// 320x240 size. A better default would be actual size of the image. Problem
80
// is determining desired window size with trackbars while still allowing resizing.
81
//
82
// Gnome Totem source may be of use here, see bacon_video_widget_set_scale_ratio
83
// in totem/src/backend/bacon-video-widget-xine.c
84
85
////////////////////////////////////////////////////////////
86
// CvImageWidget GTK Widget Public API
87
////////////////////////////////////////////////////////////
88
typedef struct _CvImageWidget CvImageWidget;
89
typedef struct _CvImageWidgetClass CvImageWidgetClass;
90
91
struct _CvImageWidget {
92
GtkWidget widget;
93
CvMat * original_image;
94
CvMat * scaled_image;
95
int flags;
96
};
97
98
struct _CvImageWidgetClass
99
{
100
GtkWidgetClass parent_class;
101
};
102
103
104
/** Allocate new image viewer widget */
105
GtkWidget* cvImageWidgetNew (int flags);
106
107
/** Set the image to display in the widget */
108
void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr);
109
110
// standard GTK object macros
111
#define CV_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
112
#define CV_IMAGE_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
113
#define CV_IS_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, cvImageWidget_get_type ())
114
115
/////////////////////////////////////////////////////////////////////////////
116
// Private API ////////////////////////////////////////////////////////
117
/////////////////////////////////////////////////////////////////////////////
118
GType cvImageWidget_get_type (void);
119
120
static GtkWidgetClass * parent_class = NULL;
121
122
// flag to help size initial window
123
#define CV_WINDOW_NO_IMAGE 2
124
125
void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr){
126
CvMat * mat, stub;
127
int origin=0;
128
129
//printf("cvImageWidgetSetImage\n");
130
131
if( CV_IS_IMAGE_HDR( arr ))
132
origin = ((IplImage*)arr)->origin;
133
134
mat = cvGetMat(arr, &stub);
135
136
if(widget->original_image && !CV_ARE_SIZES_EQ(mat, widget->original_image)){
137
cvReleaseMat( &widget->original_image );
138
}
139
if(!widget->original_image){
140
widget->original_image = cvCreateMat( mat->rows, mat->cols, CV_8UC3 );
141
gtk_widget_queue_resize( GTK_WIDGET( widget ) );
142
}
143
cvConvertImage( mat, widget->original_image,
144
(origin != 0 ? CV_CVTIMG_FLIP : 0) + CV_CVTIMG_SWAP_RB );
145
if(widget->scaled_image){
146
cvResize( widget->original_image, widget->scaled_image, CV_INTER_AREA );
147
}
148
149
// window does not refresh without this
150
gtk_widget_queue_draw( GTK_WIDGET(widget) );
151
}
152
153
GtkWidget*
154
cvImageWidgetNew (int flags)
155
{
156
CvImageWidget *image_widget;
157
158
image_widget = CV_IMAGE_WIDGET( gtk_widget_new (cvImageWidget_get_type (), NULL) );
159
image_widget->original_image = 0;
160
image_widget->scaled_image = 0;
161
image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
162
163
return GTK_WIDGET (image_widget);
164
}
165
166
static void
167
cvImageWidget_realize (GtkWidget *widget)
168
{
169
GdkWindowAttr attributes;
170
gint attributes_mask;
171
172
#if defined(GTK_VERSION3)
173
GtkAllocation allocation;
174
gtk_widget_get_allocation(widget, &allocation);
175
#endif //GTK_VERSION3
176
177
//printf("cvImageWidget_realize\n");
178
g_return_if_fail (widget != NULL);
179
g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
180
181
gtk_widget_set_realized(widget, TRUE);
182
183
#if defined(GTK_VERSION3)
184
attributes.x = allocation.x;
185
attributes.y = allocation.y;
186
attributes.width = allocation.width;
187
attributes.height = allocation.height;
188
#else
189
attributes.x = widget->allocation.x;
190
attributes.y = widget->allocation.y;
191
attributes.width = widget->allocation.width;
192
attributes.height = widget->allocation.height;
193
#endif //GTK_VERSION3
194
195
attributes.wclass = GDK_INPUT_OUTPUT;
196
attributes.window_type = GDK_WINDOW_CHILD;
197
attributes.event_mask = gtk_widget_get_events (widget) |
198
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
199
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
200
attributes.visual = gtk_widget_get_visual (widget);
201
202
#if defined(GTK_VERSION3)
203
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
204
gtk_widget_set_window(
205
widget,
206
gdk_window_new(
207
gtk_widget_get_parent_window(widget),
208
&attributes,
209
attributes_mask
210
)
211
);
212
213
gtk_widget_set_style(
214
widget,
215
gtk_style_attach(
216
gtk_widget_get_style(widget),
217
gtk_widget_get_window(widget)
218
)
219
);
220
221
gdk_window_set_user_data (
222
gtk_widget_get_window(widget),
223
widget
224
);
225
226
gtk_style_set_background (
227
gtk_widget_get_style(widget),
228
gtk_widget_get_window(widget),
229
GTK_STATE_ACTIVE
230
);
231
#else
232
// The following lines are included to prevent breaking
233
// compatibility with older Gtk2 (<gtk+-2.18) libraries.
234
attributes.colormap = gtk_widget_get_colormap (widget);
235
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
236
widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
237
238
widget->style = gtk_style_attach (widget->style, widget->window);
239
gdk_window_set_user_data (widget->window, widget);
240
241
gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
242
#endif // GTK_VERSION3
243
}
244
245
static CvSize cvImageWidget_calc_size( int im_width, int im_height, int max_width, int max_height ){
246
float aspect = (float)im_width/(float)im_height;
247
float max_aspect = (float)max_width/(float)max_height;
248
if(aspect > max_aspect){
249
return cvSize( max_width, cvRound(max_width/aspect) );
250
}
251
return cvSize( cvRound(max_height*aspect), max_height );
252
}
253
254
#if defined (GTK_VERSION3)
255
static void
256
cvImageWidget_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width)
257
{
258
g_return_if_fail (widget != NULL);
259
g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
260
CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
261
262
if(image_widget->original_image != NULL) {
263
*minimal_width = (image_widget->flags & CV_WINDOW_AUTOSIZE) != CV_WINDOW_AUTOSIZE ?
264
gdk_window_get_width(gtk_widget_get_window(widget)) : image_widget->original_image->cols;
265
}
266
else {
267
*minimal_width = 320;
268
}
269
270
if(image_widget->scaled_image != NULL) {
271
*natural_width = *minimal_width < image_widget->scaled_image->cols ?
272
image_widget->scaled_image->cols : *minimal_width;
273
}
274
else {
275
*natural_width = *minimal_width;
276
}
277
}
278
279
static void
280
cvImageWidget_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height)
281
{
282
g_return_if_fail (widget != NULL);
283
g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
284
CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
285
286
if(image_widget->original_image != NULL) {
287
*minimal_height = (image_widget->flags & CV_WINDOW_AUTOSIZE) != CV_WINDOW_AUTOSIZE ?
288
gdk_window_get_height(gtk_widget_get_window(widget)) : image_widget->original_image->rows;
289
}
290
else {
291
*minimal_height = 240;
292
}
293
294
if(image_widget->scaled_image != NULL) {
295
*natural_height = *minimal_height < image_widget->scaled_image->rows ?
296
image_widget->scaled_image->rows : *minimal_height;
297
}
298
else {
299
*natural_height = *minimal_height;
300
}
301
}
302
303
#else
304
static void
305
cvImageWidget_size_request (GtkWidget *widget,
306
GtkRequisition *requisition)
307
{
308
CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
309
310
//printf("cvImageWidget_size_request ");
311
// the case the first time cvShowImage called or when AUTOSIZE
312
if( image_widget->original_image &&
313
((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
314
(image_widget->flags & CV_WINDOW_NO_IMAGE)))
315
{
316
//printf("original ");
317
requisition->width = image_widget->original_image->cols;
318
requisition->height = image_widget->original_image->rows;
319
}
320
// default case
321
else if(image_widget->scaled_image){
322
//printf("scaled ");
323
requisition->width = image_widget->scaled_image->cols;
324
requisition->height = image_widget->scaled_image->rows;
325
}
326
// the case before cvShowImage called
327
else{
328
//printf("default ");
329
requisition->width = 320;
330
requisition->height = 240;
331
}
332
//printf("%d %d\n",requisition->width, requisition->height);
333
}
334
#endif //GTK_VERSION3
335
336
static void cvImageWidget_set_size(GtkWidget * widget, int max_width, int max_height){
337
CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
338
339
//printf("cvImageWidget_set_size %d %d\n", max_width, max_height);
340
341
// don't allow to set the size
342
if(image_widget->flags & CV_WINDOW_AUTOSIZE) return;
343
if(!image_widget->original_image) return;
344
345
CvSize scaled_image_size = cvImageWidget_calc_size( image_widget->original_image->cols,
346
image_widget->original_image->rows, max_width, max_height );
347
348
if( image_widget->scaled_image &&
349
( image_widget->scaled_image->cols != scaled_image_size.width ||
350
image_widget->scaled_image->rows != scaled_image_size.height ))
351
{
352
cvReleaseMat( &image_widget->scaled_image );
353
}
354
if( !image_widget->scaled_image ){
355
image_widget->scaled_image = cvCreateMat( scaled_image_size.height, scaled_image_size.width, CV_8UC3 );
356
357
358
}
359
assert( image_widget->scaled_image );
360
}
361
362
static void
363
cvImageWidget_size_allocate (GtkWidget *widget,
364
GtkAllocation *allocation)
365
{
366
CvImageWidget *image_widget;
367
368
//printf("cvImageWidget_size_allocate\n");
369
g_return_if_fail (widget != NULL);
370
g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
371
g_return_if_fail (allocation != NULL);
372
373
#if defined (GTK_VERSION3)
374
gtk_widget_set_allocation(widget, allocation);
375
#else
376
widget->allocation = *allocation;
377
#endif //GTK_VERSION3
378
image_widget = CV_IMAGE_WIDGET (widget);
379
380
381
if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 && image_widget->original_image ){
382
// (re) allocated scaled image
383
if( image_widget->flags & CV_WINDOW_NO_IMAGE ){
384
cvImageWidget_set_size( widget, image_widget->original_image->cols,
385
image_widget->original_image->rows);
386
}
387
else{
388
cvImageWidget_set_size( widget, allocation->width, allocation->height );
389
}
390
cvResize( image_widget->original_image, image_widget->scaled_image, CV_INTER_AREA );
391
}
392
393
if (gtk_widget_get_realized (widget))
394
{
395
image_widget = CV_IMAGE_WIDGET (widget);
396
397
if( image_widget->original_image &&
398
((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
399
(image_widget->flags & CV_WINDOW_NO_IMAGE)) )
400
{
401
#if defined (GTK_VERSION3)
402
allocation->width = image_widget->original_image->cols;
403
allocation->height = image_widget->original_image->rows;
404
gtk_widget_set_allocation(widget, allocation);
405
#else
406
widget->allocation.width = image_widget->original_image->cols;
407
widget->allocation.height = image_widget->original_image->rows;
408
#endif //GTK_VERSION3
409
gdk_window_move_resize( gtk_widget_get_window(widget),
410
allocation->x, allocation->y,
411
image_widget->original_image->cols, image_widget->original_image->rows );
412
if(image_widget->flags & CV_WINDOW_NO_IMAGE){
413
image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
414
gtk_widget_queue_resize( GTK_WIDGET(widget) );
415
}
416
}
417
else{
418
gdk_window_move_resize (gtk_widget_get_window(widget),
419
allocation->x, allocation->y,
420
allocation->width, allocation->height );
421
}
422
}
423
}
424
425
#if defined (GTK_VERSION3)
426
static void
427
cvImageWidget_destroy (GtkWidget *object)
428
#else
429
static void
430
cvImageWidget_destroy (GtkObject *object)
431
#endif //GTK_VERSION3
432
{
433
CvImageWidget *image_widget;
434
435
g_return_if_fail (object != NULL);
436
g_return_if_fail (CV_IS_IMAGE_WIDGET (object));
437
438
image_widget = CV_IMAGE_WIDGET (object);
439
440
cvReleaseMat( &image_widget->scaled_image );
441
cvReleaseMat( &image_widget->original_image );
442
443
#if defined (GTK_VERSION3)
444
if (GTK_WIDGET_CLASS (parent_class)->destroy)
445
(* GTK_WIDGET_CLASS (parent_class)->destroy) (object);
446
#else
447
if (GTK_OBJECT_CLASS (parent_class)->destroy)
448
(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
449
#endif //GTK_VERSION3
450
}
451
452
static void cvImageWidget_class_init (gpointer g_class, gpointer /*class_data*/)
453
{
454
CvImageWidgetClass* klass = (CvImageWidgetClass*)g_class;
455
#if defined (GTK_VERSION3)
456
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
457
#else
458
GtkObjectClass *object_class;
459
GtkWidgetClass *widget_class;
460
461
object_class = (GtkObjectClass*) klass;
462
widget_class = (GtkWidgetClass*) klass;
463
#endif //GTK_VERSION3
464
465
parent_class = GTK_WIDGET_CLASS( g_type_class_peek (gtk_widget_get_type ()) );
466
467
#if defined (GTK_VERSION3)
468
widget_class->destroy = cvImageWidget_destroy;
469
widget_class->get_preferred_width = cvImageWidget_get_preferred_width;
470
widget_class->get_preferred_height = cvImageWidget_get_preferred_height;
471
#else
472
object_class->destroy = cvImageWidget_destroy;
473
widget_class->size_request = cvImageWidget_size_request;
474
#endif //GTK_VERSION3
475
476
widget_class->realize = cvImageWidget_realize;
477
widget_class->size_allocate = cvImageWidget_size_allocate;
478
widget_class->button_press_event = NULL;
479
widget_class->button_release_event = NULL;
480
widget_class->motion_notify_event = NULL;
481
}
482
483
static void
484
cvImageWidget_init(GTypeInstance* instance, gpointer /*g_class*/)
485
{
486
CvImageWidget* image_widget = (CvImageWidget*)instance;
487
image_widget->original_image=0;
488
image_widget->scaled_image=0;
489
image_widget->flags=0;
490
}
491
492
GType cvImageWidget_get_type (void){
493
static GType image_type = 0;
494
495
if (!image_type)
496
{
497
image_type = g_type_register_static_simple(
498
GTK_TYPE_WIDGET,
499
(gchar*) "CvImageWidget",
500
sizeof(CvImageWidgetClass),
501
cvImageWidget_class_init,
502
sizeof(CvImageWidget),
503
cvImageWidget_init,
504
(GTypeFlags)0
505
);
506
}
507
508
return image_type;
509
}
510
/////////////////////////////////////////////////////////////////////////////
511
// End CvImageWidget
512
/////////////////////////////////////////////////////////////////////////////
513
514
515
struct CvWindow;
516
517
struct CvUIBase {
518
CvUIBase(int signature_) : signature(signature_) { }
519
520
int signature;
521
};
522
523
struct CvTrackbar : CvUIBase
524
{
525
CvTrackbar(const char* trackbar_name) :
526
CvUIBase(CV_TRACKBAR_MAGIC_VAL),
527
widget(NULL), name(trackbar_name),
528
parent(NULL), data(NULL),
529
pos(0), maxval(0), minval(0),
530
notify(NULL), notify2(NULL), userdata(NULL)
531
{
532
// nothing
533
}
534
~CvTrackbar()
535
{
536
// destroyed by parent window
537
}
538
539
GtkWidget* widget;
540
std::string name;
541
CvWindow* parent;
542
int* data;
543
int pos;
544
int maxval;
545
int minval;
546
CvTrackbarCallback notify;
547
CvTrackbarCallback2 notify2;
548
void* userdata;
549
};
550
551
552
struct CvWindow : CvUIBase
553
{
554
CvWindow(const char* window_name) :
555
CvUIBase(CV_WINDOW_MAGIC_VAL),
556
widget(NULL), frame(NULL), paned(NULL), name(window_name),
557
last_key(0), flags(0), status(0),
558
on_mouse(NULL), on_mouse_param(NULL)
559
#ifdef HAVE_OPENGL
560
,useGl(false), glDrawCallback(NULL), glDrawData(NULL)
561
#endif
562
{
563
// nothing
564
}
565
~CvWindow();
566
567
GtkWidget* widget;
568
GtkWidget* frame;
569
GtkWidget* paned;
570
std::string name;
571
572
int last_key;
573
int flags;
574
int status;//0 normal, 1 fullscreen (YV)
575
576
CvMouseCallback on_mouse;
577
void* on_mouse_param;
578
579
std::vector< Ptr<CvTrackbar> > trackbars;
580
581
#ifdef HAVE_OPENGL
582
bool useGl;
583
584
CvOpenGlDrawCallback glDrawCallback;
585
void* glDrawData;
586
#endif
587
};
588
589
590
static gboolean icvOnClose( GtkWidget* widget, GdkEvent* event, gpointer user_data );
591
static gboolean icvOnKeyPress( GtkWidget* widget, GdkEventKey* event, gpointer user_data );
592
static void icvOnTrackbar( GtkWidget* widget, gpointer user_data );
593
static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data );
594
595
#ifdef HAVE_GTHREAD
596
int thread_started=0;
597
static gpointer icvWindowThreadLoop(gpointer data);
598
GMutex* last_key_mutex = NULL;
599
GCond* cond_have_key = NULL;
600
GThread* window_thread = NULL;
601
#endif
602
603
static cv::Mutex& getWindowMutex()
604
{
605
static cv::Mutex* g_window_mutex = new cv::Mutex();
606
return *g_window_mutex;
607
}
608
609
static int last_key = -1;
610
static std::vector< Ptr<CvWindow> > g_windows;
611
612
CV_IMPL int cvInitSystem( int argc, char** argv )
613
{
614
static int wasInitialized = 0;
615
616
// check initialization status
617
if( !wasInitialized )
618
{
619
gtk_init( &argc, &argv );
620
setlocale(LC_NUMERIC,"C");
621
622
#ifdef HAVE_OPENGL
623
gtk_gl_init(&argc, &argv);
624
#endif
625
626
wasInitialized = 1;
627
}
628
629
return 0;
630
}
631
632
CV_IMPL int cvStartWindowThread(){
633
#ifdef HAVE_GTHREAD
634
cvInitSystem(0,NULL);
635
if (!thread_started)
636
{
637
if (!g_thread_supported ()) {
638
/* the GThread system wasn't inited, so init it */
639
g_thread_init(NULL);
640
}
641
642
(void)getWindowMutex(); // force mutex initialization
643
644
// protects the 'last key pressed' variable
645
last_key_mutex = g_mutex_new();
646
647
// conditional that indicates a key has been pressed
648
cond_have_key = g_cond_new();
649
650
#if !GLIB_CHECK_VERSION(2, 32, 0)
651
// this is the window update thread
652
window_thread = g_thread_create(icvWindowThreadLoop,
653
NULL, TRUE, NULL);
654
#else
655
window_thread = g_thread_new("OpenCV window update", icvWindowThreadLoop, NULL);
656
#endif
657
}
658
thread_started = window_thread!=NULL;
659
return thread_started;
660
#else
661
return 0;
662
#endif
663
}
664
665
#ifdef HAVE_GTHREAD
666
gpointer icvWindowThreadLoop(gpointer /*data*/)
667
{
668
while(1){
669
{
670
cv::AutoLock lock(getWindowMutex());
671
gtk_main_iteration_do(FALSE);
672
}
673
674
// little sleep
675
g_usleep(500);
676
677
g_thread_yield();
678
}
679
return NULL;
680
}
681
682
#endif
683
684
#define CV_LOCK_MUTEX() cv::AutoLock lock(getWindowMutex())
685
686
static CvWindow* icvFindWindowByName( const char* name )
687
{
688
for(size_t i = 0; i < g_windows.size(); ++i)
689
{
690
CvWindow* window = g_windows[i].get();
691
if (window->name == name)
692
return window;
693
}
694
return NULL;
695
}
696
697
static CvWindow* icvWindowByWidget( GtkWidget* widget )
698
{
699
for (size_t i = 0; i < g_windows.size(); ++i)
700
{
701
CvWindow* window = g_windows[i].get();
702
if (window->widget == widget || window->frame == widget || window->paned == widget)
703
return window;
704
}
705
return NULL;
706
}
707
708
CvRect cvGetWindowRect_GTK(const char* name)
709
{
710
CV_Assert(name && "NULL name string");
711
712
CV_LOCK_MUTEX();
713
CvWindow* window = icvFindWindowByName(name);
714
if (!window)
715
CV_Error( CV_StsNullPtr, "NULL window" );
716
717
gint wx, wy;
718
#ifdef HAVE_OPENGL
719
if (window->useGl) {
720
gtk_widget_translate_coordinates(window->widget, gtk_widget_get_toplevel(window->widget), 0, 0, &wx, &wy);
721
return cvRect(wx, wy, window->widget->allocation.width, window->widget->allocation.height);
722
}
723
#endif
724
725
CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
726
gtk_widget_translate_coordinates(&image_widget->widget, gtk_widget_get_toplevel(&image_widget->widget), 0, 0, &wx, &wy);
727
if (image_widget->scaled_image) {
728
#if defined (GTK_VERSION3)
729
return cvRect(wx, wy, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(window->widget)),
730
MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(window->widget)));
731
#else
732
return cvRect(wx, wy, MIN(image_widget->scaled_image->cols, window->widget->allocation.width),
733
MIN(image_widget->scaled_image->rows, window->widget->allocation.height));
734
#endif //GTK_VERSION3
735
} else if (image_widget->original_image) {
736
#if defined (GTK_VERSION3)
737
return cvRect(wx, wy, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(window->widget)),
738
MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(window->widget)));
739
#else
740
return cvRect(wx, wy, MIN(image_widget->original_image->cols, window->widget->allocation.width),
741
MIN(image_widget->original_image->rows, window->widget->allocation.height));
742
#endif //GTK_VERSION3
743
}
744
745
return cvRect(-1, -1, -1, -1);
746
}
747
748
double cvGetModeWindow_GTK(const char* name)//YV
749
{
750
CV_Assert(name && "NULL name string");
751
752
CV_LOCK_MUTEX();
753
CvWindow* window = icvFindWindowByName(name);
754
if (!window)
755
CV_Error( CV_StsNullPtr, "NULL window" );
756
757
double result = window->status;
758
return result;
759
}
760
761
762
void cvSetModeWindow_GTK( const char* name, double prop_value)//Yannick Verdie
763
{
764
CV_Assert(name && "NULL name string");
765
766
CV_LOCK_MUTEX();
767
768
CvWindow* window = icvFindWindowByName(name);
769
if( !window )
770
CV_Error( CV_StsNullPtr, "NULL window" );
771
772
if(window->flags & CV_WINDOW_AUTOSIZE)//if the flag CV_WINDOW_AUTOSIZE is set
773
return;
774
775
//so easy to do fullscreen here, Linux rocks !
776
777
if (window->status==CV_WINDOW_FULLSCREEN && prop_value==CV_WINDOW_NORMAL)
778
{
779
gtk_window_unfullscreen(GTK_WINDOW(window->frame));
780
window->status=CV_WINDOW_NORMAL;
781
return;
782
}
783
784
if (window->status==CV_WINDOW_NORMAL && prop_value==CV_WINDOW_FULLSCREEN)
785
{
786
gtk_window_fullscreen(GTK_WINDOW(window->frame));
787
window->status=CV_WINDOW_FULLSCREEN;
788
return;
789
}
790
}
791
792
void cv::setWindowTitle(const String& winname, const String& title)
793
{
794
CV_LOCK_MUTEX();
795
796
CvWindow* window = icvFindWindowByName(winname.c_str());
797
798
if (!window)
799
{
800
namedWindow(winname);
801
window = icvFindWindowByName(winname.c_str());
802
CV_Assert(window);
803
}
804
805
gtk_window_set_title(GTK_WINDOW(window->frame), title.c_str());
806
}
807
808
double cvGetPropWindowAutoSize_GTK(const char* name)
809
{
810
CV_Assert(name && "NULL name string");
811
812
CV_LOCK_MUTEX();
813
814
CvWindow* window = icvFindWindowByName(name);
815
if (!window)
816
return -1; // keep silence here
817
818
double result = window->flags & CV_WINDOW_AUTOSIZE;
819
return result;
820
}
821
822
double cvGetRatioWindow_GTK(const char* name)
823
{
824
CV_Assert(name && "NULL name string");
825
826
CV_LOCK_MUTEX();
827
828
CvWindow* window = icvFindWindowByName(name);
829
if (!window)
830
return -1; // keep silence here
831
832
#if defined (GTK_VERSION3)
833
double result = static_cast<double>(
834
gtk_widget_get_allocated_width(window->widget)) / gtk_widget_get_allocated_height(window->widget);
835
#else
836
double result = static_cast<double>(window->widget->allocation.width) / window->widget->allocation.height;
837
#endif // GTK_VERSION3
838
return result;
839
}
840
841
double cvGetOpenGlProp_GTK(const char* name)
842
{
843
#ifdef HAVE_OPENGL
844
CV_Assert(name && "NULL name string");
845
846
CV_LOCK_MUTEX();
847
848
CvWindow* window = icvFindWindowByName(name);
849
if (!window)
850
return -1; // keep silence here
851
852
double result = window->useGl;
853
return result;
854
#else
855
(void)name;
856
return -1;
857
#endif
858
}
859
860
861
// OpenGL support
862
863
#ifdef HAVE_OPENGL
864
865
namespace
866
{
867
void createGlContext(CvWindow* window)
868
{
869
GdkGLConfig* glconfig;
870
871
// Try double-buffered visual
872
glconfig = gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
873
if (!glconfig)
874
CV_Error( CV_OpenGlApiCallError, "Can't Create A GL Device Context" );
875
876
// Set OpenGL-capability to the widget
877
if (!gtk_widget_set_gl_capability(window->widget, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE))
878
CV_Error( CV_OpenGlApiCallError, "Can't Create A GL Device Context" );
879
880
window->useGl = true;
881
}
882
883
void drawGl(CvWindow* window)
884
{
885
GdkGLContext* glcontext = gtk_widget_get_gl_context(window->widget);
886
GdkGLDrawable* gldrawable = gtk_widget_get_gl_drawable(window->widget);
887
888
if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
889
CV_Error( CV_OpenGlApiCallError, "Can't Activate The GL Rendering Context" );
890
891
glViewport(0, 0, window->widget->allocation.width, window->widget->allocation.height);
892
893
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
894
895
if (window->glDrawCallback)
896
window->glDrawCallback(window->glDrawData);
897
898
if (gdk_gl_drawable_is_double_buffered (gldrawable))
899
gdk_gl_drawable_swap_buffers(gldrawable);
900
else
901
glFlush();
902
903
gdk_gl_drawable_gl_end(gldrawable);
904
}
905
}
906
907
#endif // HAVE_OPENGL
908
909
#if defined (GTK_VERSION3)
910
static gboolean cvImageWidget_draw(GtkWidget* widget, cairo_t *cr, gpointer data)
911
{
912
#ifdef HAVE_OPENGL
913
CvWindow* window = (CvWindow*)data;
914
915
if (window->useGl)
916
{
917
drawGl(window);
918
return TRUE;
919
}
920
#else
921
(void)data;
922
#endif
923
924
CvImageWidget *image_widget = NULL;
925
GdkPixbuf *pixbuf = NULL;
926
927
g_return_val_if_fail (widget != NULL, FALSE);
928
g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
929
930
image_widget = CV_IMAGE_WIDGET (widget);
931
932
if( image_widget->scaled_image ){
933
// center image in available region
934
#if defined (GTK_VERSION3)
935
int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2;
936
int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2;
937
#else
938
int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
939
int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
940
#endif //GTK_VERSION3
941
942
#if defined (GTK_VERSION3)
943
pixbuf = gdk_pixbuf_new_from_data(image_widget->scaled_image->data.ptr, GDK_COLORSPACE_RGB, false,
944
8, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(widget)),
945
MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(widget)),
946
image_widget->scaled_image->step, NULL, NULL);
947
#else
948
pixbuf = gdk_pixbuf_new_from_data(image_widget->scaled_image->data.ptr, GDK_COLORSPACE_RGB, false,
949
8, MIN(image_widget->scaled_image->cols, widget->allocation.width),
950
MIN(image_widget->scaled_image->rows, widget->allocation.height),
951
image_widget->scaled_image->step, NULL, NULL);
952
#endif //GTK_VERSION3
953
954
gdk_cairo_set_source_pixbuf(cr, pixbuf, x0, y0);
955
}
956
else if( image_widget->original_image ){
957
#if defined (GTK_VERSION3)
958
pixbuf = gdk_pixbuf_new_from_data(image_widget->original_image->data.ptr, GDK_COLORSPACE_RGB, false,
959
8, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(widget)),
960
MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(widget)),
961
image_widget->original_image->step, NULL, NULL);
962
#else
963
pixbuf = gdk_pixbuf_new_from_data(image_widget->original_image->data.ptr, GDK_COLORSPACE_RGB, false,
964
8, MIN(image_widget->original_image->cols, widget->allocation.width),
965
MIN(image_widget->original_image->rows, widget->allocation.height),
966
image_widget->original_image->step, NULL, NULL);
967
#endif //GTK_VERSION3
968
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
969
}
970
971
cairo_paint(cr);
972
if(pixbuf)
973
g_object_unref(pixbuf);
974
return TRUE;
975
}
976
977
#else
978
static gboolean cvImageWidget_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data)
979
{
980
#ifdef HAVE_OPENGL
981
CvWindow* window = (CvWindow*)data;
982
983
if (window->useGl)
984
{
985
drawGl(window);
986
return TRUE;
987
}
988
#else
989
(void)data;
990
#endif
991
992
CvImageWidget *image_widget = NULL;
993
cairo_t *cr = NULL;
994
GdkPixbuf *pixbuf = NULL;
995
996
g_return_val_if_fail (widget != NULL, FALSE);
997
g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
998
g_return_val_if_fail (event != NULL, FALSE);
999
1000
if (event->count > 0)
1001
return FALSE;
1002
1003
cr = gdk_cairo_create(widget->window);
1004
image_widget = CV_IMAGE_WIDGET (widget);
1005
1006
if( image_widget->scaled_image ){
1007
// center image in available region
1008
int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
1009
int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
1010
1011
pixbuf = gdk_pixbuf_new_from_data(image_widget->scaled_image->data.ptr, GDK_COLORSPACE_RGB, false,
1012
8, MIN(image_widget->scaled_image->cols, widget->allocation.width),
1013
MIN(image_widget->scaled_image->rows, widget->allocation.height),
1014
image_widget->scaled_image->step, NULL, NULL);
1015
1016
gdk_cairo_set_source_pixbuf(cr, pixbuf, x0, y0);
1017
}
1018
else if( image_widget->original_image ){
1019
pixbuf = gdk_pixbuf_new_from_data(image_widget->original_image->data.ptr, GDK_COLORSPACE_RGB, false,
1020
8, MIN(image_widget->original_image->cols, widget->allocation.width),
1021
MIN(image_widget->original_image->rows, widget->allocation.height),
1022
image_widget->original_image->step, NULL, NULL);
1023
gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
1024
}
1025
1026
cairo_paint(cr);
1027
if(pixbuf)
1028
g_object_unref(pixbuf);
1029
cairo_destroy(cr);
1030
return TRUE;
1031
}
1032
#endif //GTK_VERSION3
1033
1034
CV_IMPL int cvNamedWindow( const char* name, int flags )
1035
{
1036
cvInitSystem(name ? 1 : 0,(char**)&name);
1037
CV_Assert(name && "NULL name string");
1038
1039
CV_LOCK_MUTEX();
1040
1041
// Check the name in the storage
1042
if (icvFindWindowByName(name))
1043
{
1044
return 1;
1045
}
1046
1047
Ptr<CvWindow> window = makePtr<CvWindow>(name);
1048
window->flags = flags;
1049
window->status = CV_WINDOW_NORMAL;//YV
1050
1051
window->frame = gtk_window_new( GTK_WINDOW_TOPLEVEL );
1052
1053
window->paned = gtk_vbox_new( FALSE, 0 );
1054
window->widget = cvImageWidgetNew( flags );
1055
gtk_box_pack_end( GTK_BOX(window->paned), window->widget, TRUE, TRUE, 0 );
1056
gtk_widget_show( window->widget );
1057
gtk_container_add( GTK_CONTAINER(window->frame), window->paned );
1058
gtk_widget_show( window->paned );
1059
1060
#ifndef HAVE_OPENGL
1061
if (flags & CV_WINDOW_OPENGL)
1062
CV_Error( CV_OpenGlNotSupported, "Library was built without OpenGL support" );
1063
#else
1064
if (flags & CV_WINDOW_OPENGL)
1065
createGlContext(window);
1066
1067
window->glDrawCallback = 0;
1068
window->glDrawData = 0;
1069
#endif
1070
1071
//
1072
// configure event handlers
1073
// TODO -- move this to CvImageWidget ?
1074
g_signal_connect( window->frame, "key-press-event",
1075
G_CALLBACK(icvOnKeyPress), window );
1076
g_signal_connect( window->widget, "button-press-event",
1077
G_CALLBACK(icvOnMouse), window );
1078
g_signal_connect( window->widget, "button-release-event",
1079
G_CALLBACK(icvOnMouse), window );
1080
g_signal_connect( window->widget, "motion-notify-event",
1081
G_CALLBACK(icvOnMouse), window );
1082
g_signal_connect( window->widget, "scroll-event",
1083
G_CALLBACK(icvOnMouse), window );
1084
g_signal_connect( window->frame, "delete-event",
1085
G_CALLBACK(icvOnClose), window );
1086
#if defined(GTK_VERSION3)
1087
g_signal_connect( window->widget, "draw",
1088
G_CALLBACK(cvImageWidget_draw), window );
1089
#else
1090
g_signal_connect( window->widget, "expose-event",
1091
G_CALLBACK(cvImageWidget_expose), window );
1092
#endif //GTK_VERSION3
1093
1094
1095
#if defined(GTK_VERSION3_4)
1096
gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK) ;
1097
#else
1098
gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK) ;
1099
#endif //GTK_VERSION3_4
1100
1101
gtk_widget_show( window->frame );
1102
gtk_window_set_title( GTK_WINDOW(window->frame), name );
1103
1104
g_windows.push_back(window);
1105
1106
bool b_nautosize = ((flags & CV_WINDOW_AUTOSIZE) == 0);
1107
gtk_window_set_resizable( GTK_WINDOW(window->frame), b_nautosize );
1108
1109
// allow window to be resized
1110
if( b_nautosize ){
1111
GdkGeometry geometry;
1112
geometry.min_width = 50;
1113
geometry.min_height = 50;
1114
gtk_window_set_geometry_hints( GTK_WINDOW( window->frame ), GTK_WIDGET( window->widget ),
1115
&geometry, (GdkWindowHints) (GDK_HINT_MIN_SIZE));
1116
}
1117
1118
#ifdef HAVE_OPENGL
1119
if (window->useGl)
1120
cvSetOpenGlContext(name);
1121
#endif
1122
1123
return 1;
1124
}
1125
1126
1127
#ifdef HAVE_OPENGL
1128
1129
CV_IMPL void cvSetOpenGlContext(const char* name)
1130
{
1131
GdkGLContext* glcontext;
1132
GdkGLDrawable* gldrawable;
1133
1134
CV_Assert(name && "NULL name string");
1135
1136
CV_LOCK_MUTEX();
1137
1138
CvWindow* window = icvFindWindowByName(name);
1139
if (!window)
1140
CV_Error( CV_StsNullPtr, "NULL window" );
1141
1142
if (!window->useGl)
1143
CV_Error( CV_OpenGlNotSupported, "Window doesn't support OpenGL" );
1144
1145
glcontext = gtk_widget_get_gl_context(window->widget);
1146
gldrawable = gtk_widget_get_gl_drawable(window->widget);
1147
1148
if (!gdk_gl_drawable_make_current(gldrawable, glcontext))
1149
CV_Error( CV_OpenGlApiCallError, "Can't Activate The GL Rendering Context" );
1150
}
1151
1152
CV_IMPL void cvUpdateWindow(const char* name)
1153
{
1154
CV_Assert(name && "NULL name string");
1155
1156
CV_LOCK_MUTEX();
1157
1158
CvWindow* window = icvFindWindowByName(name);
1159
if (!window)
1160
return;
1161
1162
// window does not refresh without this
1163
gtk_widget_queue_draw( GTK_WIDGET(window->widget) );
1164
}
1165
1166
CV_IMPL void cvSetOpenGlDrawCallback(const char* name, CvOpenGlDrawCallback callback, void* userdata)
1167
{
1168
CV_Assert(name && "NULL name string");
1169
1170
CV_LOCK_MUTEX();
1171
1172
CvWindow* window = icvFindWindowByName(name);
1173
if( !window )
1174
return;
1175
1176
if (!window->useGl)
1177
CV_Error( CV_OpenGlNotSupported, "Window was created without OpenGL context" );
1178
1179
window->glDrawCallback = callback;
1180
window->glDrawData = userdata;
1181
}
1182
1183
#endif // HAVE_OPENGL
1184
1185
1186
1187
CvWindow::~CvWindow()
1188
{
1189
gtk_widget_destroy(frame);
1190
}
1191
1192
static void checkLastWindow()
1193
{
1194
// if last window...
1195
if (g_windows.empty())
1196
{
1197
#ifdef HAVE_GTHREAD
1198
if( thread_started )
1199
{
1200
// send key press signal to jump out of any waiting cvWaitKey's
1201
g_cond_broadcast( cond_have_key );
1202
}
1203
else
1204
{
1205
#endif
1206
// Some GTK+ modules (like the Unity module) use GDBusConnection,
1207
// which has a habit of postponing cleanup by performing it via
1208
// idle sources added to the main loop. Since this was the last window,
1209
// we can assume that no event processing is going to happen in the
1210
// nearest future, so we should force that cleanup (by handling all pending
1211
// events) while we still have the chance.
1212
// This is not needed if thread_started is true, because the background
1213
// thread will process events continuously.
1214
while( gtk_events_pending() )
1215
gtk_main_iteration();
1216
#ifdef HAVE_GTHREAD
1217
}
1218
#endif
1219
}
1220
}
1221
1222
static void icvDeleteWindow( CvWindow* window )
1223
{
1224
bool found = false;
1225
for (std::vector< Ptr<CvWindow> >::iterator i = g_windows.begin();
1226
i != g_windows.end(); ++i)
1227
{
1228
if (i->get() == window)
1229
{
1230
g_windows.erase(i);
1231
found = true;
1232
break;
1233
}
1234
}
1235
CV_Assert(found && "Can't destroy non-registered window");
1236
1237
checkLastWindow();
1238
}
1239
1240
CV_IMPL void cvDestroyWindow( const char* name )
1241
{
1242
CV_Assert(name && "NULL name string");
1243
1244
CV_LOCK_MUTEX();
1245
1246
bool found = false;
1247
for (std::vector< Ptr<CvWindow> >::iterator i = g_windows.begin();
1248
i != g_windows.end(); ++i)
1249
{
1250
if (i->get()->name == name)
1251
{
1252
g_windows.erase(i);
1253
found = true;
1254
break;
1255
}
1256
}
1257
CV_Assert(found && "Can't destroy non-registered window");
1258
1259
checkLastWindow();
1260
}
1261
1262
1263
CV_IMPL void
1264
cvDestroyAllWindows( void )
1265
{
1266
CV_LOCK_MUTEX();
1267
1268
g_windows.clear();
1269
checkLastWindow();
1270
}
1271
1272
// CvSize icvCalcOptimalWindowSize( CvWindow * window, CvSize new_image_size){
1273
// CvSize window_size;
1274
// GtkWidget * toplevel = gtk_widget_get_toplevel( window->frame );
1275
// gdk_drawable_get_size( GDK_DRAWABLE(toplevel->window),
1276
// &window_size.width, &window_size.height );
1277
1278
// window_size.width = window_size.width + new_image_size.width - window->widget->allocation.width;
1279
// window_size.height = window_size.height + new_image_size.height - window->widget->allocation.height;
1280
1281
// return window_size;
1282
// }
1283
1284
CV_IMPL void
1285
cvShowImage( const char* name, const CvArr* arr )
1286
{
1287
CV_Assert(name && "NULL name string");
1288
1289
CV_LOCK_MUTEX();
1290
1291
CvWindow* window = icvFindWindowByName(name);
1292
if(!window)
1293
{
1294
cvNamedWindow(name, 1);
1295
window = icvFindWindowByName(name);
1296
}
1297
CV_Assert(window);
1298
1299
if (arr)
1300
{
1301
#ifdef HAVE_OPENGL
1302
if (window->useGl)
1303
{
1304
cv::imshow(name, cv::cvarrToMat(arr));
1305
return;
1306
}
1307
#endif
1308
1309
CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
1310
cvImageWidgetSetImage( image_widget, arr );
1311
}
1312
}
1313
1314
CV_IMPL void cvResizeWindow(const char* name, int width, int height )
1315
{
1316
CV_Assert(name && "NULL name string");
1317
1318
CV_LOCK_MUTEX();
1319
1320
CvWindow* window = icvFindWindowByName(name);
1321
if(!window)
1322
return;
1323
1324
CvImageWidget* image_widget = CV_IMAGE_WIDGET( window->widget );
1325
//if(image_widget->flags & CV_WINDOW_AUTOSIZE)
1326
//EXIT;
1327
1328
gtk_window_set_resizable( GTK_WINDOW(window->frame), 1 );
1329
gtk_window_resize( GTK_WINDOW(window->frame), width, height );
1330
1331
// disable initial resize since presumably user wants to keep
1332
// this window size
1333
image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
1334
}
1335
1336
1337
CV_IMPL void cvMoveWindow( const char* name, int x, int y )
1338
{
1339
CV_Assert(name && "NULL name string");
1340
1341
CV_LOCK_MUTEX();
1342
1343
CvWindow* window = icvFindWindowByName(name);
1344
if(!window)
1345
return;
1346
1347
gtk_window_move( GTK_WINDOW(window->frame), x, y );
1348
}
1349
1350
1351
static CvTrackbar*
1352
icvFindTrackbarByName( const CvWindow* window, const char* name )
1353
{
1354
for (size_t i = 0; i < window->trackbars.size(); ++i)
1355
{
1356
CvTrackbar* trackbar = window->trackbars[i].get();
1357
if (trackbar->name == name)
1358
return trackbar;
1359
}
1360
return NULL;
1361
}
1362
1363
static int
1364
icvCreateTrackbar( const char* trackbar_name, const char* window_name,
1365
int* val, int count, CvTrackbarCallback on_notify,
1366
CvTrackbarCallback2 on_notify2, void* userdata )
1367
{
1368
CV_Assert(window_name && "NULL window name");
1369
CV_Assert(trackbar_name && "NULL trackbar name");
1370
1371
if( count <= 0 )
1372
CV_Error( CV_StsOutOfRange, "Bad trackbar maximal value" );
1373
1374
CV_LOCK_MUTEX();
1375
1376
CvWindow* window = icvFindWindowByName(window_name);
1377
if(!window)
1378
return 0;
1379
1380
CvTrackbar* trackbar = icvFindTrackbarByName(window, trackbar_name);
1381
if (!trackbar)
1382
{
1383
Ptr<CvTrackbar> trackbar_ = makePtr<CvTrackbar>(trackbar_name);
1384
trackbar = trackbar_.get();
1385
trackbar->parent = window;
1386
window->trackbars.push_back(trackbar_);
1387
1388
GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
1389
GtkWidget* hscale_label = gtk_label_new( trackbar_name );
1390
GtkWidget* hscale = gtk_hscale_new_with_range( 0, count, 1 );
1391
gtk_scale_set_digits( GTK_SCALE(hscale), 0 );
1392
//gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
1393
gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
1394
1395
trackbar->widget = hscale;
1396
gtk_box_pack_start( GTK_BOX(hscale_box), hscale_label, FALSE, FALSE, 5 );
1397
gtk_widget_show( hscale_label );
1398
gtk_box_pack_start( GTK_BOX(hscale_box), hscale, TRUE, TRUE, 5 );
1399
gtk_widget_show( hscale );
1400
gtk_box_pack_start( GTK_BOX(window->paned), hscale_box, FALSE, FALSE, 5 );
1401
gtk_widget_show( hscale_box );
1402
}
1403
1404
if( val )
1405
{
1406
int value = *val;
1407
if( value < 0 )
1408
value = 0;
1409
if( value > count )
1410
value = count;
1411
gtk_range_set_value( GTK_RANGE(trackbar->widget), value );
1412
trackbar->pos = value;
1413
trackbar->data = val;
1414
}
1415
1416
trackbar->maxval = count;
1417
trackbar->notify = on_notify;
1418
trackbar->notify2 = on_notify2;
1419
trackbar->userdata = userdata;
1420
g_signal_connect( trackbar->widget, "value-changed",
1421
G_CALLBACK(icvOnTrackbar), trackbar );
1422
1423
// queue a widget resize to trigger a window resize to
1424
// compensate for the addition of trackbars
1425
gtk_widget_queue_resize( GTK_WIDGET(window->widget) );
1426
1427
return 1;
1428
}
1429
1430
1431
CV_IMPL int
1432
cvCreateTrackbar( const char* trackbar_name, const char* window_name,
1433
int* val, int count, CvTrackbarCallback on_notify )
1434
{
1435
return icvCreateTrackbar(trackbar_name, window_name, val, count,
1436
on_notify, 0, 0);
1437
}
1438
1439
1440
CV_IMPL int
1441
cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
1442
int* val, int count, CvTrackbarCallback2 on_notify2,
1443
void* userdata )
1444
{
1445
return icvCreateTrackbar(trackbar_name, window_name, val, count,
1446
0, on_notify2, userdata);
1447
}
1448
1449
1450
CV_IMPL void
1451
cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param )
1452
{
1453
CV_Assert(window_name && "NULL window name");
1454
1455
CV_LOCK_MUTEX();
1456
1457
CvWindow* window = icvFindWindowByName(window_name);
1458
if (!window)
1459
return;
1460
1461
window->on_mouse = on_mouse;
1462
window->on_mouse_param = param;
1463
}
1464
1465
1466
CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
1467
{
1468
CV_Assert(window_name && "NULL window name");
1469
CV_Assert(trackbar_name && "NULL trackbar name");
1470
1471
CV_LOCK_MUTEX();
1472
1473
CvWindow* window = icvFindWindowByName(window_name);
1474
if (!window)
1475
return -1;
1476
1477
CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
1478
if (!trackbar)
1479
return -1;
1480
1481
return trackbar->pos;
1482
}
1483
1484
1485
CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos )
1486
{
1487
CV_Assert(window_name && "NULL window name");
1488
CV_Assert(trackbar_name && "NULL trackbar name");
1489
1490
CV_LOCK_MUTEX();
1491
1492
CvWindow* window = icvFindWindowByName(window_name);
1493
if(!window)
1494
return;
1495
1496
CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
1497
if( trackbar )
1498
{
1499
if( pos < trackbar->minval )
1500
pos = trackbar->minval;
1501
1502
if( pos > trackbar->maxval )
1503
pos = trackbar->maxval;
1504
}
1505
else
1506
{
1507
CV_Error( CV_StsNullPtr, "No trackbar found" );
1508
}
1509
1510
gtk_range_set_value( GTK_RANGE(trackbar->widget), pos );
1511
}
1512
1513
1514
CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval)
1515
{
1516
CV_Assert(window_name && "NULL window name");
1517
CV_Assert(trackbar_name && "NULL trackbar name");
1518
1519
CV_LOCK_MUTEX();
1520
1521
CvWindow* window = icvFindWindowByName(window_name);
1522
if(!window)
1523
return;
1524
1525
CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
1526
if(!trackbar)
1527
return;
1528
1529
trackbar->maxval = maxval;
1530
if (trackbar->maxval >= trackbar->minval)
1531
gtk_range_set_range(GTK_RANGE(trackbar->widget), trackbar->minval, trackbar->maxval);
1532
}
1533
1534
1535
CV_IMPL void cvSetTrackbarMin(const char* trackbar_name, const char* window_name, int minval)
1536
{
1537
CV_Assert(window_name && "NULL window name");
1538
CV_Assert(trackbar_name && "NULL trackbar name");
1539
1540
CV_LOCK_MUTEX();
1541
1542
CvWindow* window = icvFindWindowByName(window_name);
1543
if(!window)
1544
return;
1545
1546
CvTrackbar* trackbar = icvFindTrackbarByName(window,trackbar_name);
1547
if(!trackbar)
1548
return;
1549
1550
trackbar->minval = minval;
1551
if (trackbar->maxval >= trackbar->minval)
1552
gtk_range_set_range(GTK_RANGE(trackbar->widget), trackbar->minval, trackbar->maxval);
1553
}
1554
1555
1556
CV_IMPL void* cvGetWindowHandle( const char* window_name )
1557
{
1558
CV_Assert(window_name && "NULL window name");
1559
1560
CV_LOCK_MUTEX();
1561
1562
CvWindow* window = icvFindWindowByName(window_name);
1563
if(!window)
1564
return NULL;
1565
1566
return (void*)window->widget;
1567
}
1568
1569
1570
CV_IMPL const char* cvGetWindowName( void* window_handle )
1571
{
1572
CV_Assert(window_handle && "NULL window handle");
1573
1574
CV_LOCK_MUTEX();
1575
1576
CvWindow* window = icvWindowByWidget( (GtkWidget*)window_handle );
1577
if (window)
1578
return window->name.c_str();
1579
1580
return ""; // FIXME: NULL?
1581
}
1582
1583
static GtkFileFilter* icvMakeGtkFilter(const char* name, const char* patterns, GtkFileFilter* images)
1584
{
1585
GtkFileFilter* filter = gtk_file_filter_new();
1586
gtk_file_filter_set_name(filter, name);
1587
1588
while(patterns[0])
1589
{
1590
gtk_file_filter_add_pattern(filter, patterns);
1591
gtk_file_filter_add_pattern(images, patterns);
1592
patterns += strlen(patterns) + 1;
1593
}
1594
1595
return filter;
1596
}
1597
1598
static void icvShowSaveAsDialog(GtkWidget* widget, CvWindow* window)
1599
{
1600
if (!window || !widget)
1601
return;
1602
1603
CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget);
1604
if (!image_widget || !image_widget->original_image)
1605
return;
1606
1607
GtkWidget* dialog = gtk_file_chooser_dialog_new("Save As...",
1608
GTK_WINDOW(widget),
1609
GTK_FILE_CHOOSER_ACTION_SAVE,
1610
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1611
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1612
NULL);
1613
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1614
1615
cv::String sname = gtk_window_get_title(GTK_WINDOW(window->frame));
1616
sname = sname.substr(sname.find_last_of("\\/") + 1) + ".png";
1617
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), sname.c_str());
1618
1619
GtkFileFilter* filter_all = gtk_file_filter_new();
1620
gtk_file_filter_set_name(filter_all, "All Files");
1621
gtk_file_filter_add_pattern(filter_all, "*");
1622
1623
GtkFileFilter* filter_images = gtk_file_filter_new();
1624
gtk_file_filter_set_name(filter_images, "All Images");
1625
1626
GtkFileFilter* file_filters[] = {
1627
icvMakeGtkFilter("Portable Network Graphics files (*.png)", "*.png\0", filter_images),
1628
icvMakeGtkFilter("JPEG files (*.jpeg;*.jpg;*.jpe)", "*.jpeg\0*.jpg\0*.jpe\0", filter_images),
1629
icvMakeGtkFilter("Windows bitmap (*.bmp;*.dib)", "*.bmp\0*.dib\0", filter_images),
1630
icvMakeGtkFilter("TIFF Files (*.tiff;*.tif)", "*.tiff\0*.tif\0", filter_images),
1631
icvMakeGtkFilter("JPEG-2000 files (*.jp2)", "*.jp2\0", filter_images),
1632
icvMakeGtkFilter("WebP files (*.webp)", "*.webp\0", filter_images),
1633
icvMakeGtkFilter("Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)", "*.pbm\0*.pgm\0*.ppm\0*.pxm\0*.pnm\0", filter_images),
1634
icvMakeGtkFilter("OpenEXR Image files (*.exr)", "*.exr\0", filter_images),
1635
icvMakeGtkFilter("Radiance HDR (*.hdr;*.pic)", "*.hdr\0*.pic\0", filter_images),
1636
icvMakeGtkFilter("Sun raster files (*.sr;*.ras)", "*.sr\0*.ras\0", filter_images),
1637
filter_images,
1638
filter_all
1639
};
1640
1641
for (size_t idx = 0; idx < sizeof(file_filters)/sizeof(file_filters[0]); ++idx)
1642
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), file_filters[idx]); // filter ownership is transferred to dialog
1643
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter_images);
1644
1645
cv::String filename;
1646
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1647
{
1648
char* fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1649
filename = fname;
1650
g_free(fname);
1651
}
1652
gtk_widget_destroy(dialog);
1653
1654
if (!filename.empty())
1655
{
1656
cv::Mat bgr;
1657
cv::cvtColor(cv::cvarrToMat(image_widget->original_image), bgr, cv::COLOR_RGB2BGR);
1658
cv::imwrite(filename, bgr);
1659
}
1660
}
1661
1662
#if defined (GTK_VERSION3)
1663
#define GDK_Escape GDK_KEY_Escape
1664
#define GDK_Return GDK_KEY_Return
1665
#define GDK_Linefeed GDK_KEY_Linefeed
1666
#define GDK_Tab GDK_KEY_Tab
1667
#define GDK_s GDK_KEY_s
1668
#define GDK_S GDK_KEY_S
1669
#endif //GTK_VERSION3
1670
1671
static gboolean icvOnKeyPress(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
1672
{
1673
int code = 0;
1674
1675
if ( BIT_ALLIN(event->state, GDK_CONTROL_MASK) && (event->keyval == GDK_s || event->keyval == GDK_S))
1676
{
1677
try
1678
{
1679
icvShowSaveAsDialog(widget, (CvWindow*)user_data);
1680
}
1681
catch(...)
1682
{
1683
// suppress all exceptions here
1684
}
1685
}
1686
1687
switch( event->keyval )
1688
{
1689
case GDK_Escape:
1690
code = 27;
1691
break;
1692
case GDK_Return:
1693
case GDK_Linefeed:
1694
code = 13;
1695
break;
1696
case GDK_Tab:
1697
code = '\t';
1698
break;
1699
default:
1700
code = event->keyval;
1701
}
1702
1703
code |= event->state << 16;
1704
1705
#ifdef HAVE_GTHREAD
1706
if(thread_started)
1707
{
1708
g_mutex_lock(last_key_mutex);
1709
last_key = code;
1710
// signal any waiting threads
1711
g_cond_broadcast(cond_have_key);
1712
g_mutex_unlock(last_key_mutex);
1713
}
1714
else
1715
#endif
1716
{
1717
last_key = code;
1718
}
1719
1720
return FALSE;
1721
}
1722
1723
1724
static void icvOnTrackbar( GtkWidget* widget, gpointer user_data )
1725
{
1726
int pos = cvRound( gtk_range_get_value(GTK_RANGE(widget)));
1727
CvTrackbar* trackbar = (CvTrackbar*)user_data;
1728
1729
if( trackbar && trackbar->signature == CV_TRACKBAR_MAGIC_VAL &&
1730
trackbar->widget == widget )
1731
{
1732
trackbar->pos = pos;
1733
if( trackbar->data )
1734
*trackbar->data = pos;
1735
if( trackbar->notify2 )
1736
trackbar->notify2(pos, trackbar->userdata);
1737
else if( trackbar->notify )
1738
trackbar->notify(pos);
1739
}
1740
}
1741
1742
static gboolean icvOnClose( GtkWidget* widget, GdkEvent* /*event*/, gpointer user_data )
1743
{
1744
CvWindow* window = (CvWindow*)user_data;
1745
if( window->signature == CV_WINDOW_MAGIC_VAL &&
1746
window->frame == widget )
1747
{
1748
icvDeleteWindow(window);
1749
}
1750
return TRUE;
1751
}
1752
1753
1754
static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data )
1755
{
1756
// TODO move this logic to CvImageWidget
1757
CvWindow* window = (CvWindow*)user_data;
1758
CvPoint2D32f pt32f = {-1., -1.};
1759
CvPoint pt = {-1,-1};
1760
int cv_event = -1, state = 0, flags = 0;
1761
CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
1762
1763
if( window->signature != CV_WINDOW_MAGIC_VAL ||
1764
window->widget != widget || !window->widget ||
1765
!window->on_mouse /*|| !image_widget->original_image*/)
1766
return FALSE;
1767
1768
if( event->type == GDK_MOTION_NOTIFY )
1769
{
1770
GdkEventMotion* event_motion = (GdkEventMotion*)event;
1771
1772
cv_event = CV_EVENT_MOUSEMOVE;
1773
pt32f.x = cvRound(event_motion->x);
1774
pt32f.y = cvRound(event_motion->y);
1775
state = event_motion->state;
1776
}
1777
else if( event->type == GDK_BUTTON_PRESS ||
1778
event->type == GDK_BUTTON_RELEASE ||
1779
event->type == GDK_2BUTTON_PRESS )
1780
{
1781
GdkEventButton* event_button = (GdkEventButton*)event;
1782
pt32f.x = cvRound(event_button->x);
1783
pt32f.y = cvRound(event_button->y);
1784
1785
1786
if( event_button->type == GDK_BUTTON_PRESS )
1787
{
1788
cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN :
1789
event_button->button == 2 ? CV_EVENT_MBUTTONDOWN :
1790
event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0;
1791
}
1792
else if( event_button->type == GDK_BUTTON_RELEASE )
1793
{
1794
cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP :
1795
event_button->button == 2 ? CV_EVENT_MBUTTONUP :
1796
event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0;
1797
}
1798
else if( event_button->type == GDK_2BUTTON_PRESS )
1799
{
1800
cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK :
1801
event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK :
1802
event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0;
1803
}
1804
state = event_button->state;
1805
}
1806
else if( event->type == GDK_SCROLL )
1807
{
1808
#if defined(GTK_VERSION3_4)
1809
// NOTE: in current implementation doesn't possible to put into callback function delta_x and delta_y separetely
1810
double delta = (event->scroll.delta_x + event->scroll.delta_y);
1811
cv_event = (event->scroll.delta_y!=0) ? CV_EVENT_MOUSEHWHEEL : CV_EVENT_MOUSEWHEEL;
1812
#else
1813
cv_event = CV_EVENT_MOUSEWHEEL;
1814
#endif //GTK_VERSION3_4
1815
1816
state = event->scroll.state;
1817
1818
switch(event->scroll.direction) {
1819
#if defined(GTK_VERSION3_4)
1820
case GDK_SCROLL_SMOOTH: flags |= (((int)delta << 16));
1821
break;
1822
#endif //GTK_VERSION3_4
1823
case GDK_SCROLL_LEFT: cv_event = CV_EVENT_MOUSEHWHEEL;
1824
/* FALLTHRU */
1825
case GDK_SCROLL_UP: flags |= ~0xffff;
1826
break;
1827
case GDK_SCROLL_RIGHT: cv_event = CV_EVENT_MOUSEHWHEEL;
1828
/* FALLTHRU */
1829
case GDK_SCROLL_DOWN: flags |= (((int)1 << 16));
1830
break;
1831
default: ;
1832
};
1833
}
1834
1835
if( cv_event >= 0 )
1836
{
1837
// scale point if image is scaled
1838
if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 &&
1839
image_widget->original_image &&
1840
image_widget->scaled_image )
1841
{
1842
// image origin is not necessarily at (0,0)
1843
#if defined (GTK_VERSION3)
1844
int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2;
1845
int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2;
1846
#else
1847
int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
1848
int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
1849
#endif //GTK_VERSION3
1850
pt.x = cvFloor( ((pt32f.x-x0)*image_widget->original_image->cols)/
1851
image_widget->scaled_image->cols );
1852
pt.y = cvFloor( ((pt32f.y-y0)*image_widget->original_image->rows)/
1853
image_widget->scaled_image->rows );
1854
}
1855
else
1856
{
1857
pt = cvPointFrom32f( pt32f );
1858
}
1859
1860
// if((unsigned)pt.x < (unsigned)(image_widget->original_image->width) &&
1861
// (unsigned)pt.y < (unsigned)(image_widget->original_image->height) )
1862
{
1863
flags |= BIT_MAP(state, GDK_SHIFT_MASK, CV_EVENT_FLAG_SHIFTKEY) |
1864
BIT_MAP(state, GDK_CONTROL_MASK, CV_EVENT_FLAG_CTRLKEY) |
1865
BIT_MAP(state, GDK_MOD1_MASK, CV_EVENT_FLAG_ALTKEY) |
1866
BIT_MAP(state, GDK_MOD2_MASK, CV_EVENT_FLAG_ALTKEY) |
1867
BIT_MAP(state, GDK_BUTTON1_MASK, CV_EVENT_FLAG_LBUTTON) |
1868
BIT_MAP(state, GDK_BUTTON2_MASK, CV_EVENT_FLAG_MBUTTON) |
1869
BIT_MAP(state, GDK_BUTTON3_MASK, CV_EVENT_FLAG_RBUTTON);
1870
window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param );
1871
}
1872
}
1873
1874
return FALSE;
1875
}
1876
1877
1878
static gboolean icvAlarm( gpointer user_data )
1879
{
1880
*(int*)user_data = 1;
1881
return FALSE;
1882
}
1883
1884
1885
CV_IMPL int cvWaitKey( int delay )
1886
{
1887
#ifdef HAVE_GTHREAD
1888
if (thread_started && g_thread_self() != window_thread)
1889
{
1890
gboolean expired = true;
1891
int my_last_key;
1892
1893
g_mutex_lock(last_key_mutex);
1894
// wait for signal or timeout if delay > 0
1895
if(delay>0){
1896
GTimeVal timer;
1897
g_get_current_time(&timer);
1898
g_time_val_add(&timer, delay*1000);
1899
expired = !g_cond_timed_wait(cond_have_key, last_key_mutex, &timer);
1900
}
1901
else{
1902
if (g_windows.empty())
1903
{
1904
CV_LOG_WARNING(NULL, "cv::waitKey() is called without timeout and missing active windows. Ignoring");
1905
}
1906
else
1907
{
1908
g_cond_wait(cond_have_key, last_key_mutex);
1909
expired=false;
1910
}
1911
}
1912
my_last_key = last_key;
1913
g_mutex_unlock(last_key_mutex);
1914
if(expired || g_windows.empty()){
1915
return -1;
1916
}
1917
return my_last_key;
1918
}
1919
else
1920
#endif
1921
{
1922
int expired = 0;
1923
guint timer = 0;
1924
if( delay > 0 )
1925
timer = g_timeout_add( delay, icvAlarm, &expired );
1926
last_key = -1;
1927
while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && (delay > 0 || !g_windows.empty()))
1928
;
1929
1930
if( delay > 0 && !expired )
1931
g_source_remove(timer);
1932
}
1933
return last_key;
1934
}
1935
1936
1937
#endif // HAVE_GTK
1938
#endif // _WIN32
1939
1940
/* End of file. */
1941
1942