ホームページ開発ツール>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に関連しては、以下の点に注意する必要があります。
  1. Xojo本体は画像のアルファチャンネル(マスクとして利用)に対応しているが、プラグイン側のREALpictureはアルファ値を持てない(ヘッダを調べてみたが、それらしいものを見つけられなかった)ので、Carbon版と同じく、アイコンとマスクを別々に取得して、Xojo側で合成する。
  2. ピクセルごとのRGBの並びが異なるため、並べ替える。(cocoa = RGBA , Xojo = kRBPixelXRGB32, // 4 bytes/pixel: Unused, Red, Green, Blue
                                         kRBPixelBGRX32 // 4 bytes/pixel: Blue, Green, Red, Unused )
    (CocoaのRGB値は、厳密に扱う場合は配慮が必要。詳細は、例えばこちらのサイトを参照。)
 これらを踏まえた、処理の流れは以下の通りです。
  1. 入力ファイルパスを元に、iconForFile:メソッドにより、NSImageを取得
  2. NSImageのTIFFRepresentationメソッドと、NSBitmapImageRepのimageRepWithData:メソッドを用いて、NSBitmapImageRepを取得
  3. NSBitmapImageRepのbitmapDataメソッドを用いて、rawデータを取得
  4. rgbの並べ替え(アイコン)、または、アルファ値をrgbにコピー(マスク)処理
  5. REALBuildPictureFromBufferメソッドにより、REALpicture形式に変換して返す

 まずデバッグ用プロジェクトを作る

 例によって、デスクトップアプリケーション用プロジェクトを作ってデバッグ用としました。(上述のTIFFRepresentationの中身も、これで確認しました。)
 今回は、共通部分が多く、rawからimageを復元する部分のみがアプリ固有となっています。(以下は抜粋。)
- (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を配置して、描画している
}
注1)フルバージョンの実装部はこちら。ヘッダはこちら
注2)別途、ウィンドウ上にButton1個とImage Well1個を用意して接続する。
注3)rawからimageを復元するコードは、以下の参考サイトの内容を再利用させて頂きました。

 参考サイト(3):CGDataProviderRef provider= CGDataProviderCreateWithData(NULL (UInt8*)data, byte - Pastebin.com


 アイコン画像を取得するプラグインを作る(実装編)

 アプリケーションによるテストが一段落したら、プラグイン固有の処理に切り替えます。

 プラグイン作成手順は前回と同じですので、そちらを参照して下さい。

 ソースコードはこちら
注1)kindの100番台をマスクに割り当てていますが、この辺は好みのやり方でいいと思います。

 動作テストする

  1. Xojoを起動する。
  2. Window1のプロパティに以下を追加。
    pPic As Picture
    pPicImage As Picture
    pPicMask As Picture
    
  3. 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
    
  4. 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
    
  5. 実行する。
  6. 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]