|
|
- With Carbon-
Carbon Graphics
With Carbon - Carbon Graphics
GWorldをNSImageに変換する
Keywords: CGContextDrawImage()
(この記事は、[macosx-dev-jp:04092] Re: ARGBのNSBitmapImageRep作成方法と、CocoaDev: NSImageFromAMovieFrame を参考にさせてもらいました。)
Carbon では、GWorld と呼ばれるオフスクリーングラフィックスが使われるんだ。GWorld は、PixMap という構造体に、画像の生データを持っている。この GWorld を NSImage に変換してみよう。
単純に考えれば、GWorld から生データを取り出して、NSBitmapImageRep を作ればいいじゃん、って思うけど、どうもそう簡単にはいかないらしい。というのは、GWorld のデータのフォーマットが ARGB なのに対して、NSBitmapImageRep は RGBA だからだ。これを変換しなくてはいけない。めんどくせー。
手で変換するのもあれなので、CoreGraphics を使ってみよう。処理の流れは、こうなるらしい。
- GWorld のデータから、CGImageCreate() を使って、alphaInfo に kCGImageAlphaFirst か kCGImageAlphaPremultipliedFirst を指定して、source 画像を作る。
- NSBitmapimageRep の bitmapData から、CGBitmapContextCreate() を使って、alphaInfo に kCGImageAlphaLast か kCGImageAlphaPremultipliedFirst を指定して、destination コンテキストを作る。
-
CGContextDrawImage() を使って、source 画像を destination コンテキストに描きだす。
これでオッケーらしい。
では、ソースコードを。GWorld を NSImage に変換する関数だ。
imageFromGworld()
NSImage* imageFromGworld(GWorldPtr gworld)
{
// PixMapHandleを取得します
PixMapHandle pixMapH;
pixMapH = GetGWorldPixMap(gworld);
// PixMapHandleをロックします
if (!LockPixels(pixMapH)) {
return nil;
}
// PixMapHandleの情報を取得します
Rect portRect;
int pixelsWide;
int pixelsHigh;
void* baseAddr;
long rowBytes;
GetPortBounds(gworld, &portRect);
pixelsWide = portRect.right - portRect.left;
pixelsHigh = portRect.bottom - portRect.top;
baseAddr = GetPixBaseAddr(pixMapH);
rowBytes = GetPixRowBytes(pixMapH);
// Source画像を作成します
CGColorSpaceRef srcColorSpaceRef;
CGDataProviderRef dataProviderRef;
CGImageRef srcImageRef;
srcColorSpaceRef = CGColorSpaceCreateDeviceRGB();
dataProviderRef = CGDataProviderCreateWithData(
NULL, baseAddr, rowBytes * pixelsHigh, NULL);
srcImageRef = CGImageCreate(
pixelsWide,
pixelsHigh,
8,
32,
rowBytes,
srcColorSpaceRef,
kCGImageAlphaPremultipliedFirst,
dataProviderRef,
NULL,
NO,
kCGRenderingIntentDefault);
// 空のNSBitmapImageのインスタンスを作成します
NSBitmapImageRep* bitmapImageRep;
bitmapImageRep = [[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:pixelsWide
pixelsHigh:pixelsHigh
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:NULL
bitsPerPixel:NULL] autorelease];
// Destinationのコンテキストを作成します
CGColorSpaceRef dstColorSpaceRef;
CGContextRef dstContextRef;
dstColorSpaceRef = CGColorSpaceCreateDeviceRGB();
dstContextRef = CGBitmapContextCreate(
[bitmapImageRep bitmapData],
pixelsWide,
pixelsHigh,
8,
[bitmapImageRep bytesPerRow],
dstColorSpaceRef,
kCGImageAlphaPremultipliedLast);
// Source画像をdestinationコンテキストに描きます
CGContextDrawImage(
dstContextRef,
CGRectMake(0, 0, pixelsWide, pixelsHigh),
srcImageRef);
// 各種データを解放します
CGColorSpaceRelease(srcColorSpaceRef);
CGDataProviderRelease(dataProviderRef);
CGImageRelease(srcImageRef);
CGColorSpaceRelease(dstColorSpaceRef);
CGContextRelease(dstContextRef);
// PixMapHandleをアンロックします
UnlockPixels(pixMapH);
// NSImageのインスタンスを作成します
NSImage* image;
image = [[NSImage alloc]
initWithSize:NSMakeSize(pixelsWide, pixelsHigh)];
[image addRepresentation:bitmapImageRep];
[image autorelease];
return image;
}
けっこう長くてめんどくさいでしょ。でもやりたいことは CGContextDrawImage() を呼び出すことで、ほとんどはその準備だ。このソースコードを使った例は、次のスクリーンショットを撮るで紹介しよう。
With Carbon - Carbon Graphics
スクリーンショットを撮る
Keywords: GetQDGlobalsScreenBits()
(この記事は、keta さんの掲示板での書き込みを参考にさせてもらいました。ありがとうございました。)
現在のスクリーンショットを撮る方法を考えてみよう。
まず、現在のスクリーンの画像データにアクセスルにはどうすればいいか?これは簡単で、 GetQDGlobalsScreenBits() を呼び出せばいいらしい。でも、こいつを使うと古い BitMap 形式で取得される。そこで、CopyBits() を使って GWorld に書き出してやる。GWorld に書き出してしまえば、さっきの GWorldをNSImageに変換する を使えば NSImage として Cocoa で使えるし、Carbon ならそのまま使うことができる。
GWorld に CopyBits() するコードは、次のようになるんだ。
ScreenShotCocoa/main.m
// ScreenBits(ディスプレイ)を取得します
BitMap bitmap;
Rect bounds;
GetQDGlobalsScreenBits(&bitmap);
bounds = bitmap.bounds;
// 32Bitsの色深度でGWorldを作成します
GWorldPtr gworld;
OSErr result;
result = NewGWorld(&gworld, 32, &bounds, 0, 0, 0);
if (result != noErr) {
return 1;
}
// ScreenBitsをGWorldにコピーします
GWorldPtr saveGWorld;
GDHandle saveGDH;
GetGWorld(&saveGWorld, &saveGDH);
SetGWorld(gworld, NULL);
EraseRect(&bounds);
LockPortBits(gworld);
CopyBits (
&bitmap,
GetPortBitMapForCopyBits(gworld),
&bounds,
&bounds,
srcCopy,
NULL);
UnlockPortBits(gworld);
SetGWorld(saveGWorld, saveGDH);
Cocoa なら、この GWorld を NSImage に変換するなりしてやればいい。
また、Carbon なら、QuickTime を使ってもっと手っ取り早く、こうできる。
ScreenShotCarbon/main.m
// ScreenBits(ディスプレイ)を取得します
PixMap pixMap;
GetQDGlobalsScreenBits((BitMap*)&pixMap);
// PNGで書き出します
GraphicsExportComponent exporter;
PixMapPtr pixMapP;
FSSpec spec;
pixMapP = &pixMap;
FSMakeFSSpec(0, 0, "\pScreenShot.PNG", &spec);
OpenADefaultComponent(
GraphicsExporterComponentType,
kQTFileTypePNG,
&exporter);
GraphicsExportSetInputPixmap(exporter, &pixMapP);
GraphicsExportSetDepth(exporter, 24);
GraphicsExportSetOutputFile(exporter, &spec);
GraphicsExportDoExport(exporter, NULL);
CloseComponent(exporter);
ファイルに書き出すなら、こっちの方が楽だね。
■サンプルダウンロード:
ScreenShotCocoa.dmg
ScreenShotCarbon.dmg
|