Cocoa developing note part1 ColorPicker

EF’s website keeps crashing, don’t know what the hell is going on. Billy is going to leave, I still have one thing to teach him, if he want to invite a girl (just girl ) to have dinner, the most trendy way is to say “YueMa“, all right, repeat after me,

“YueMa

“YouMa

“No, No, No,it’s YueMa

“YaoMa

“Better,they are similar, but too directly, YueMa~”

“YueMa

“Great!”

About one week ago, a colleague of mine complained about “There is no handy screen picture capturer on Mac OS X!”, she was still a windows user at that time. But that fired me, I can do programming with iOS by Objective-C, so nothing would stop me to write a app running on Mac OS X too, especially I am learning Swift too.

Capture image from screen

First, the most important thing is to capture from screen with any origin and size, here is what I found from stackoverflow, insert these in the top of your code:

#ifndef PROFILE_WINDOW_GRAB
#define PROFILE_WINDOW_GRAB 0
#endif

#if PROFILE_WINDOW_GRAB
#define StopwatchStart() AbsoluteTime start = UpTime()
#define Profile(img) CFRelease(CGDataProviderCopyData(CGImageGetDataProvider(img)))
#define StopwatchEnd(caption) do { Duration time = AbsoluteDeltaToDuration(UpTime(), start); double timef = time < 0 ? time / -1000000.0 : time / 1000.0; NSLog(@"%s Time Taken: %f seconds", caption, timef); } while(0)
#else
#define StopwatchStart()
#define Profile(img)
#define StopwatchEnd(caption)
#endif

then, when you want to capture a image, do it like this:

StopwatchStart();
CGRect windowsCGRect = NSRectToCGRect(self.mainWindow.frame);
CGRect holeViewCGRect = NSRectToCGRect(self.holeView.frame);
CGRect fullscreenRect = self.mainWindow.screen.frame;
float yWin = fullscreenRect.size.height - windowsCGRect.origin.y - windowsCGRect.size.height;
float yHole = windowsCGRect.size.height - holeViewCGRect.origin.y - holeViewCGRect.size.height;
CGRect rectToShot = CGRectMake(windowsCGRect.origin.x + holeViewCGRect.origin.x,
                                   yWin+yHole, holeViewCGRect.size.width, holeViewCGRect.size.height);
CGImageRef screenShot = CGWindowListCreateImage(rectToShot, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault);
Profile(screenShot);
StopwatchEnd("Screenshot");
return screenShot;

You may notice that, WTF is that ‘mainWindow’, and WTH is that ‘holeView’?

Well ,if you want to take a image from the screen, nothing could be better than the native image capture app comes with Mac OS X, the only thing it doesn’t provided is, you can not setup a regular size, or ratio for the image size. So I did it like this, make a transparent mask like this:

As you can see, the picture you get is what inside the frame. Simple, isn’t it? ‘mainWindow’ is NSWindow instance of this Window. holeView is the content which is transparent inside it, the code below shows how to create a transparent view:

  • Create a sub class of NSView, named it HoleView
  • insert code into drawRect method:
    - (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    NSRectFillUsingOperation(NSMakeRect(0, 0, self.frame.size.width, self.frame.size.height), NSCompositeClear);
    }
Top Left vs. Bottom Left

Another difference is the coordination systems, iOS starts the coordination system (x=0,y=0) from top left corner, but Mac OS X starts it from bottom left,  it’s weird(ok, I am naive), all the UI coordination systems I’d ever used are base on top-left rules. So it takes me some time to adapt.

E.g, if you want to take picture with (x=0,y=0,w=100,y=100) rect, then the start y-axis position should be:

screenHeight - startY, if I take my MBP’s resolution, that will be (x=0,y=(800-0),w=100,y=100).

Sub Window or Multi-Windows

The next thing I need to do is, I need a standalone setting panel, for resize the image capture frame window.  I am familiar with iOS dev, and know how to create multi view and handle them in flows, but not Cocoa for Mac, so I learnt it from apple development bible, I mean, the documents in apple dev center. Here are the steps to create a popup window:

  • Create a new Cocoa Class file, make it as a subclass of NSWindowController. Name it NewWindowController for example.
  • Don’t forget to select ‘Also create XIB file for user interface’.
  • Use these code to popup it any where you want:
    NewWindowController *newWindowController = [[NewWindowController alloc] initWithWindowNibName:@"NewWindowController"];
    [newWindowController showWindow:self];
Act Keyboard and Mouse Event:

To receive a keyboard event, you need :

  • set view can act a keyboard event,add this two methods in to your NSResponder subclass:
    -(BOOL)acceptsFirstResponder
    {
    return YES;
    }
  • (BOOL)canBecomeKeyView
    {
    return YES;
    }
  • make your view as first responder

    [self.window makeFirstResponder:self];
  • handle keyboard action in method like this:

    -(void)keyDown:(NSEvent *)theEvent{
    if (theEvent.keyCode == 49) {
    }
    }
Pick a color under mouse

I saw that somebody recommend other to use NSReadPixel, it can not work out of the window view, I found code like this works:

NSPoint where = CGPointMake((int)[NSEvent mouseLocation].x,800-(int)[NSEvent mouseLocation].y);
CGDirectDisplayID did = [[self.window.screen.deviceDescription objectForKey:@"NSScreenNumber"] intValue];
CGImageRef image = CGDisplayCreateImageForRect(did,
                                                   CGRectMake(where.x, where.y, 1, 1));
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:image];
CGImageRelease(image);
NSColor *color = [bitmap colorAtX:0 y:0];

AppPackage