]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/src/highgui/window_cocoa.mm
43aa7e93ea627f35ac5463c560a545399594c84c
[opencv.git] / opencv / src / highgui / window_cocoa.mm
1 /* The file is the modified version of window_cocoa.mm from opencv-cocoa project by Andre Cohen */
2
3 /*M///////////////////////////////////////////////////////////////////////////////////////
4 //
5 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
6 //
7 //  By downloading, copying, installing or using the software you agree to this license.
8 //  If you do not agree to this license, do not download, install,
9 //  copy or use the software.
10 //
11 //
12 //                         License Agreement
13 //                For Open Source Computer Vision Library
14 //
15 // Copyright (C) 2010, Willow Garage Inc., all rights reserved.
16 // Third party copyrights are property of their respective owners.
17 //
18 // Redistribution and use in source and binary forms, with or without modification,
19 // are permitted provided that the following conditions are met:
20 //
21 //   * Redistribution's of source code must retain the above copyright notice,
22 //     this list of conditions and the following disclaimer.
23 //
24 //   * Redistribution's in binary form must reproduce the above copyright notice,
25 //     this list of conditions and the following disclaimer in the documentation
26 //     and/or other materials provided with the distribution.
27 //
28 //   * The name of Intel Corporation may not be used to endorse or promote products
29 //     derived from this software without specific prior written permission.
30 //
31 // This software is provided by the copyright holders and contributors "as is" and
32 // any express or implied warranties, including, but not limited to, the implied
33 // warranties of merchantability and fitness for a particular purpose are disclaimed.
34 // In no event shall the Intel Corporation or contributors be liable for any direct,
35 // indirect, incidental, special, exemplary, or consequential damages
36 // (including, but not limited to, procurement of substitute goods or services;
37 // loss of use, data, or profits; or business interruption) however caused
38 // and on any theory of liability, whether in contract, strict liability,
39 // or tort (including negligence or otherwise) arising in any way out of
40 // the use of this software, even if advised of the possibility of such damage.
41 //
42 //M*/
43
44 #import <Cocoa/Cocoa.h>
45 #include "_highgui.h"
46
47 const int TOP_BORDER  = 7;
48
49 static NSApplication *application = nil;
50 static NSAutoreleasePool *pool = nil;
51 static NSMutableDictionary *windows = nil;
52 static bool wasInitialized = false;
53
54 @interface CVView : NSView {
55         NSImage *image;
56 }
57 @property(assign) NSImage *image;
58 - (void)setImageData:(CvArr *)arr;
59 @end
60
61 @interface CVSlider : NSView {
62         NSSlider *slider;
63         NSTextField *name;
64         int *value;
65         void *userData;
66         CvTrackbarCallback callback;
67         CvTrackbarCallback2 callback2;
68 }
69 @property(assign) NSSlider *slider;
70 @property(assign) NSTextField *name;
71 @property(assign) int *value;
72 @property(assign) void *userData;
73 @property(assign) CvTrackbarCallback callback;
74 @property(assign) CvTrackbarCallback2 callback2;
75 @end
76
77 @interface CVWindow : NSWindow {
78         NSMutableDictionary *sliders;
79         CvMouseCallback mouseCallback;
80         void *mouseParam;
81         BOOL autosize;
82 }
83 @property(assign) CvMouseCallback mouseCallback;
84 @property(assign) void *mouseParam;
85 @property(assign) BOOL autosize;
86 @property(assign) NSMutableDictionary *sliders;
87 - (CVView *)contentView;
88 - (void)cvSendMouseEvent:(NSEvent *)event type:(int)type flags:(int)flags;
89 - (void)cvMouseEvent:(NSEvent *)event;
90 - (void)createSliderWithName:(const char *)name maxValue:(int)max value:(int *)value callback:(CvTrackbarCallback)callback;
91 @end
92
93 static void icvCocoaCleanup(void)
94 {
95     if( application )
96     {
97         [application terminate:nil];
98         [pool release];
99     }
100 }
101
102 CV_IMPL int cvInitSystem( int argc, char** argv) 
103 {
104         wasInitialized = true;
105         
106         pool = [[NSAutoreleasePool alloc] init];
107         application = [NSApplication sharedApplication];
108         windows = [[NSMutableDictionary alloc] init];
109
110 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
111     
112 #ifndef NSAppKitVersionNumber10_5
113 #define NSAppKitVersionNumber10_5 949
114 #endif
115     if( floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5 )
116         [application setActivationPolicy:0/*NSApplicationActivationPolicyRegular*/];
117 #endif
118     [application finishLaunching];
119     atexit(icvCocoaCleanup);
120         
121     return 0;
122 }
123
124 CVWindow *cvGetWindow(const char *name) {
125         NSString *cvname = [NSString stringWithFormat:@"%s", name];
126         return (CVWindow *)[windows valueForKey:cvname];
127 }
128
129 CV_IMPL int cvStartWindowThread()
130 {
131     return 0;
132 }
133
134 CV_IMPL void cvDestroyWindow( const char* name)
135 {
136         CVWindow *window = cvGetWindow(name);
137         if(window) {
138                 [window performClose:nil];
139                 [windows removeObjectForKey:[NSString stringWithFormat:@"%s", name]];
140         }
141 }
142
143
144 CV_IMPL void cvDestroyAllWindows( void )
145 {
146         for(NSString *key in windows) {
147                 [[windows valueForKey:key] performClose:nil];
148         }
149         [windows removeAllObjects];
150 }
151
152
153 CV_IMPL void cvShowImage( const char* name, const CvArr* arr)
154 {
155         CVWindow *window = cvGetWindow(name);
156         if(window) {
157         bool empty = [[window contentView] image] == nil;
158         NSRect rect = [window frame];
159         NSRect vrectOld = [[window contentView] frame];
160         
161                 [[window contentView] setImageData:(CvArr *)arr];
162                 if([window autosize] || empty) {
163                         NSRect vrectNew = vrectOld;
164             vrectNew.size = [[[window contentView] image] size];
165             rect.size.width += vrectNew.size.width - vrectOld.size.width;
166             rect.size.height += vrectNew.size.height - vrectOld.size.height;
167                         [window setFrame:rect display:YES];
168                 } else {
169                         [window display];
170                 }
171         }
172         
173 }
174
175 CV_IMPL void cvResizeWindow( const char* name, int width, int height)
176 {
177         CVWindow *window = cvGetWindow(name);
178         if(window) {
179                 NSRect frame = [window frame];
180                 frame.size.width = width;
181                 frame.size.height = height;
182                 [window setFrame:frame display:YES];
183         }
184 }
185
186 CV_IMPL void cvMoveWindow( const char* name, int x, int y)
187 {
188         CV_FUNCNAME("cvMoveWindow");
189         __BEGIN__;
190         
191         CVWindow *window = nil;
192         
193         if(name == NULL)
194                 CV_ERROR( CV_StsNullPtr, "NULL window name" );
195         
196         window = cvGetWindow(name);
197         if(window) {
198                 y = [[window screen] frame].size.height - y;
199                 [window setFrameTopLeftPoint:NSMakePoint(x, y)];
200         }
201         
202         __END__;
203 }
204
205 CV_IMPL int cvCreateTrackbar (const char* trackbar_name,
206                               const char* window_name,
207                               int* val, int count,
208                               CvTrackbarCallback on_notify)
209 {
210         CV_FUNCNAME("cvCreateTrackbar");
211
212         int result = 0;
213         CVWindow *window = nil;
214         
215         __BEGIN__;
216         
217         if(window_name == NULL)
218                 CV_ERROR( CV_StsNullPtr, "NULL window name" );
219                 
220         window = cvGetWindow(window_name);
221         if(window) {
222                 [window createSliderWithName:trackbar_name
223                                                         maxValue:count
224                                                            value:val 
225                                                         callback:on_notify];
226                 result = 1;
227         }
228         
229         __END__;
230         return result;
231 }
232
233
234 CV_IMPL int cvCreateTrackbar2(const char* trackbar_name,
235                               const char* window_name,
236                               int* val, int count,
237                               CvTrackbarCallback2 on_notify2,
238                               void* userdata)
239 {
240         int res = cvCreateTrackbar(trackbar_name, window_name, val, count, NULL);
241         if(res) {
242                 CVSlider *slider = [[cvGetWindow(window_name) sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
243                 [slider setCallback2:on_notify2];
244                 [slider setUserData:userdata];
245         }
246         return res;
247 }
248
249
250 CV_IMPL void
251 cvSetMouseCallback( const char* name, CvMouseCallback function, void* info)
252 {
253         CV_FUNCNAME("cvSetMouseCallback");
254         
255         CVWindow *window = nil;
256         
257         __BEGIN__;
258         
259         if(name == NULL)
260                 CV_ERROR( CV_StsNullPtr, "NULL window name" );
261                 
262         window = cvGetWindow(name);
263         if(window) {
264                 [window setMouseCallback:function];
265                 [window setMouseParam:info];
266         }
267         
268         __END__;
269 }
270
271  CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
272 {
273         CV_FUNCNAME("cvGetTrackbarPos");
274         
275         CVWindow *window = nil;
276         int pos = -1;
277         
278         __BEGIN__;
279         
280         if(trackbar_name == NULL || window_name == NULL)
281                 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
282         
283         window = cvGetWindow(window_name);
284         if(window) {
285                 CVSlider *slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
286                 if(slider) {
287                         pos = [[slider slider] intValue];
288                 }
289         }
290         
291         __END__;
292         return pos;
293 }
294
295 CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name, int pos)
296 {
297         CV_FUNCNAME("cvSetTrackbarPos");
298         
299         CVWindow *window = nil;
300         CVSlider *slider = nil;
301         
302         __BEGIN__;
303         
304         if(trackbar_name == NULL || window_name == NULL)
305                 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
306         
307         if(pos <= 0)
308         CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
309         
310         window = cvGetWindow(window_name);
311         if(window) {
312                 slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
313                 if(slider) {
314                         [[slider slider] setIntValue:pos];
315                 }
316         }
317         
318         __END__;
319 }
320
321 CV_IMPL void* cvGetWindowHandle( const char* name )
322 {
323         return cvGetWindow(name);
324 }
325
326
327 CV_IMPL const char* cvGetWindowName( void* window_handle )
328 {       
329         for(NSString *key in windows) {
330                 if([windows valueForKey:key] == window_handle)
331                         return [key UTF8String];
332         }
333         return 0;
334 }
335
336 CV_IMPL int cvNamedWindow( const char* name, int flags )
337 {
338     if( !wasInitialized )
339         cvInitSystem(0, 0);
340     
341         CVWindow *window = [[CVWindow alloc] initWithContentRect:NSMakeRect(0,0,200,200)
342         styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|
343         (!(flags & CV_WND_PROP_AUTOSIZE) ? NSResizableWindowMask : 0)
344         backing:NSBackingStoreBuffered  
345             defer:NO];
346         
347         NSString *windowName = [NSString stringWithFormat:@"%s", name];
348         
349         [window setContentView:[[CVView alloc] init]];
350         
351         [window setHasShadow:YES];
352         [window setAcceptsMouseMovedEvents:YES];
353         [window useOptimizedDrawing:YES];
354         [window setTitle:windowName];
355         [window makeKeyAndOrderFront:nil];
356
357         [window setAutosize:(flags == CV_WINDOW_AUTOSIZE)];     
358         
359         [windows setValue:window forKey:windowName];
360         
361         return [windows count]-1;
362 }
363
364 CV_IMPL int cvWaitKey (int maxWait)
365 {
366     int returnCode = -1;
367         double start = [[NSDate date] timeIntervalSince1970];
368     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
369
370         while(true) {
371                 if(([[NSDate date] timeIntervalSince1970] - start) * 1000 >= maxWait && maxWait>0)
372                         break;
373                 
374                 //event = [application currentEvent];
375         [pool release];
376         pool = [[NSAutoreleasePool alloc] init];
377         
378         NSEvent *event =
379         [application
380          nextEventMatchingMask:NSAnyEventMask
381          untilDate://[NSDate dateWithTimeIntervalSinceNow: 1./100]
382          [NSDate distantPast]
383          inMode:NSDefaultRunLoopMode
384          dequeue:YES];
385         
386                 if([event type] == NSKeyDown) {
387                         returnCode = [[event characters] characterAtIndex:0];
388             break;
389                 }
390         
391         [application sendEvent:event];
392         [application updateWindows];
393         
394                 [NSThread sleepForTimeInterval:1/100.];
395         }
396     [pool release];
397
398         return returnCode;
399 }
400
401 @implementation CVWindow 
402
403 @synthesize mouseCallback;
404 @synthesize mouseParam;
405 @synthesize autosize;
406 @synthesize sliders;
407
408 - (void)cvSendMouseEvent:(NSEvent *)event type:(int)type flags:(int)flags {
409         NSPoint mp = [NSEvent mouseLocation];
410         NSRect visible = [[self contentView] frame];
411     mp = [self convertScreenToBase: mp];
412     double viewHeight = [self contentView].frame.size.height;
413     double viewWidth = [self contentView].frame.size.width;
414     CVWindow *window = (CVWindow *)[[self contentView] window];
415     for(NSString *key in [window sliders]) {
416         NSSlider *slider = [[window sliders] valueForKey:key];
417         viewHeight = std::min(viewHeight, (double)([slider frame].origin.y));
418     }
419     viewHeight -= TOP_BORDER;
420     mp.y = viewHeight - mp.y;
421     
422     NSImage* image = ((CVView*)[self contentView]).image;
423     NSSize imageSize = [image size];
424     mp.x = mp.x * imageSize.width / std::max(viewWidth, 1.);
425     mp.y = mp.y * imageSize.height / std::max(viewHeight, 1.);
426     
427     if( mp.x >= 0 && mp.y >= 0 && mp.x < imageSize.width && mp.y < imageSize.height )
428         mouseCallback(type, mp.x, mp.y, flags, mouseParam);
429 }
430
431 - (void)cvMouseEvent:(NSEvent *)event {
432         if(!mouseCallback) 
433                 return;
434                 
435         int flags = 0;
436         if([event modifierFlags] & NSShiftKeyMask)              flags |= CV_EVENT_FLAG_SHIFTKEY;
437         if([event modifierFlags] & NSControlKeyMask)    flags |= CV_EVENT_FLAG_CTRLKEY;
438         if([event modifierFlags] & NSAlternateKeyMask)  flags |= CV_EVENT_FLAG_ALTKEY;
439                 
440         if([event type] == NSLeftMouseDown)     {[self cvSendMouseEvent:event type:CV_EVENT_LBUTTONDOWN flags:flags | CV_EVENT_FLAG_LBUTTON];}
441         if([event type] == NSLeftMouseUp)       {[self cvSendMouseEvent:event type:CV_EVENT_LBUTTONUP   flags:flags | CV_EVENT_FLAG_LBUTTON];}
442         if([event type] == NSRightMouseDown){[self cvSendMouseEvent:event type:CV_EVENT_RBUTTONDOWN flags:flags | CV_EVENT_FLAG_RBUTTON];}
443         if([event type] == NSRightMouseUp)      {[self cvSendMouseEvent:event type:CV_EVENT_RBUTTONUP   flags:flags | CV_EVENT_FLAG_RBUTTON];}
444         if([event type] == NSOtherMouseDown){[self cvSendMouseEvent:event type:CV_EVENT_MBUTTONDOWN flags:flags];}
445         if([event type] == NSOtherMouseUp)      {[self cvSendMouseEvent:event type:CV_EVENT_MBUTTONUP   flags:flags];}
446         if([event type] == NSMouseMoved)        {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags];}
447         if([event type] == NSLeftMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags | CV_EVENT_FLAG_LBUTTON];}
448         if([event type] == NSRightMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags | CV_EVENT_FLAG_RBUTTON];}
449         if([event type] == NSOtherMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE   flags:flags | CV_EVENT_FLAG_MBUTTON];}
450 }
451 - (void)keyDown:(NSEvent *)theEvent {
452         [super keyDown:theEvent];
453 }
454 - (void)rightMouseDragged:(NSEvent *)theEvent {
455         [self cvMouseEvent:theEvent];
456 }
457 - (void)rightMouseUp:(NSEvent *)theEvent {
458         [self cvMouseEvent:theEvent];
459 }
460 - (void)rightMouseDown:(NSEvent *)theEvent {
461         // Does not seem to work?
462         [self cvMouseEvent:theEvent];
463 }
464 - (void)mouseMoved:(NSEvent *)theEvent {
465         [self cvMouseEvent:theEvent];
466 }
467 - (void)otherMouseDragged:(NSEvent *)theEvent {
468         [self cvMouseEvent:theEvent];
469 }
470 - (void)otherMouseUp:(NSEvent *)theEvent {
471         [self cvMouseEvent:theEvent];
472 }
473 - (void)otherMouseDown:(NSEvent *)theEvent {
474         [self cvMouseEvent:theEvent];
475 }
476 - (void)mouseDragged:(NSEvent *)theEvent {
477         [self cvMouseEvent:theEvent];
478 }
479 - (void)mouseUp:(NSEvent *)theEvent {
480         [self cvMouseEvent:theEvent];
481 }
482 - (void)mouseDown:(NSEvent *)theEvent {
483         [self cvMouseEvent:theEvent];
484 }
485
486 - (void)createSliderWithName:(const char *)name maxValue:(int)max value:(int *)value callback:(CvTrackbarCallback)callback {
487         if(sliders == nil)
488                 sliders = [[NSMutableDictionary alloc] init];
489         
490         NSString *cvname = [NSString stringWithFormat:@"%s", name];
491         
492         // Avoid overwriting slider
493         if([sliders valueForKey:cvname]!=nil)
494                 return;
495         
496         // Create slider
497         CVSlider *slider = [[CVSlider alloc] init];
498         [[slider name] setStringValue:cvname];
499         [[slider slider] setMaxValue:max];
500         if(value)
501     {
502                 [[slider slider] setIntValue:*value];
503         [slider setValue:value];
504     }
505         if(callback)
506                 [slider setCallback:callback];
507         
508         // Save slider
509         [sliders setValue:slider forKey:cvname];
510         [[self contentView] addSubview:slider];
511         
512         // Update slider sizes
513         [[self contentView] setFrameSize:[[self contentView] frame].size];
514         [[self contentView] setNeedsDisplay:YES];
515 }
516
517 - (CVView *)contentView {
518         return (CVView*)[super contentView];
519 }
520
521 @end
522
523 @implementation CVView 
524
525 @synthesize image;
526
527 - (id)init {
528         [super init];
529         image = nil;
530         return self;
531 }
532
533 - (void)setImageData:(CvArr *)arr {
534         CvMat *arrMat, *cvimage, stub;
535          
536         arrMat = cvGetMat(arr, &stub);
537         
538         cvimage = cvCreateMat(arrMat->rows, arrMat->cols, CV_8UC3);
539         cvConvertImage(arrMat, cvimage, CV_CVTIMG_SWAP_RB);
540         
541         CGColorSpaceRef colorspace = NULL;
542     CGDataProviderRef provider = NULL;
543         int width = cvimage->width;
544     int height = cvimage->height;
545
546     colorspace = CGColorSpaceCreateDeviceRGB();
547
548     int size = 8;
549     int nbChannels = 3;
550       
551     provider = CGDataProviderCreateWithData(NULL, cvimage->data.ptr, width * height , NULL );
552       
553         CGImageRef imageRef = CGImageCreate(width, height, size , size*nbChannels , cvimage->step, colorspace,  kCGImageAlphaNone , provider, NULL, true, kCGRenderingIntentDefault);
554         
555         NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:imageRef] autorelease];
556         if(image) {
557                 [image release];
558         }
559         image = [[NSImage alloc] init];
560         [image addRepresentation:bitmap];
561         
562     CGDataProviderRelease(provider);
563         cvReleaseMat(&cvimage);
564         
565         [self setNeedsDisplay:YES];
566 }
567
568 - (void)setFrameSize:(NSSize)size {
569         [super setFrameSize:size];
570         int height = size.height;
571         
572         CVWindow *window = (CVWindow *)[self window];
573         for(NSString *key in [window sliders]) {
574                 NSSlider *slider = [[window sliders] valueForKey:key];
575                 NSRect r = [slider frame];
576                 r.origin.y = height - r.size.height;
577                 [slider setFrame:r];
578                 height -= r.size.height;
579         }
580 }
581
582 - (void)drawRect:(NSRect)rect {
583         CVWindow *window = (CVWindow *)[self window];
584         int height = 0;
585         for(NSString *key in [window sliders]) {
586                 height += [[[window sliders] valueForKey:key] frame].size.height;
587         }
588         
589         [super drawRect:rect];
590
591         NSRect imageRect = {{0,0}, {self.frame.size.width, self.frame.size.height-height-6}};
592         
593         if(image != nil) {
594                 [image drawInRect: imageRect
595                                       fromRect: NSZeroRect
596                                      operation: NSCompositeSourceOver
597                                       fraction: 1.0];
598         }
599
600 }
601
602 @end
603
604 @implementation CVSlider 
605
606 @synthesize slider;
607 @synthesize name;
608 @synthesize value;
609 @synthesize userData;
610 @synthesize callback;
611 @synthesize callback2;
612
613 - (id)init {
614         [super init];
615
616         callback = NULL;
617         value = NULL;
618         userData = NULL;
619
620         [self setFrame:NSMakeRect(0,0,200,25)];
621
622         name = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0,120, 20)];
623         [name setEditable:NO];
624     [name setSelectable:NO];
625     [name setBezeled:NO];
626     [name setBordered:NO];
627         [name setDrawsBackground:NO];
628         [[name cell] setLineBreakMode:NSLineBreakByTruncatingTail];
629         [self addSubview:name];
630         
631         slider = [[NSSlider alloc] initWithFrame:NSMakeRect(120, 0, 76, 20)];
632         [slider setAutoresizingMask:NSViewWidthSizable];
633         [slider setMinValue:0];
634         [slider setMaxValue:100];
635         [slider setContinuous:YES];
636         [slider setTarget:self];
637         [slider setAction:@selector(sliderChanged:)];
638         [self addSubview:slider];
639         
640         [self setAutoresizingMask:NSViewWidthSizable];
641         
642         [self setFrame:NSMakeRect(12, 0, 182, 30)];
643         
644         return self;
645 }
646
647 - (void)sliderChanged:(NSNotification *)notification {
648     int pos = [slider intValue];
649     if(value)
650         *value = pos;
651         if(callback)
652                 callback(pos);
653         if(callback2)
654                 callback2(pos, userData);
655 }
656
657 @end
658
659 /* End of file. */