1 /* The file is the modified version of window_cocoa.mm from opencv-cocoa project by Andre Cohen */
3 /*M///////////////////////////////////////////////////////////////////////////////////////
5 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
13 // For Open Source Computer Vision Library
15 // Copyright (C) 2010, Willow Garage Inc., all rights reserved.
16 // Third party copyrights are property of their respective owners.
18 // Redistribution and use in source and binary forms, with or without modification,
19 // are permitted provided that the following conditions are met:
21 // * Redistribution's of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
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.
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.
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.
44 #import <Cocoa/Cocoa.h>
47 const int TOP_BORDER = 7;
49 static NSApplication *application = nil;
50 static NSAutoreleasePool *pool = nil;
51 static NSMutableDictionary *windows = nil;
52 static bool wasInitialized = false;
54 @interface CVView : NSView {
57 @property(assign) NSImage *image;
58 - (void)setImageData:(CvArr *)arr;
61 @interface CVSlider : NSView {
66 CvTrackbarCallback callback;
67 CvTrackbarCallback2 callback2;
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;
77 @interface CVWindow : NSWindow {
78 NSMutableDictionary *sliders;
79 CvMouseCallback mouseCallback;
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;
93 static void icvCocoaCleanup(void)
97 [application terminate:nil];
102 CV_IMPL int cvInitSystem( int argc, char** argv)
104 wasInitialized = true;
106 pool = [[NSAutoreleasePool alloc] init];
107 application = [NSApplication sharedApplication];
108 windows = [[NSMutableDictionary alloc] init];
110 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
112 #ifndef NSAppKitVersionNumber10_5
113 #define NSAppKitVersionNumber10_5 949
115 if( floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5 )
116 [application setActivationPolicy:0/*NSApplicationActivationPolicyRegular*/];
118 [application finishLaunching];
119 atexit(icvCocoaCleanup);
124 CVWindow *cvGetWindow(const char *name) {
125 NSString *cvname = [NSString stringWithFormat:@"%s", name];
126 return (CVWindow *)[windows valueForKey:cvname];
129 CV_IMPL int cvStartWindowThread()
134 CV_IMPL void cvDestroyWindow( const char* name)
136 CVWindow *window = cvGetWindow(name);
138 [window performClose:nil];
139 [windows removeObjectForKey:[NSString stringWithFormat:@"%s", name]];
144 CV_IMPL void cvDestroyAllWindows( void )
146 for(NSString *key in windows) {
147 [[windows valueForKey:key] performClose:nil];
149 [windows removeAllObjects];
153 CV_IMPL void cvShowImage( const char* name, const CvArr* arr)
155 CVWindow *window = cvGetWindow(name);
157 bool empty = [[window contentView] image] == nil;
158 NSRect rect = [window frame];
159 NSRect vrectOld = [[window contentView] frame];
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];
175 CV_IMPL void cvResizeWindow( const char* name, int width, int height)
177 CVWindow *window = cvGetWindow(name);
179 NSRect frame = [window frame];
180 frame.size.width = width;
181 frame.size.height = height;
182 [window setFrame:frame display:YES];
186 CV_IMPL void cvMoveWindow( const char* name, int x, int y)
188 CV_FUNCNAME("cvMoveWindow");
191 CVWindow *window = nil;
194 CV_ERROR( CV_StsNullPtr, "NULL window name" );
196 window = cvGetWindow(name);
198 y = [[window screen] frame].size.height - y;
199 [window setFrameTopLeftPoint:NSMakePoint(x, y)];
205 CV_IMPL int cvCreateTrackbar (const char* trackbar_name,
206 const char* window_name,
208 CvTrackbarCallback on_notify)
210 CV_FUNCNAME("cvCreateTrackbar");
213 CVWindow *window = nil;
217 if(window_name == NULL)
218 CV_ERROR( CV_StsNullPtr, "NULL window name" );
220 window = cvGetWindow(window_name);
222 [window createSliderWithName:trackbar_name
234 CV_IMPL int cvCreateTrackbar2(const char* trackbar_name,
235 const char* window_name,
237 CvTrackbarCallback2 on_notify2,
240 int res = cvCreateTrackbar(trackbar_name, window_name, val, count, NULL);
242 CVSlider *slider = [[cvGetWindow(window_name) sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
243 [slider setCallback2:on_notify2];
244 [slider setUserData:userdata];
251 cvSetMouseCallback( const char* name, CvMouseCallback function, void* info)
253 CV_FUNCNAME("cvSetMouseCallback");
255 CVWindow *window = nil;
260 CV_ERROR( CV_StsNullPtr, "NULL window name" );
262 window = cvGetWindow(name);
264 [window setMouseCallback:function];
265 [window setMouseParam:info];
271 CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
273 CV_FUNCNAME("cvGetTrackbarPos");
275 CVWindow *window = nil;
280 if(trackbar_name == NULL || window_name == NULL)
281 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
283 window = cvGetWindow(window_name);
285 CVSlider *slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
287 pos = [[slider slider] intValue];
295 CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name, int pos)
297 CV_FUNCNAME("cvSetTrackbarPos");
299 CVWindow *window = nil;
300 CVSlider *slider = nil;
304 if(trackbar_name == NULL || window_name == NULL)
305 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
308 CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
310 window = cvGetWindow(window_name);
312 slider = [[window sliders] valueForKey:[NSString stringWithFormat:@"%s", trackbar_name]];
314 [[slider slider] setIntValue:pos];
321 CV_IMPL void* cvGetWindowHandle( const char* name )
323 return cvGetWindow(name);
327 CV_IMPL const char* cvGetWindowName( void* window_handle )
329 for(NSString *key in windows) {
330 if([windows valueForKey:key] == window_handle)
331 return [key UTF8String];
336 CV_IMPL int cvNamedWindow( const char* name, int flags )
338 if( !wasInitialized )
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
347 NSString *windowName = [NSString stringWithFormat:@"%s", name];
349 [window setContentView:[[CVView alloc] init]];
351 [window setHasShadow:YES];
352 [window setAcceptsMouseMovedEvents:YES];
353 [window useOptimizedDrawing:YES];
354 [window setTitle:windowName];
355 [window makeKeyAndOrderFront:nil];
357 [window setAutosize:(flags == CV_WINDOW_AUTOSIZE)];
359 [windows setValue:window forKey:windowName];
361 return [windows count]-1;
364 CV_IMPL int cvWaitKey (int maxWait)
367 double start = [[NSDate date] timeIntervalSince1970];
368 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
371 if(([[NSDate date] timeIntervalSince1970] - start) * 1000 >= maxWait && maxWait>0)
374 //event = [application currentEvent];
376 pool = [[NSAutoreleasePool alloc] init];
380 nextEventMatchingMask:NSAnyEventMask
381 untilDate://[NSDate dateWithTimeIntervalSinceNow: 1./100]
383 inMode:NSDefaultRunLoopMode
386 if([event type] == NSKeyDown) {
387 returnCode = [[event characters] characterAtIndex:0];
391 [application sendEvent:event];
392 [application updateWindows];
394 [NSThread sleepForTimeInterval:1/100.];
401 @implementation CVWindow
403 @synthesize mouseCallback;
404 @synthesize mouseParam;
405 @synthesize autosize;
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));
419 viewHeight -= TOP_BORDER;
420 mp.y = viewHeight - mp.y;
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.);
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);
431 - (void)cvMouseEvent:(NSEvent *)event {
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;
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];}
451 - (void)keyDown:(NSEvent *)theEvent {
452 [super keyDown:theEvent];
454 - (void)rightMouseDragged:(NSEvent *)theEvent {
455 [self cvMouseEvent:theEvent];
457 - (void)rightMouseUp:(NSEvent *)theEvent {
458 [self cvMouseEvent:theEvent];
460 - (void)rightMouseDown:(NSEvent *)theEvent {
461 // Does not seem to work?
462 [self cvMouseEvent:theEvent];
464 - (void)mouseMoved:(NSEvent *)theEvent {
465 [self cvMouseEvent:theEvent];
467 - (void)otherMouseDragged:(NSEvent *)theEvent {
468 [self cvMouseEvent:theEvent];
470 - (void)otherMouseUp:(NSEvent *)theEvent {
471 [self cvMouseEvent:theEvent];
473 - (void)otherMouseDown:(NSEvent *)theEvent {
474 [self cvMouseEvent:theEvent];
476 - (void)mouseDragged:(NSEvent *)theEvent {
477 [self cvMouseEvent:theEvent];
479 - (void)mouseUp:(NSEvent *)theEvent {
480 [self cvMouseEvent:theEvent];
482 - (void)mouseDown:(NSEvent *)theEvent {
483 [self cvMouseEvent:theEvent];
486 - (void)createSliderWithName:(const char *)name maxValue:(int)max value:(int *)value callback:(CvTrackbarCallback)callback {
488 sliders = [[NSMutableDictionary alloc] init];
490 NSString *cvname = [NSString stringWithFormat:@"%s", name];
492 // Avoid overwriting slider
493 if([sliders valueForKey:cvname]!=nil)
497 CVSlider *slider = [[CVSlider alloc] init];
498 [[slider name] setStringValue:cvname];
499 [[slider slider] setMaxValue:max];
502 [[slider slider] setIntValue:*value];
503 [slider setValue:value];
506 [slider setCallback:callback];
509 [sliders setValue:slider forKey:cvname];
510 [[self contentView] addSubview:slider];
512 // Update slider sizes
513 [[self contentView] setFrameSize:[[self contentView] frame].size];
514 [[self contentView] setNeedsDisplay:YES];
517 - (CVView *)contentView {
518 return (CVView*)[super contentView];
523 @implementation CVView
533 - (void)setImageData:(CvArr *)arr {
534 CvMat *arrMat, *cvimage, stub;
536 arrMat = cvGetMat(arr, &stub);
538 cvimage = cvCreateMat(arrMat->rows, arrMat->cols, CV_8UC3);
539 cvConvertImage(arrMat, cvimage, CV_CVTIMG_SWAP_RB);
541 CGColorSpaceRef colorspace = NULL;
542 CGDataProviderRef provider = NULL;
543 int width = cvimage->width;
544 int height = cvimage->height;
546 colorspace = CGColorSpaceCreateDeviceRGB();
551 provider = CGDataProviderCreateWithData(NULL, cvimage->data.ptr, width * height , NULL );
553 CGImageRef imageRef = CGImageCreate(width, height, size , size*nbChannels , cvimage->step, colorspace, kCGImageAlphaNone , provider, NULL, true, kCGRenderingIntentDefault);
555 NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:imageRef] autorelease];
559 image = [[NSImage alloc] init];
560 [image addRepresentation:bitmap];
562 CGDataProviderRelease(provider);
563 cvReleaseMat(&cvimage);
565 [self setNeedsDisplay:YES];
568 - (void)setFrameSize:(NSSize)size {
569 [super setFrameSize:size];
570 int height = size.height;
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;
578 height -= r.size.height;
582 - (void)drawRect:(NSRect)rect {
583 CVWindow *window = (CVWindow *)[self window];
585 for(NSString *key in [window sliders]) {
586 height += [[[window sliders] valueForKey:key] frame].size.height;
589 [super drawRect:rect];
591 NSRect imageRect = {{0,0}, {self.frame.size.width, self.frame.size.height-height-6}};
594 [image drawInRect: imageRect
596 operation: NSCompositeSourceOver
604 @implementation CVSlider
609 @synthesize userData;
610 @synthesize callback;
611 @synthesize callback2;
620 [self setFrame:NSMakeRect(0,0,200,25)];
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];
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];
640 [self setAutoresizingMask:NSViewWidthSizable];
642 [self setFrame:NSMakeRect(12, 0, 182, 30)];
647 - (void)sliderChanged:(NSNotification *)notification {
648 int pos = [slider intValue];
654 callback2(pos, userData);