ホームページ>開発ツール>Xojo Cocoa Plugin 覚書・応用編2・アイコンの取得
Xojo Cocoa Plugin 覚書
目次
応用編2・アイコンの取得
- はじめに
- アイコン画像を取得するプラグインを作る(アルゴリズム編)
- まずデバッグ用プロジェクトを作る
- アイコン画像を取得するプラグインを作る(実装編)
- 動作テストする
- おわりに
- お世話になったサイト
- 更新履歴
はじめに
以前作ったMach-O対応版(Carbon版)と同等の機能を持つCocoa版についての話題です。
今回は、アイコンの取得です。
テスト環境は前回と同じく、以下の通りです。
・Mac mini mid 2010 (intel) / Mac OS X 10.9.4 / Xojo 2014 Release 2 / Xcode 5.1.1
アイコン画像を取得するプラグインを作る(アルゴリズム編)
Carbon版ではIconRefを取得してIconFamiryに変換後、任意のアイコンを取得する方法を用いましたが、Cocoa版では、まずは、広くネット上で認知されている「NSWorkspaceクラスのiconForFile:メソッドを使う」方法を試してみました。
ただし、この方法は手軽なのですが、問題がない訳ではありません。
それは、(処理の過程でTIFFRepresentationを利用するのですが、ここでの)アイコンの品質が必ずしも期待通りにならない、という点です。
TIFFRepresentationで取得できるアイコン群の並びは(実験したところ)以下の通りです。
(注:SizeとPixelsはTIFFRepresentationのDump値、一番右はファイルに書き出した画像の実際のピクセル数)
アイコン画像がアップルのガイドライン(他に、例えばこちら)通りに用意されていれば問題ないのでしょうが、そうでない(即ち、アイコンに抜けがある)場合は、どうも近傍(?)のものがリサイズされてアサインされるようで、この時、常に大きなものが縮小される訳ではなく、小さなものが拡大されることもあるようです。Size={512, 512} Pixels=1024x1024 | 1024x1024 Size={256, 256} Pixels=1024x1024 | 512x512 Size={512, 512} Pixels=1024x1024 | 512x512 Size={128, 128} Pixels=1024x1024 | 256x256 Size={256, 256} Pixels=1024x1024 | 256x256 Size={128, 128} Pixels=128x128 | 128x128 Size={36, 36} Pixels=128x128 | 256x256 Size={32, 32} Pixels=128x128 | 64x64 Size={48, 48} Pixels=48x48 | 128x128 Size={36, 36} Pixels=48x48 | 128x128 Size={18, 18} Pixels=48x48 | 64x64 Size={32, 32} Pixels=32x32 | 32x32 Size={16, 16} Pixels=32x32 | 32x32 Size={18, 18} Pixels=32x32 | 32x32 Size={16, 16} Pixels=16x16 | 16x16
で、データを見ても拡大されたかどうかが分からない訳です。
今回は実験目的なので、アイコンサイズの並びは上記の通りと仮定して、配列要素番号を引数として指定することで、所望のアイコンを取得する方式にしましたが、目的のサイズが必ず存在するか不明の場合は、最大サイズをそのまま(あるいは、目的のサイズに縮小して)返す、あたりが現実解でしょうか。
また、Xojoに関連しては、以下の点に注意する必要があります。
これらを踏まえた、処理の流れは以下の通りです。
- Xojo本体は画像のアルファチャンネル(マスクとして利用)に対応しているが、プラグイン側のREALpictureはアルファ値を持てない(ヘッダを調べてみたが、それらしいものを見つけられなかった)ので、Carbon版と同じく、アイコンとマスクを別々に取得して、Xojo側で合成する。
- ピクセルごとのRGBの並びが異なるため、並べ替える。(cocoa = RGBA , Xojo = kRBPixelXRGB32, // 4 bytes/pixel: Unused, Red, Green, Blue
kRBPixelBGRX32 // 4 bytes/pixel: Blue, Green, Red, Unused )
(CocoaのRGB値は、厳密に扱う場合は配慮が必要。詳細は、例えばこちらのサイトを参照。)
- 入力ファイルパスを元に、iconForFile:メソッドにより、NSImageを取得
- NSImageのTIFFRepresentationメソッドと、NSBitmapImageRepのimageRepWithData:メソッドを用いて、NSBitmapImageRepを取得
- NSBitmapImageRepのbitmapDataメソッドを用いて、rawデータを取得
- rgbの並べ替え(アイコン)、または、アルファ値をrgbにコピー(マスク)処理
- REALBuildPictureFromBufferメソッドにより、REALpicture形式に変換して返す
まずデバッグ用プロジェクトを作る
例によって、デスクトップアプリケーション用プロジェクトを作ってデバッグ用としました。(上述のTIFFRepresentationの中身も、これで確認しました。)
今回は、共通部分が多く、rawからimageを復元する部分のみがアプリ固有となっています。(以下は抜粋。)
注1)フルバージョンの実装部はこちら。ヘッダはこちら。- (void)getIconImageFrom:(NSString *)path with:(NSInteger)kind { // ------------------------------------------------------ ここからプラグイン NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; NSImage *image = [workspace iconForFile:path]; NSArray *bitmapImages = [NSBitmapImageRep imageRepsWithData:[image TIFFRepresentation]]; NSBitmapImageRep *bitmapImageRep = [bitmapImages objectAtIndex:kind]; unsigned char *rawData = [bitmapImageRep bitmapData]; NSInteger bytesPerRow = [bitmapImageRep bytesPerRow]; NSUInteger width = [bitmapImageRep pixelsWide]; NSUInteger height = [bitmapImageRep pixelsHigh]; (省略) // ------------------------------------------------------ ここまでプラグイン NSLog(@"%@",bitmapImages); // 内容の確認 [[image TIFFRepresentation] writeToFile:@"/Users/hoge/Desktop/test.tif" atomically:YES]; // ファイルに書き出し size_t bufferLength = width * height * 4; CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, rawData, bufferLength, NULL); size_t bitsPerComponent = 8; size_t bitsPerPixel = 32; CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast; CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; CGImageRef iref = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, YES, renderingIntent); // rawデータからImageを復元している NSImage *image2 = [[NSImage alloc] initWithCGImage:iref size:NSMakeSize(width, height)]; NSImage *image3 = [[NSImage alloc] initWithData:[image2 TIFFRepresentation]]; // 冗長だが、image2を直接描くとエラーになる [imgWell setImage:image3]; // WindowにImage Wellを配置して、描画している }
注2)別途、ウィンドウ上にButton1個とImage Well1個を用意して接続する。
注3)rawからimageを復元するコードは、以下の参考サイトの内容を再利用させて頂きました。
参考サイト(3):CGDataProviderRef provider= CGDataProviderCreateWithData(NULL (UInt8*)data, byte - Pastebin.com
アイコン画像を取得するプラグインを作る(実装編)
アプリケーションによるテストが一段落したら、プラグイン固有の処理に切り替えます。
プラグイン作成手順は前回と同じですので、そちらを参照して下さい。
ソースコードはこちら。
注1)kindの100番台をマスクに割り当てていますが、この辺は好みのやり方でいいと思います。
動作テストする
- Xojoを起動する。
- Window1のプロパティに以下を追加。
pPic As Picture pPicImage As Picture pPicMask As Picture
- Window1にCanvasを1個置き、Paintイベントに以下を記述。
if pPic<>nil then if me.Width < pPic.Width or me.Height < pPic.Height then g.DrawPicture pPic,0,0,me.Width,me.Height,0,0,pPic.Width,pPic.Height else g.DrawPicture pPic,0,0 end if end if
- Window1にPushButtonを2個置き、PushButtonのActionイベントに以下を記述。(上段はPushButton1用、下段はPushButton2用)
Dim f as FolderItem Dim kind As Integer // 0=1024x1024, 1=512x512, 2=512x512, 3=256x256, 4=256x256, 5=128x128, 6=256x256, 7=64x64 // 8=128x128, 9=128x128, 10=64x64, 11=32x32, 12=32x32, 13=32x32, 14=16x16 kind = 14 Dim dlg as OpenDialog = New OpenDialog f = dlg.ShowModal() // get File if f <> nil then Dim str As String str=ReplaceAll(f.ShellPath,"\","") pPicImage=PluginModule1.getIconImage(str,kind) // Plug-in pPicMask=PluginModule1.getIconImage(str,kind+100) // Plug-in if pPic<>nil then pPic=nil end if pPic=new Picture(pPicImage.Width,pPicImage.Height,32) pPic.Graphics.DrawPicture pPicImage,0,0 pPic.Mask.Graphics.DrawPicture pPicMask,0,0 Canvas1.Refresh end if
Dim f as FolderItem Dim kind As Integer // 0=1024x1024, 1=512x512, 2=512x512, 3=256x256, 4=256x256, 5=128x128, 6=256x256, 7=64x64 // 8=128x128, 9=128x128, 10=64x64, 11=32x32, 12=32x32, 13=32x32, 14=16x16 kind = 14 f = SelectFolder // get Folder if f <> nil then Dim str As String str=ReplaceAll(f.ShellPath,"\","") pPicImage=PluginModule1.getIconImage(str,kind) // Plug-in pPicMask=PluginModule1.getIconImage(str,kind+100) // Plug-in if pPic<>nil then pPic=nil end if pPic=new Picture(pPicImage.Width,pPicImage.Height,32) pPic.Graphics.DrawPicture pPicImage,0,0 pPic.Mask.Graphics.DrawPicture pPicMask,0,0 Canvas1.Refresh end if
- 実行する。
- PushuButton1または2を押す。ファイルまたはフォルダを指定してアイコン画像が表示されればOK。
(必要なら、ポップアップメニュー等でkindを実行時に指定できるようにして下さい。)
おわりに
Carbon版と同等のAPIがあれば、狙ったサイズのアイコンがあるかないかが分かり、なければより大きなサイズから(自分で)リサイズする、といったことができそうなので、もう少し調べてみるつもりです、
また、アイコンの種類を指定して取得する方法についても、調べてみたいと思っています。
注)上述のサンプルはあくまで動作確認用で、配布を前提としたものではありません。(配布用とするにはいくつかの配慮が必要なようです。)
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):High Resolution Guidelines for OS X: Optimizing for High Resolution
参考サイト(2):Hys.LOG : MacのRetina対応アイコン
参考サイト(3):CGDataProviderRef provider= CGDataProviderCreateWithData(NULL (UInt8*)data, byte - Pastebin.com
参考サイト(4):CoreGraphicsでハマる - 定食屋おろポン
更新履歴
2014.09.06 参考サイト(4)と関連する記述を追加
2014.09.01 参考サイト(3)と関連する記述を追加
2014.08.30 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]