ページ

2008年8月17日日曜日

マウスカーソルのキャプチャ (3)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

前回の続き。CGSGetGlobalCursorData( ) を使ったマウスカーソルのキャプチャのソース解説。サンプルでは AppController.m に実装されている。

まずボタンを押すと #start: が呼ばれ、ここでタイマーを仕掛ける。

-(IBAction)start:(id)sender
{
NSTimer* timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(fire:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}


5秒経つと #fire: が呼ばれる。タイマーを無効化した後、マウスカーソルのキャプチャ処理 #saveCursorImage を呼出す。
- (void)fire:(NSTimer*)theTimer
{
static int t = 5;
if (t < 0) {
t = 5;
}
t--;
if (t < 0) {
[theTimer invalidate];
[self saveCursorImage];
}
}


#saveCursorImage での流れは次の通り。
 (1) CGSNewConnection( ) でコネクションを確立
 (2) コネクションを使い、マウスカーソルの画像を取得する。この時、CGSGetGlobalCursorDataSize( ) と CGSGetGlobalCursorData( ) を使う
 (3) 得られたデータを CGImageRef へ変換する
 (4) さらに CGImageRef を NSBitmapImagerep へ変換する
 (5) 最後に NSData を経てファイル (PNG)へ書き出す

以下、具体的に追ってみよう。
まずは宣言から。プライベート関数を使うので自前でプロトタイプ宣言を行っておく。
typedef int CGSConnectionRef;
static CGSConnectionRef conn = 0;
extern CGError CGSNewConnection(void*, CGSConnectionRef*);
extern CGError CGSReleaseConnection(CGSConnectionRef);
extern CGError CGSGetGlobalCursorDataSize(CGSConnectionRef, int*);
extern CGError CGSGetGlobalCursorDa


必要な変数も定義しておこう。
-(void)saveCursorImage
{
int cursorDataSize;
int cursorRowBytes, colorDepth, components, cursorBitsPerComponent;
unsigned char *cursorData;
CGRect cursorRect;
CGPoint hotspot;
   :
   :



(1) CGSNewConnection( ) でコネクションを確立
 if (CGSNewConnection(NULL, &conn) != kCGErrorSuccess) {
NSLog(@"CGSNewConnection error.\n");
return;
}



(2) コネクションを使い、マウスカーソルの画像を取得する。
 if (CGSGetGlobalCursorDataSize(conn, &cursorDataSize) != kCGErrorSuccess) {
NSLog(@"CGSGetGlobalCursorDataSize error\n");
return;
}
cursorData = (unsigned char*) malloc(cursorDataSize);

CGError err = CGSGetGlobalCursorData(conn, cursorData, &cursorDataSize, &cursorRowBytes,
&cursorRect, &hotspot, &colorDepth, &components, &cursorBitsPerComponent);
if (err != kCGErrorSuccess) {
NSLog(@"CGSGetGlobalCursorData error\n");
return;
}


(3) 得られたデータを CGImageRef へ変換する
一旦、CGDataProvidreRef を経由させる。またCGImageRef を生成するのに カラースペースが必要なので用意する(これは最後に解放してやる)。
 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef providerRef = CGDataProviderCreateWithData(self, cursorData, cursorDataSize, nil);

// http://lists.apple.com/archives/quartz-dev//2008/Apr/msg00025.html
CGImageRef cgImageRef = CGImageCreate(cursorRect.size.width,
cursorRect.size.height,
cursorBitsPerComponent,
cursorBitsPerComponent * components,
cursorRowBytes,
colorSpace,
kCGImageAlphaPremultipliedLast,
providerRef,
nil,
NO,
kCGRenderingIntentDefault);


(4) さらに CGImageRef を NSBitmapImagerep へ変換する
これは簡単。CGImageRefなら、イニシャライザ一発でNSBitmapImageRepを作れる。
 NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCGImage:cgImageRef];


(5) 最後に NSData を経てファイル (PNG)へ書き出す
デスクトップへ cursor.png という名前(固定)で書き出す。
 NSData* data = [bitmap representationUsingType:NSPNGFileType
properties:[NSDictionary dictionary]];
NSString* path =
[NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* filename = [NSString stringWithFormat:@"%@/cursor.png", path];
[data writeToFile:filename atomically:YES];


-- - - - -
プラベート関数なのが惜しい!
なおこれが使えても、最終的には別途キャプチャした画像と合成する作業が残っている。座標を合わせるのが面倒そうだが。。