Geometry 関数
NSBundle
NSDictionary
NSFileManager
NSMutableAttributedString
NSNumber
NSScanner
NSString
NSTimer
Graphic 関数
NSApplication
NSBezierPath
NSCell
NSColor
NSCursor
NSCustomImageRep
NSDocument
NSDocumentController
NSDragging
NSEvent
NSFontManager
NSGraphicsContext
NSImage
NSMenu
NSOutlineView
NSPanel
NSPopUpButton
NSResponder
NSScrollView
NSString 追加
NSTableColumn
NSTableView
NSTextStorage
NSTextView
NSToolbar
NSView
NSWindow
NSWorkspace
その他
.nib ウィンドウ
Views パレット
クラス
インスタンス変数
メソッド
CFXML
Carbon Event
Carbon Graphics
-GWorldをNSImageに変換する
-スクリーンショットを撮る
Cocoa で日本語
メソッド
その他

- 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 に kCGImageAlphaFirstkCGImageAlphaPremultipliedFirst を指定して、source 画像を作る。
  • NSBitmapimageRep の bitmapData から、CGBitmapContextCreate() を使って、alphaInfo に kCGImageAlphaLastkCGImageAlphaPremultipliedFirst を指定して、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



[Home] [Download] [Archives] [BBS] [Cocoa Programming Tips 1001] [Core Foundation の秘密] [Safari Developer Center] [はじめてのブラウザのつくり方] [Sketch BP] [スクリーンセイバーを作ろう] [Objective-C 最適化] [Authorization API 完全理解] [Mac OS X Programming Books Review] [オブジェクト指向の言語比較論] [panther-dev]

mailto: mkino@xd5.so-net.ne.jp