ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでアイコンを取得してみる
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでアイコンを取得してみる
はじめに
以下は、Xojo Cocoaビルドについての話題です。
アイコンの取得は以前、プラグインで行う方法を模索してみましたが、Declareの方がよりお手軽なので、試してみました。
なお検証には、Xojo 2016 Release 3を用いています。(Mac mini mid 2010 + macOS 10.13.4 High Sierra)
注)Retinaディスプレイ上での挙動については、環境がないのでここでは触れていません。また、XojoにもHiDPI Support機能がありますが、これも同じ理由でノータッチです。
方針
アイコン画像の取得には、アプリケーションを指定して抜き出す方法や、icnsファイルを指定する方法等がありますが、今回は、icnsファイルからにしてみます。
任意のicnsファイルを指定して取得するようにしてもいいのですが、ここではOS標準のアイコンを、システムファイルから取得することにします。
OS標準のアイコンの在り処は、以下の記事で示されています。
参考サイト(1):OS Xのシステム標準の画像やアイコンはどこに入っている?(その2)|Mac - 週刊アスキー
取得の方法としては、以前プラグインでも行なった、NSWorkspaceクラスのiconForFile:メソッドとTIFFRepresentationを利用します。
TIFFデータ取得後は、これもこちらでやったように、一度PNGに変換し、そこからXpjoのPictureに変換する方式をとります。
icnsファイルに含めるアイコン画像の種別は、以前にも参照させて頂いたサイトにある通り、10種類です。
ですが、上述のOS標準アイコンをプレビューで表示してみると、多くは8種類(16x16(72dpi)と32x32(72dpi)が抜け)ですが、他に1〜4種類程度のものも見られます。
このこともあって、ここではサイズを指定するのではなく、含まれるアイコンをリストアップする方式にします。
なお、XojoのPictureは、DPI値を反映するため、144DPIの画像は画面上では半分の大きさで表示されます。
以上を踏まえ、仕様は以下の通りとしました。
- 指定したフォルダパスに含まれる全てのicnsファイルをリスト表示する(パスの初期値はOS標準のアイコンフォルダとしておく)
- ファイルリストから任意のファイルをクリックすると、そのファイル内のサイズ別アイコンをリスト表示する
- アイコンリストから任意のサイズをクリックすると、そのアイコン画像を表示する
- アイコン画像は、PNG形式でファイル出力可能とする
Xojoでの実装
【ソースコードのコピー&ペーストについて】
ソースコード(グレー背景部分の全文)をコピーし、指定のウィンドウ/クラスにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
ただし、この方法は、メソッドでは問題ないようですが、イベント/アクション/プロパティでは不安定?なので、ペーストできない場合は、各項目のカッコ内を適用して下さい。
実行してみたところ、アイコン画像の取得と表示が機能することを確認しました。
- Xojoで新規プロジェクトを作成
- ファイルタイプグループをプロジェクトに追加(Name:FileTypes1)し、一般的なファイルタイプの追加の中から、「image/png」を追加。
- Window1に。BevelButton(Name:BevelButton1)、Canvas(Name:Canvas1)、Listbox2個(Name:Listbox1, ColumnCount:2、Name:Listbox2, ColumnCount:3)、PushButton(Name:PushButton1)、TextField(Name:TextField1)を追加
- 以下をBevelButton1にペースト(できなければ、Sub - Endの間をActionイベントに記述)
Sub Action() Handles Action Dim dlg As New SaveAsDialog Dim f As FolderItem // ダイアログ表示 dlg.Filter=FileTypes1.Png f=dlg.ShowModalWithin(Self) if f=nil then return end if // ファイル出力 pic.Save(f,Picture.SaveAsPNG) End Sub
- 以下をCanvas1にペースト(できなければ、Sub - Endの間をPaintイベントに記述)
Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint if pic<>nil then g.DrawPicture pic,0,0 end if End Sub
- 以下をListbox1にペースト(できなければ、Sub - Endの間をChangeイベントに記述)
Sub Change() Handles Change fItem=GetFolderItem(me.Cell(me.ListIndex,1),3) GetIconInfo() End Sub
- 以下をListbox2にペースト(できなければ、Sub - Endの間をChangeイベントに記述)
Sub Change() Handles Change if me.ListIndex>=0 then // Pictureの取得 pic=GetIconPicture(me.ListIndex) // Pictureの諸元を表示 Label1.Text="width x height = "+str(pic.Width)+" x "+str(pic.Height)+" : resolution = "+str(pic.VerticalResolution)+" x "+str(pic.HorizontalResolution) // Pictureの再描画 Canvas1.Refresh end if End Sub
- 以下をPushButton1にペースト(できなければ、Sub - Endの間をActionイベントに記述)
Sub Action() Handles Action GetList() End Sub
- 以下をWindow1にペースト(できなければ、Sub - Endの間をOpenイベントに記述)
Sub Open() Handles Open // OS標準アイコンを納めたフォルダへのパス TextField1.Text="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/" // ファイルリストの生成 GetList() End Sub
- 以下をWindow1にペースト
Protected Sub ExtractBodyAndExtn(st As String, byRef body As String, byRef ext As String) Dim i, cnt As Integer // ピリオドの数をカウント cnt=CountFields(st,".") // ピリオドがなければ入力をbody、拡張子を空として返す if cnt<=1 then body=st ext="" return end if // 最後のピリオド以降を拡張子とみなす ext=NthField(st,".",cnt) // 最後のピリオド以前をbodyとみなす body="" for i=1 to CountFields(st,".")-2 body=body+NthField(st,".",i)+"." next body=body+NthField(st,".",cnt-1) End Sub
- 以下をWindow1にペースト
Protected Sub GetIconInfo() // ファイルが取得できなければ戻る if fItem=nil or fItem.Exists=false then return end if // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // 指定された.icnsファイルへのパスからイメージを取得 Dim image As Ptr = NSClassFromString("NSImage") Declare Function alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr image = alloc(image) Declare Function initWithContentsOfFile Lib "Cocoa" Selector "initWithContentsOfFile:" (receiver As Ptr, path As CFStringRef) As Ptr image = initWithContentsOfFile(image, fItem.NativePath) // TIFF形式でアイコン群を取得 Declare Function TIFFRepresentation Lib "Cocoa" Selector "TIFFRepresentation" (receiver As Ptr) As Ptr Dim pnt1 As Ptr = TIFFRepresentation(image) Dim bitmapImages As Ptr = NSClassFromString("NSBitmapImageRep") Declare Function imageRepsWithData Lib "Cocoa" Selector "imageRepsWithData:" (receiver As Ptr, path As Ptr) As Ptr bitmapImages = imageRepsWithData(bitmapImages, pnt1) // 群の要素数を取得 Declare Function myCount Lib "Cocoa" Selector "count" (receiver As Ptr) As Integer Dim cnt As Integer = myCount(bitmapImages) // 要素のループ Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, path As Integer) As Ptr Declare Function mySize Lib "Cocoa" Selector "size" (receiver As Ptr) As NSSize Declare Function pixelsWide Lib "Cocoa" Selector "pixelsWide" (receiver As Ptr) As Integer Declare Function pixelsHigh Lib "Cocoa" Selector "pixelsHigh" (receiver As Ptr) As Integer Declare Function bytesPerRow Lib "Cocoa" Selector "bytesPerRow" (receiver As Ptr) As Integer Dim bitmapImageRep As Ptr Dim k, bpr, ww, hh As Integer Dim iconSize As NSSize Listbox2.DeleteAllRows // まず全行削除 for k=0 to cnt-1 // 要素を取得 bitmapImageRep = objectAtIndex(bitmapImages, k) // 情報を取得 iconSize = mySize(bitmapImageRep) ww = pixelsWide(bitmapImageRep) hh = pixelsHigh(bitmapImageRep) bpr = bytesPerRow(bitmapImageRep) // 情報を表示 Listbox2.AddRow str(iconSize.Width)+" x "+str(iconSize.Height) ListBox2.Cell(ListBox2.ListCount-1,1)=str(ww)+" x "+str(hh) ListBox2.Cell(ListBox2.ListCount-1,2)=str(bpr) next End Sub
- 以下をWindow1にペースト
注)1バイトずつコピーの代替法については、こちらを参照。(2019.03.26)Protected Function GetIconPicture(idx As Integer) as Picture // ファイルが取得できなければnilを返す if fItem=nil or fItem.Exists=false then return nil end if // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // 指定された.icnsファイルへのパスからイメージを取得 Dim image As Ptr = NSClassFromString("NSImage") Declare Function alloc Lib "Cocoa" Selector "alloc" (receiver As Ptr) As Ptr image = alloc(image) Declare Function initWithContentsOfFile Lib "Cocoa" Selector "initWithContentsOfFile:" (receiver As Ptr, path As CFStringRef) As Ptr image = initWithContentsOfFile(image, fItem.NativePath) // TIFF形式でアイコン群を取得 Declare Function TIFFRepresentation Lib "Cocoa" Selector "TIFFRepresentation" (receiver As Ptr) As Ptr Dim pnt1 As Ptr = TIFFRepresentation(image) Dim bitmapImages As Ptr = NSClassFromString("NSBitmapImageRep") Declare Function imageRepsWithData Lib "Cocoa" Selector "imageRepsWithData:" (receiver As Ptr, path As Ptr) As Ptr bitmapImages = imageRepsWithData(bitmapImages, pnt1) // 指定されたサイズのアイコンを取得 Dim bitmapImageRep As Ptr Declare Function objectAtIndex Lib "Cocoa" Selector "objectAtIndex:" (receiver As Ptr, path As Integer) As Ptr bitmapImageRep = objectAtIndex(bitmapImages, idx) // falseをNSNumber形式に変換 Dim numb As Ptr = NSClassFromString("NSNumber") Declare Function numberWithBool Lib "Cocoa" Selector "numberWithBool:" (receiver As Ptr, path As Boolean) As Ptr numb = numberWithBool(numb, false) // PNG用オプションのセット Dim dict As Ptr = NSClassFromString("NSDictionary") Declare Function dictionaryWithObject Lib "Cocoa" Selector "dictionaryWithObject:forKey:" (receiver As Ptr, objt As Ptr, key As CFStringRef) As Ptr dict = dictionaryWithObject(dict, numb, "NSImageInterlaced") // PNG出力(NSPNGFileType = 4) Declare Function representationUsingType Lib "Cocoa" Selector "representationUsingType:properties:" (receiver As Ptr, type As Integer, prop As Ptr) As Ptr Dim pngData As Ptr = representationUsingType(bitmapImageRep, 4, dict) // clean up Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr) release(image) // PNGデータの長さを取得 Declare Function length Lib "Cocoa" Selector "length" (receiver As Ptr) As Integer Dim lng As Integer = length(pngData) // バイト列の抽出 Declare Function bytes Lib "Cocoa" Selector "bytes" (receiver As Ptr) As Ptr Dim bstream As Ptr = bytes(pngData) // バイト列をMemoryBlockに1バイトずつコピー Dim i As Integer Dim mb As new MemoryBlock(lng) for i=0 to lng-1 mb.Byte(i) = bstream.Byte(i) next // MemoryBlockを使ってPictureを生成 Dim pic As Picture = Picture.FromData(mb) return pic End Function
- 以下をWindow1にペースト
Protected Sub GetList() Dim path As String = TextField1.Text Dim f As FolderItem = GetFolderItem(path,3) // 指定されたフォルダ内の全ファイルをチェック Dim body, extn As String Dim i As Integer for i=1 to f.Count // ファイル名を本体名と拡張子に分解 ExtractBodyAndExtn(f.Item(i).Name,body,extn) if extn="icns" then // 拡張子がicnsなら ListBox1.AddRow body // 本体名を登録 ListBox1.Cell(ListBox1.ListCount-1,1)=f.Item(i).NativePath // ファイルパスを登録 end if next End Sub
- 以下をWindow1にペースト(できなければプロパティに、名前:fItem、データ型:FolderItem、を追加)
Protected Property fItem as FolderItem
- 以下をWindow1にペースト(できなければプロパティに、名前:pic、データ型:Picture、を追加)
Protected Property pic as Picture
- 他に、NSMakeSize(メソッド)、NSSize(構造体)が必要ですが、それらはmacoslibからコピーさせて頂きました。(上記Window1にコピーする。)
おわりに
サイズを指定して取得する場合は、(プラグインの時の繰り返しになりますが)所望のサイズがあればそれを、なければ直近または最大サイズを縮小(大きなものがなければ拡大)、になりそうです。
また、場合によってはDPI値を変更する方法も有効かと思われます。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):OS Xのシステム標準の画像やアイコンはどこに入っている?(その2)|Mac - 週刊アスキー
参考サイト(2):Hys.LOG : MacのRetina対応アイコン
更新履歴
2019.03.26 1バイトずつコピーの代替法へのリンクを追加
2018.05.31 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]