ホームページ開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでアプリアイコンを作る

 Xojo / Real Studio Trial and Error

CocoaのDeclareでアプリアイコンを作る

目次
 はじめに

 以下は、Xojo Cocoaビルドについての話題です。

 アプリケーションのアイコンを簡易的に作成する方法について、調べてみました。

 なお検証には、Xojo 2022 Release 4.1を用いています。(Mac mini 2018 + macOS 15.2 Sequoia)


 方針

 アプリケーションのアイコン作成については、アップルがドキュメントを公開しています。
 これらから、どう作ればいいかは分かるのですが、簡単に流用できるベース画像(または環境)というのは、特にはなさそうです。

 参考サイト(1):アプリアイコン | Apple Developer Documentation
 参考サイト(2):Apple Design Resources - Apple Developer(テンプレートのダウンロード)

 一般公開するアプリで、凝ったアイコンにしたいのであれば、グラフィックソフトを駆使する等して、ガイドラインに準拠したものを一から作り上げるのもありでしょう。
 ですが、自分用のツールのように、デフォルトのアイコンでは物足らないが、手間と時間を掛けて作る程ではない、といった、厳密さよりは簡便さが優先するケースもある、という想定で考えてみました。

 基本となるのは、以前こちらでやったものの例2(以下、前回)です。
 アイコンに必要な要素(角丸(注1)のベース画像/ドロップシャドウ/グラデーション)は全て揃っています。
 異なるのはサイズで、前回は128x128pixelでしたが、これを1024x1024pixelとします。
注1:アップルのテンプレート画像では、角丸にスムースカーブが採用されているようだ。
CALayerでは、cornerCurveプロパティにcontinuousをセットすると、適用される。(注:macOS 10.15 Catalina以降)
 参考サイト(3):【100回作って100回壊す?】Mac App Icon制作に初挑戦!半年間の軌跡と芽生えたデザインのスタンス|Goodpatch Blog グッドパッチブログ
 参考サイト(4):ios - smooth rounded corners in swift - Stack Overflow
 ただし、単色のベタ塗りだけでは表現力に乏しいので、(1) 2分割(縦または横)、(2) 外部画像を取り込む、機能も追加します。
 更に、文字も書けるようにし、(プレビューのように)縁も付けられるようにします。
 各要素のパラメーターはカスタマイズ可能とし、結果を見ながら調整できるようにします。

 外部画像は、作成時点で角丸にするのは面倒なので、矩形で作って、取り込み後に角をマスクするようにします。
 サイズは1024x1024pixelで作成しますが、上下左右100pixelは余白として扱われるので、表示はされません。

 前回は、NSViewだけ作ればよかったのですが、今回はPNG画像ファイルが終点なので、(ありがちですが)複数の段階を経ることになります。
 NSView → NSBitmapImageRep → CGBitmapContext → CGImage → NSImage → TIFF → NSBitmapImageRep → NSData → PNG
(注:2番目と7番目は、途中にリサイズのステップが含まれているため、中身は同一ではない。)

 PNG画像ファイルは、アップルの基準を満たすよう、以下の10種類とし、フォルダーにまとめてひとまず出力します。
(「App.iconset」という名前にしておくのですが、iconsetは予約語、Appの理由は後述。)
 小アイコンは1024x1024pixel画像を機械的に縮小します。サイズに合わせて最適化せよ、というガイドラインから外れる場合も想定されますが、ここではよしとします。
icon_512x512@2x.png
icon_512x512.png
icon_256x256@2x.png
icon_256x256.png
icon_128x128@2x.png
icon_128x128.png
icon_32x32@2x.png
icon_32x32.png
icon_16x16@2x.png
icon_16x16.png
 このうち@マークの付いているものは解像度が144dpiで、これらの画素数は2倍になります。ないものは72dpiで、ファイル名中の数値と同一になります。
解像度は、NSImageあたりがプロパティでも持っているのかと思ったら、そうではないようで、NSImageのサイズと入力元の画像のサイズが異なると、解像度が変化する、ということが実験的に確認できている。
(例えば、CGBitmapContext作成時の幅/高さを1024、NSImage作成時の幅/高さを512にすると、解像度が144dpiになる。両方512にすると72dpiになる。)
 参考サイト(5):macos - NSImage size not real size with some pictures? - Stack Overflow
 最終成果品はicnsファイルになります。
 Xojoでは、アイコンファイル名は「App.icns」に固定されているので、ここで作るものもそれに合わせます。(iconsetフォルダー名をAppとしておくのもこのため。)
 作成には、コマンドラインツールのiconutilを用います。内部からShellを使って起動します。

 なお、作成したアイコンの使い方ですが、Xojo標準のアイコン設定(AppのIconエディター)は使えず、App.icnsをアプリのResourcesフォルダーに直接コピーする必要があります。
(毎回コピーするのは煩わしいので、Build SettingsでCopyFilesを追加し、App.icnsをドロップ後、DestinationをResources Folderにしておけば簡単です。)

 以上を踏まえ、(残りの)仕様は以下の通りとしました。

 Xojoでの実装
注:以下の実装では、一部Xojo 2022r4.1ではDeprecatedな機能が使われています。必要なら推奨される機能に置き換えて下さい。
【ソースコードのコピー&ペーストについて】
・ソースコード(グレー背景部分の全文)をコピーし、指定のオブジェクトにペーストすると、(新規作成して名前等を個別にコピー&ペーストしなくても)復元されます。
・ペーストはオブジェクトに行って下さい。オブジェクト内のEvent Handlers/Methods/Properties等にペーストしても、うまくいかない場合があります。
・それでもペーストできない場合は、各項目のカッコ内を適用して下さい。
  1. Xojoで新規プロジェクトを作成(Window1のWidthを1375、Heightを1044に変更)
  2. Window1に、DesktopButton3個、DesktopCheckBox1個、DesktopComboBox1個、DesktopGroupBox2個、DesktopLabel21個、DesktopSeparator1個、DesktopTextField13個、ClrCanvas4個(41項参照)、myRadioButton5個(47項参照)を置く。(名前と機能の関係は25項、配置はスクリーンショット参照)
  3. ComboBox1のInitial Valueを「Helvetica, Hiragino Kaku Gothic ProN W3, Hiragino Kaku Gothic ProN W6, Hiragino Mincho ProN W3, Hiragino Mincho ProN W6」とする(カンマの代わりに改行で区切る。必要なら追加して下さい。)
  4. 以下をButton1(Caption:Set)にペースト(できなければ、Sub - Endの間をPressedイベントに記述)
    Sub Pressed() Handles Pressed
      SetIconView(kLeave)
    End Sub
    
  5. 以下をButton2(Caption:Set Default)にペースト(できなければ、Sub - Endの間をPressedイベントに記述)
    Sub Pressed() Handles Pressed
      SetIconView(kReset)
    End Sub
    
  6. 以下をButton3(Caption:Export)にペースト(できなければ、Sub - Endの間をPressedイベントに記述)
    Sub Pressed() Handles Pressed
      // icnsファイル生成
      MakeIcns()
    End Sub
    
  7. 以下をmyRadioButton1(Caption:Internal)にペースト(できなければ、Sub - Endの間をValueChangedイベントに記述)
    Sub ValueChanged() Handles ValueChanged
      SetIconView(kLeave)
    End Sub
    
  8. 以下をmyRadioButton2(Caption:Dropped File)にペースト(できなければ、Sub - Endの間をValueChangedイベントに記述)
    Sub ValueChanged() Handles ValueChanged
      SetIconView(kLeave)
    End Sub
    
  9. 以下をWindow1にペースト(できなければ、Sub - Endの間をDropObjectイベントに記述)
    Sub DropObject(obj As DragItem, action As DragItem.Types) Handles DropObject
      If obj.FolderItemAvailable Then
        pPath = obj.FolderItem.NativePath
      End If
      
      // アイコンビュー生成
      SetIconView(kLeave)
    End Sub
    
  10. 以下をWindow1にペースト(できなければ、Sub - Endの間をMenuBarSelectedイベントに記述)
    Sub MenuBarSelected() Handles MenuBarSelected
      FileOpen.Enabled=true
      FileSave.Enabled=false
      FileSaveAs.Enabled=true
      if pFIparam<>nil then
        FileSave.Enabled=true
      end if
    End Sub
    
  11. 以下をWindow1にペースト(できなければ、Sub - Endの間をOpeningイベントに記述)
    Sub Opening() Handles Opening
      me.Top=me.Top+3
      me.AcceptFileDrop("image/png")
      
      // NSView。引数は順に、生成したviewのインスタンス、配置するウィンドウ、位置/サイズ
      Dim d As NSViewCanvas = new NSViewCanvas(pViewInst, self, NSMakeRect(10, 10, 1024, 1024))
      
      // アイコンビュー生成(初期化)
      SetIconView(kReset)
    End Sub
    
  12. 以下をWindow1にペースト(できなければ、Sub - Endの間をMenu HandlersのFileOpenに記述)
    Function FileOpen() As Boolean
      LoadParam()
      SetIconView(kLeave)
      Return True
    End Function
    
  13. 以下をWindow1にペースト(できなければ、Sub - Endの間をMenu HandlersのFileSaveに記述)
    Function FileSave() As Boolean
      SaveParam(false)
      Return True
    End Function
    
  14. 以下をWindow1にペースト(できなければ、Sub - Endの間をMenu HandlersのFileSaveAsに記述)
    Function FileSaveAs() As Boolean
      SaveParam(true)
      Return True
    End Function
    
  15. 以下をWindow1にペースト
    Protected Sub GenMsgDlg(mes As String, expl As String)
      Dim d As New MessageDialog
      Dim b As MessageDialogButton
      
      d.Message = mes
      d.Explanation = expl
      
      b = d.ShowModal
    End Sub
    
  16. 以下をWindow1にペースト
    Protected Sub LoadParam()
      Var f As FolderItem = FolderItem.ShowOpenFileDialog("text/plain") // defined as a FileType
      If f = Nil Then
        return
      end if
      
      Var t As TextInputStream = TextInputStream.Open(f)
      t.Encoding = Encodings.UTF8
      
      Dim vv As String = t.ReadLine
      if vv="0" then
        myRadioButton1.Value = true
      else
        myRadioButton2.Value = true
      end if
      vv = t.ReadLine
      if vv="0" then
        myRadioButton3.Value=true
      elseif vv="1" then
        myRadioButton4.Value=true
      else
        myRadioButton5.Value=true
      end if
      TextFieldBY.Text = t.ReadLine
      ClrCanvas1.pClr = gClrItoC(Val(t.ReadLine))
      TextFieldBX.Text = t.ReadLine
      ClrCanvas2.pClr = gClrItoC(Val(t.ReadLine))
      
      Dim ss As String = t.ReadLine
      if ss="false" then
        CheckBox1.Value = false
      else
        CheckBox1.Value = true
      end if
      TextFieldBW.Text = t.ReadLine
      ClrCanvas3.pClr = gClrItoC(Val(t.ReadLine))
      
      TextFieldSO.Text = t.ReadLine
      TextFieldSR.Text = t.ReadLine
      TextFieldSP.Text = t.ReadLine
      
      TextFieldGS.Text = t.ReadLine
      TextFieldGE.Text = t.ReadLine
      TextFieldGO.Text = t.ReadLine
      
      TextFieldTX.Text = t.ReadLine
      TextFieldTY.Text = t.ReadLine
      ComboBox1.Text = t.ReadLine
      TextFieldTF.Text = t.ReadLine
      TextFieldTS.Text = t.ReadLine
      ClrCanvas4.pClr = gClrItoC(Val(t.ReadLine))
      
      pPath=t.ReadLine  // Dropped File Path
      
      t.Close
      
      ClrCanvas1.Refresh
      ClrCanvas2.Refresh
      ClrCanvas3.Refresh
      ClrCanvas4.Refresh
      
      pFIparam=f
    End Sub
    
    注)外部画像のパスが抜けていたので追加した。(2025.02.15)
  17. 以下をWindow1にペースト
    Protected Sub MakeIcns()
      // デスクトップにApp.iconsetフォルダーを取得
      Dim f As FolderItem = SpecialFolder.Desktop.Child("App.iconset")
      if f.Exists then  // 既に作成済なら、警告して戻る
        GenMsgDlg("App.iconsetが既に存在しています。","処理を中止します。")
        return
      end if
      // iconsetフォルダーの作成
      f.CreateAsFolder
      
      // iconsetフォルダー内に、各サイズの画像をpng形式で書き出し
      Dim sz, rs As Integer
      Dim st As String
      Dim f2 As FolderItem
      for i As Integer = 0 to 9
        
        select case i
        case 0
          sz=512
          rs=2
          st="icon_512x512@2x.png"
        case 1
          sz=512
          rs=1
          st="icon_512x512.png"
        case 2
          sz=256
          rs=2
          st="icon_256x256@2x.png"
        case 3
          sz=256
          rs=1
          st="icon_256x256.png"
        case 4
          sz=128
          rs=2
          st="icon_128x128@2x.png"
        case 5
          sz=128
          rs=1
          st="icon_128x128.png"
        case 6
          sz=32
          rs=2
          st="icon_32x32@2x.png"
        case 7
          sz=32
          rs=1
          st="icon_32x32.png"
        case 8
          sz=16
          rs=2
          st="icon_16x16@2x.png"
        case 9
          sz=16
          rs=1
          st="icon_16x16.png"
        end select
        
        f2=f.Child(st)
        SaveImage(pViewInst,sz,rs,f.NativePath+"/"+st)
        
      next
      
      // icns形式に変換
      MakeIcnsShell(f)
      
      GenMsgDlg("処理が終了しました。","")
    End Sub
    
  18. 以下をWindow1にペースト
    Protected Sub MakeIcnsShell(f As FolderItem)
      '$ iconutil -c icns ~/Pictures/hoge.iconset
      
      // Log確認用文字列生成
      Dim str1, str2, str3 As String
      str1="iconutil -c icns """
      str2=f.NativePath
      str3=""""
      
      // Shell生成
      Var s As Shell
      s = new Shell
      
      // Shell実行
      s.Execute(str1+str2+str3)
      if s.ExitCode = 0 then
        
      else
        MessageBox("Error code: " + s.ErrorCode.ToString)
      end if
    End Sub
    
  19. 以下をWindow1にペースト
    Protected Sub MakeIconViewGen(view As Ptr, shOffset As CGFloat, shRadius As CGFloat, shOpacity As Single, grStPnt As CGFloat, grEdPnt As CGFloat, grOpacity As Single, chCodx As CGFloat, chCody As CGFloat, chFname As String, chFsize As CGFloat, chText As String, chClr As Ptr, bsClr As Ptr, rimMode As Boolean, rimWth As Integer, rimClr As Ptr)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 共通項
      Dim white As Ptr = NSClassFromString("NSColor")
      Declare Function whiteColor Lib "Cocoa" Selector "whiteColor" (receiver As Ptr) As Ptr
      white = whiteColor(white)
      Declare Function myCGColor Lib "Cocoa" Selector "CGColor" (receiver As Ptr) As Ptr
      white = myCGColor(white)
      Dim black As Ptr = NSClassFromString("NSColor")
      Declare Function blackColor Lib "Cocoa" Selector "blackColor" (receiver As Ptr) As Ptr
      black = blackColor(black)
      black = myCGColor(black)
      Dim red As Ptr = NSClassFromString("NSColor")
      Declare Function redColor Lib "Cocoa" Selector "redColor" (receiver As Ptr) As Ptr
      red = redColor(red)
      red = myCGColor(red)
      
      // IconViewの生成
      Dim iconView As Ptr = NSClassFromString("NSView")
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      iconView = alloc(iconView)
      Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr
      iconView = initWithFrame(iconView, NSMakeRect(0, 0, 1024, 1024))
      Declare Sub setWantsLayer Lib "Cocoa" Selector "setWantsLayer:" (receiver As Ptr, val As Boolean)
      setWantsLayer(iconView, true)
      
      // ベースとなる角丸矩形(縁ありの場合は縁用になる)
      Dim baselayer As Ptr = NSClassFromString("CALayer")
      Declare Function layer Lib "Cocoa" Selector "layer" (receiver As Ptr) As Ptr
      baselayer = layer(baselayer)
      Declare Sub setBackgroundColor Lib "Cocoa" Selector "setBackgroundColor:" (receiver As Ptr, val As Ptr)
      if rimMode then  // 縁ありの場合は縁カラーをセット、縁なしの場合は背景色をセット
        setBackgroundColor(baselayer, myCGColor(rimClr))
      else
        setBackgroundColor(baselayer, myCGColor(bsClr))
      end if
      Declare Sub setFrame Lib "Cocoa" Selector "setFrame:" (receiver As Ptr, rect As CGRect)
      setFrame(baselayer, CGRectMake(100, 100, 824, 824))
      Declare Sub setCornerRadius Lib "Cocoa" Selector "setCornerRadius:" (receiver As Ptr, val As CGFloat)
      setCornerRadius(baselayer, 184.0)
      Declare Sub setCornerCurve Lib "Cocoa" Selector "setCornerCurve:" (receiver As Ptr, val As CFStringRef)
      setCornerCurve(baselayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      // 以下、シャドウ
      Declare Sub setMasksToBounds Lib "Cocoa" Selector "setMasksToBounds:" (receiver As Ptr, val As Boolean)
      setMasksToBounds(baselayer, false)
      Declare Sub setShadowOffset Lib "Cocoa" Selector "setShadowOffset:" (receiver As Ptr, rect As CGSize)
      setShadowOffset(baselayer, CGSizeMake(0.0, shOffset))
      Declare Sub setShadowOpacity Lib "Cocoa" Selector "setShadowOpacity:" (receiver As Ptr, val As Single)
      setShadowOpacity(baselayer, shOpacity)
      Declare Sub setShadowColor Lib "Cocoa" Selector "setShadowColor:" (receiver As Ptr, val As Ptr)
      setShadowColor(baselayer, black)
      Declare Sub setShadowRadius Lib "Cocoa" Selector "setShadowRadius:" (receiver As Ptr, val As CGFloat)
      setShadowRadius(baselayer, shRadius)
      Dim layer1 As Ptr = layer(iconView)
      Declare Sub addSublayer Lib "Cocoa" Selector "addSublayer:" (receiver As Ptr, layer As Ptr)
      addSublayer(layer1, baselayer)
      
      // 縁ありの場合(baselayerを縁としているので、以下をイメージ領域とする)
      if rimMode then
        // イメージ描画用の矩形を角丸にするマスク(縁を除いた大きさ)
        Dim masklayer As Ptr = NSClassFromString("CALayer")
        masklayer = layer(masklayer)
        setBackgroundColor(masklayer, black)
        setFrame(masklayer, CGRectMake(100+rimWth, 100+rimWth, 824-rimWth*2, 824-rimWth*2))
        setCornerRadius(masklayer, 184-rimWth*0.65)  // 現物合わせの調整値
        setCornerCurve(masklayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
        // ベースとなる矩形
        Dim innerlayer As Ptr = NSClassFromString("CALayer")
        innerlayer = layer(innerlayer)
        setBackgroundColor(innerlayer, myCGColor(bsClr))
        setFrame(innerlayer, CGRectMake(0, 0, 1024, 1024))
        // splitlayerにmasklayerを追加
        Declare Sub setMask Lib "Cocoa" Selector "setMask:" (receiver As Ptr, msk As Ptr)
        setMask(innerlayer, masklayer)
        // IconViewのlayerにimagelayerを追加
        addSublayer(layer1, innerlayer)
      end if
      
      // テキスト
      if chText<>"" then
        Dim textLayer As Ptr = NSClassFromString("CATextLayer")
        textLayer = layer(textLayer)
        setFrame(textLayer, CGRectMake(chCodx, chCody, 824, chFsize))
        Declare Sub setString Lib "Cocoa" Selector "setString:" (receiver As Ptr, val As CFStringRef)
        setString(textLayer, chText)
        Declare Sub setForegroundColor Lib "Cocoa" Selector "setForegroundColor:" (receiver As Ptr, val As Ptr)
        setForegroundColor(textLayer, chClr)
        Dim font As Ptr = NSClassFromString("NSFont")
        Declare Function systemFontOfSize Lib "Cocoa" Selector "fontWithName:size:" (receiver As Ptr, name As CFStringRef, size As CGFloat) As Ptr
        font = systemFontOfSize(font, chFname, chFsize)
        Declare Sub setFont Lib "Cocoa" Selector "setFont:" (receiver As Ptr, val As Ptr)
        setFont(textLayer, font)
        Declare Sub setFontSize Lib "Cocoa" Selector "setFontSize:" (receiver As Ptr, val As CGFloat)
        setFontSize(textLayer, chFsize)
        Declare Sub setAlignmentMode Lib "Cocoa" Selector "setAlignmentMode:" (receiver As Ptr, val As CFStringRef)
        setAlignmentMode(textLayer, "center")
        // IconViewのlayerにtextLayerを追加
        addSublayer(layer1, textLayer)
      end if
      
      // 全面にかかる透明度の高いグラデーション
      Dim gradientLayer As Ptr = NSClassFromString("CAGradientLayer")
      gradientLayer = layer(gradientLayer)
      setCornerRadius(gradientLayer, 184.0)
      setCornerCurve(gradientLayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      setFrame(gradientLayer, CGRectMake(100, 100, 824, 824))
      Dim colors As Ptr = NSClassFromString("NSMutableArray")
      Declare Function myArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr
      colors = myArray(colors)
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
      addObject(colors, black)
      addObject(colors, white)
      Declare Sub setColors Lib "Cocoa" Selector "setColors:" (receiver As Ptr, val As Ptr)
      setColors(gradientLayer, colors)
      Declare Sub setStartPoint Lib "Cocoa" Selector "setStartPoint:" (receiver As Ptr, val As CGPoint)
      setStartPoint(gradientLayer, CGPointMake(0.0, grStPnt))
      Declare Sub setEndPoint Lib "Cocoa" Selector "setEndPoint:" (receiver As Ptr, val As CGPoint)
      setEndPoint(gradientLayer, CGPointMake(0.0, grEdPnt))
      Declare Sub setOpacity Lib "Cocoa" Selector "setOpacity:" (receiver As Ptr, val As Single)
      setOpacity(gradientLayer, grOpacity)
      // IconViewのlayerにgradientLayerを追加
      addSublayer(layer1, gradientLayer)
      
      // まず、以前のIconViewを削除
      RemoveIconView(view)
      
      // IconViewを表示用ビューに追加
      Declare Sub addSubview Lib "Cocoa" selector "addSubview:" (class_id As Ptr, view As Ptr)
      addSubview(view, iconView)
      
      // clean up
      Declare Sub release Lib "Cocoa" selector "release" (class_id As Ptr)
      release(iconView)
      
      // 表示用ビューを再描画
      Declare Sub display Lib "Cocoa" selector "display" (class_id As Ptr)
      display(view)
    End Sub
    
    注)gradientLayerのsetFrameがNSRectになっていたのでCGRectに修正した。(2025.02.20)
  20. 以下をWindow1にペースト
    Protected Sub MakeIconViewGenSplit(view As Ptr, shOffset As CGFloat, shRadius As CGFloat, shOpacity As Single, grStPnt As CGFloat, grEdPnt As CGFloat, grOpacity As Single, chCodx As CGFloat, chCody As CGFloat, chFname As String, chFsize As CGFloat, chText As String, chClr As Ptr, bsClr1 As Ptr, bsClr2 As Ptr, bsX As Integer, bsY As Integer, mode As Integer, rimMode As Boolean, rimWth As Integer, rimClr As Ptr)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 共通項
      Declare Function myCGColor Lib "Cocoa" Selector "CGColor" (receiver As Ptr) As Ptr
      Dim white As Ptr = NSClassFromString("NSColor")
      Declare Function whiteColor Lib "Cocoa" Selector "whiteColor" (receiver As Ptr) As Ptr
      white = whiteColor(white)
      white = myCGColor(white)
      Dim black As Ptr = NSClassFromString("NSColor")
      Declare Function blackColor Lib "Cocoa" Selector "blackColor" (receiver As Ptr) As Ptr
      black = blackColor(black)
      black = myCGColor(black)
      Dim red As Ptr = NSClassFromString("NSColor")
      Declare Function redColor Lib "Cocoa" Selector "redColor" (receiver As Ptr) As Ptr
      red = redColor(red)
      red = myCGColor(red)
      Dim clear As Ptr = NSClassFromString("NSColor")
      Declare Function clearColor Lib "Cocoa" Selector "clearColor" (receiver As Ptr) As Ptr
      clear = clearColor(clear)
      clear = myCGColor(clear)
      
      // 縁無し時は、縁幅とカラーをリセット
      Dim bgcolor As Ptr = myCGColor(rimClr)
      if rimMode=false then
        rimWth=0
        bgcolor=white
      end if
      
      // IconViewの生成
      Dim iconView As Ptr = NSClassFromString("NSView")
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      iconView = alloc(iconView)
      Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr
      iconView = initWithFrame(iconView, NSMakeRect(0, 0, 1024, 1024))
      Declare Sub setWantsLayer Lib "Cocoa" Selector "setWantsLayer:" (receiver As Ptr, val As Boolean)
      setWantsLayer(iconView, true)
      
      // シャドウ付加用の角丸矩形(縁なし時は白、縁あり時は縁カラー)
      Dim shadowlayer As Ptr = NSClassFromString("CALayer")
      Declare Function layer Lib "Cocoa" Selector "layer" (receiver As Ptr) As Ptr
      shadowlayer = layer(shadowlayer)
      Declare Sub setBackgroundColor Lib "Cocoa" Selector "setBackgroundColor:" (receiver As Ptr, val As Ptr)
      setBackgroundColor(shadowlayer, bgcolor)
      Declare Sub setFrame Lib "Cocoa" Selector "setFrame:" (receiver As Ptr, rect As CGRect)
      setFrame(shadowlayer, CGRectMake(100, 100, 824, 824))
      Declare Sub setCornerRadius Lib "Cocoa" Selector "setCornerRadius:" (receiver As Ptr, val As CGFloat)
      setCornerRadius(shadowlayer, 184.0)
      Declare Sub setCornerCurve Lib "Cocoa" Selector "setCornerCurve:" (receiver As Ptr, val As CFStringRef)
      setCornerCurve(shadowlayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      
      // 以下、シャドウ
      Declare Sub setMasksToBounds Lib "Cocoa" Selector "setMasksToBounds:" (receiver As Ptr, val As Boolean)
      setMasksToBounds(shadowlayer, false)
      Declare Sub setShadowOffset Lib "Cocoa" Selector "setShadowOffset:" (receiver As Ptr, rect As CGSize)
      setShadowOffset(shadowlayer, CGSizeMake(0.0, shOffset))
      Declare Sub setShadowOpacity Lib "Cocoa" Selector "setShadowOpacity:" (receiver As Ptr, val As Single)
      setShadowOpacity(shadowlayer, shOpacity)
      Declare Sub setShadowColor Lib "Cocoa" Selector "setShadowColor:" (receiver As Ptr, val As Ptr)
      setShadowColor(shadowlayer, black)
      Declare Sub setShadowRadius Lib "Cocoa" Selector "setShadowRadius:" (receiver As Ptr, val As CGFloat)
      setShadowRadius(shadowlayer, shRadius)
      // iconViewのlayerを取得
      Dim layer1 As Ptr = layer(iconView)
      // IconViewのlayerにshadowlayerを追加
      Declare Sub addSublayer Lib "Cocoa" Selector "addSublayer:" (receiver As Ptr, layer As Ptr)
      addSublayer(layer1, shadowlayer)
      
      // イメージ描画用の矩形を角丸にするマスク
      Dim masklayer1 As Ptr = NSClassFromString("CALayer")
      masklayer1 = layer(masklayer1)
      setBackgroundColor(masklayer1, black)
      if mode=1 then
        setFrame(masklayer1, CGRectMake(100+rimWth, 100-bsY+rimWth, 824-rimWth*2, 824-rimWth*2))
      else
        setFrame(masklayer1, CGRectMake(100-bsX+rimWth, 100+rimWth, 824-rimWth*2, 824-rimWth*2))
      end if
      setCornerRadius(masklayer1, 184-rimWth*0.65)  // 現物合わせの調整値
      setCornerCurve(masklayer1, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      Dim masklayer2 As Ptr = NSClassFromString("CALayer")
      masklayer2 = layer(masklayer2)
      setBackgroundColor(masklayer2, black)
      setFrame(masklayer2, CGRectMake(100+rimWth, 100+rimWth, 824-rimWth*2, 824-rimWth*2))
      setCornerRadius(masklayer2, 184-rimWth*0.65)  // 現物合わせの調整値
      setCornerCurve(masklayer2, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      
      // ベースとなる矩形(上または右)
      Dim splitlayer1 As Ptr = NSClassFromString("CALayer")
      splitlayer1 = layer(splitlayer1)
      setBackgroundColor(splitlayer1, myCGColor(bsClr1))
      if mode=1 then
        setFrame(splitlayer1, CGRectMake(0, bsY, 1024, 1024-bsY))
      else
        setFrame(splitlayer1, CGRectMake(bsX, 0, 1024-bsX, 1024))
      end if
      // ベースとなる矩形(下または左)
      Dim splitlayer2 As Ptr = NSClassFromString("CALayer")
      splitlayer2 = layer(splitlayer2)
      setBackgroundColor(splitlayer2, myCGColor(bsClr2))
      if mode=1 then
        setFrame(splitlayer2, CGRectMake(0, 0, 1024, bsY))
      else
        setFrame(splitlayer2, CGRectMake(0, 0, bsX, 1024))
      end if
      Declare Sub setMask Lib "Cocoa" Selector "setMask:" (receiver As Ptr, msk As Ptr)
      setMask(splitlayer1, masklayer1)
      setMask(splitlayer2, masklayer2)
      // IconViewのlayerにsplitlayer1,splitlayer2を追加
      addSublayer(layer1, splitlayer1)
      addSublayer(layer1, splitlayer2)
      
      // テキスト
      if chText<>"" then
        Dim textLayer As Ptr = NSClassFromString("CATextLayer")
        textLayer = layer(textLayer)
        setFrame(textLayer, CGRectMake(chCodx, chCody, 824, chFsize))
        Declare Sub setString Lib "Cocoa" Selector "setString:" (receiver As Ptr, val As CFStringRef)
        setString(textLayer, chText)
        Declare Sub setForegroundColor Lib "Cocoa" Selector "setForegroundColor:" (receiver As Ptr, val As Ptr)
        setForegroundColor(textLayer, chClr)
        Dim font As Ptr = NSClassFromString("NSFont")
        Declare Function systemFontOfSize Lib "Cocoa" Selector "fontWithName:size:" (receiver As Ptr, name As CFStringRef, size As CGFloat) As Ptr
        font = systemFontOfSize(font, chFname, chFsize)
        Declare Sub setFont Lib "Cocoa" Selector "setFont:" (receiver As Ptr, val As Ptr)
        setFont(textLayer, font)
        Declare Sub setFontSize Lib "Cocoa" Selector "setFontSize:" (receiver As Ptr, val As CGFloat)
        setFontSize(textLayer, chFsize)
        Declare Sub setAlignmentMode Lib "Cocoa" Selector "setAlignmentMode:" (receiver As Ptr, val As CFStringRef)
        setAlignmentMode(textLayer, "center")
        // IconViewのlayerにtextLayerを追加
        addSublayer(layer1, textLayer)
      end if
      
      // 全面にかかる透明度の高いグラデーション
      Dim gradientLayer As Ptr = NSClassFromString("CAGradientLayer")
      gradientLayer = layer(gradientLayer)
      setCornerRadius(gradientLayer, 184.0)
      setCornerCurve(gradientLayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      setFrame(gradientLayer, CGRectMake(100, 100, 824, 824))
      Dim colors As Ptr = NSClassFromString("NSMutableArray")
      Declare Function myArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr
      colors = myArray(colors)
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
      addObject(colors, black)
      addObject(colors, white)
      Declare Sub setColors Lib "Cocoa" Selector "setColors:" (receiver As Ptr, val As Ptr)
      setColors(gradientLayer, colors)
      Declare Sub setStartPoint Lib "Cocoa" Selector "setStartPoint:" (receiver As Ptr, val As CGPoint)
      setStartPoint(gradientLayer, CGPointMake(0.0, grStPnt))
      Declare Sub setEndPoint Lib "Cocoa" Selector "setEndPoint:" (receiver As Ptr, val As CGPoint)
      setEndPoint(gradientLayer, CGPointMake(0.0, grEdPnt))
      Declare Sub setOpacity Lib "Cocoa" Selector "setOpacity:" (receiver As Ptr, val As Single)
      setOpacity(gradientLayer, grOpacity)
      // IconViewのlayerにgradientLayerを追加
      addSublayer(layer1, gradientLayer)
      
      // まず、以前のIconViewを削除
      RemoveIconView(view)
      
      // IconViewを表示用ビューに追加
      Declare Sub addSubview Lib "Cocoa" selector "addSubview:" (class_id As Ptr, view As Ptr)
      addSubview(view, iconView)
      
      // clean up
      Declare Sub release Lib "Cocoa" selector "release" (class_id As Ptr)
      release(iconView)
      
      // 表示用ビューを再描画
      Declare Sub display Lib "Cocoa" selector "display" (class_id As Ptr)
      display(view)
    End Sub
    
    注)gradientLayerのsetFrameがNSRectになっていたのでCGRectに修正した。(2025.02.20)
  21. 以下をWindow1にペースト
    Protected Sub MakeIconViewPng(view As Ptr, path As String, shOffset As CGFloat, shRadius As CGFloat, shOpacity As Single, grStPnt As CGFloat, grEdPnt As CGFloat, grOpacity As Single, chCodx As CGFloat, chCody As CGFloat, chFname As String, chFsize As CGFloat, chText As String, chClr As Ptr, rimMode As Boolean, rimWth As Integer, rimClr As Ptr)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // 共通項
      Declare Function myCGColor Lib "Cocoa" Selector "CGColor" (receiver As Ptr) As Ptr
      Dim white As Ptr = NSClassFromString("NSColor")
      Declare Function whiteColor Lib "Cocoa" Selector "whiteColor" (receiver As Ptr) As Ptr
      white = whiteColor(white)
      white = myCGColor(white)
      Dim black As Ptr = NSClassFromString("NSColor")
      Declare Function blackColor Lib "Cocoa" Selector "blackColor" (receiver As Ptr) As Ptr
      black = blackColor(black)
      black = myCGColor(black)
      Dim red As Ptr = NSClassFromString("NSColor")
      Declare Function redColor Lib "Cocoa" Selector "redColor" (receiver As Ptr) As Ptr
      red = redColor(red)
      red = myCGColor(red)
      Dim clear As Ptr = NSClassFromString("NSColor")
      Declare Function clearColor Lib "Cocoa" Selector "clearColor" (receiver As Ptr) As Ptr
      clear = clearColor(clear)
      clear = myCGColor(clear)
      
      // IconViewの生成
      Dim iconView As Ptr = NSClassFromString("NSView")
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      iconView = alloc(iconView)
      Declare Function initWithFrame Lib "Cocoa" Selector "initWithFrame:" (receiver As Ptr, identifier As NSRect) As Ptr
      iconView = initWithFrame(iconView, NSMakeRect(0, 0, 1024, 1024))
      Declare Sub setWantsLayer Lib "Cocoa" Selector "setWantsLayer:" (receiver As Ptr, val As Boolean)
      setWantsLayer(iconView, true)
      
      // シャドウ付加用の角丸矩形(白)
      Dim shadowlayer As Ptr = NSClassFromString("CALayer")
      Declare Function layer Lib "Cocoa" Selector "layer" (receiver As Ptr) As Ptr
      shadowlayer = layer(shadowlayer)
      Declare Sub setBackgroundColor Lib "Cocoa" Selector "setBackgroundColor:" (receiver As Ptr, val As Ptr)
      setBackgroundColor(shadowlayer, myCGColor(rimClr))
      Declare Sub setFrame Lib "Cocoa" Selector "setFrame:" (receiver As Ptr, rect As CGRect)
      setFrame(shadowlayer, CGRectMake(100, 100, 824, 824))
      Declare Sub setCornerRadius Lib "Cocoa" Selector "setCornerRadius:" (receiver As Ptr, val As CGFloat)
      setCornerRadius(shadowlayer, 184.0)
      Declare Sub setCornerCurve Lib "Cocoa" Selector "setCornerCurve:" (receiver As Ptr, val As CFStringRef)
      setCornerCurve(shadowlayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      
      // 以下、シャドウ
      Declare Sub setMasksToBounds Lib "Cocoa" Selector "setMasksToBounds:" (receiver As Ptr, val As Boolean)
      setMasksToBounds(shadowlayer, false)
      Declare Sub setShadowOffset Lib "Cocoa" Selector "setShadowOffset:" (receiver As Ptr, rect As CGSize)
      setShadowOffset(shadowlayer, CGSizeMake(0.0, shOffset))
      Declare Sub setShadowOpacity Lib "Cocoa" Selector "setShadowOpacity:" (receiver As Ptr, val As Single)
      setShadowOpacity(shadowlayer, shOpacity)
      Declare Sub setShadowColor Lib "Cocoa" Selector "setShadowColor:" (receiver As Ptr, val As Ptr)
      setShadowColor(shadowlayer, black)
      Declare Sub setShadowRadius Lib "Cocoa" Selector "setShadowRadius:" (receiver As Ptr, val As CGFloat)
      setShadowRadius(shadowlayer, shRadius)
      // iconViewのlayerを取得
      Dim layer1 As Ptr = layer(iconView)
      // iconViewのlayerにshadowlayerを追加
      Declare Sub addSublayer Lib "Cocoa" Selector "addSublayer:" (receiver As Ptr, layer As Ptr)
      addSublayer(layer1, shadowlayer)
      
      // イメージ描画用の矩形を角丸にするマスク
      Dim masklayer As Ptr = NSClassFromString("CALayer")
      masklayer = layer(masklayer)
      setBackgroundColor(masklayer, black)
      if rimMode then
        setFrame(masklayer, CGRectMake(100+rimWth, 100+rimWth, 824-rimWth*2, 824-rimWth*2))
      else
        setFrame(masklayer, CGRectMake(100, 100, 824, 824))
      end if
      if rimMode then
        setCornerRadius(masklayer, 184-rimWth*0.65)  // 現物合わせの調整値
        setCornerCurve(masklayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      else
        setCornerRadius(masklayer, 184.0)  // 現物合わせの調整値
        setCornerCurve(masklayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      end if
      // イメージ描画用の矩形
      Dim imagelayer As Ptr = NSClassFromString("CALayer")
      imagelayer = layer(imagelayer)
      Dim image0 As Ptr = NSClassFromString("NSImage")
      image0 = alloc(image0)
      Declare Function initWithContentsOfFile Lib "Cocoa" Selector "initWithContentsOfFile:" (receiver As Ptr, name As CFStringRef) As Ptr  // 画像をファイルから読み込み
      image0 = initWithContentsOfFile(image0, path)
      Declare Function myCGImage Lib "Cocoa" Selector "CGImage" (receiver As Ptr) As Ptr
      Dim cgimg As Ptr = myCGImage(image0)
      Declare Sub setContents Lib "Cocoa" Selector "setContents:" (receiver As Ptr, cont As Ptr)
      setContents(imagelayer, cgimg)
      setFrame(imagelayer, CGRectMake(0, 0, 1024, 1024))
      Declare Sub setMask Lib "Cocoa" Selector "setMask:" (receiver As Ptr, msk As Ptr)
      setMask(imagelayer, masklayer)
      // iconViewのlayerにimagelayerを追加
      addSublayer(layer1, imagelayer)
      Declare Sub release Lib "Cocoa" selector "release" (class_id As Ptr)
      release(image0)
      
      // テキスト
      if chText<>"" then
        Dim textLayer As Ptr = NSClassFromString("CATextLayer")
        textLayer = layer(textLayer)
        setFrame(textLayer, CGRectMake(chCodx, chCody, 824, chFsize))
        Declare Sub setString Lib "Cocoa" Selector "setString:" (receiver As Ptr, val As CFStringRef)
        setString(textLayer, chText)
        Declare Sub setForegroundColor Lib "Cocoa" Selector "setForegroundColor:" (receiver As Ptr, val As Ptr)
        setForegroundColor(textLayer, chClr)
        Dim font As Ptr = NSClassFromString("NSFont")
        Declare Function systemFontOfSize Lib "Cocoa" Selector "fontWithName:size:" (receiver As Ptr, name As CFStringRef, size As CGFloat) As Ptr
        font = systemFontOfSize(font, chFname, chFsize)
        Declare Sub setFont Lib "Cocoa" Selector "setFont:" (receiver As Ptr, val As Ptr)
        setFont(textLayer, font)
        Declare Sub setFontSize Lib "Cocoa" Selector "setFontSize:" (receiver As Ptr, val As CGFloat)
        setFontSize(textLayer, chFsize)
        Declare Sub setAlignmentMode Lib "Cocoa" Selector "setAlignmentMode:" (receiver As Ptr, val As CFStringRef)
        setAlignmentMode(textLayer, "center")
        // iconViewのlayerにtextLayerを追加
        addSublayer(layer1, textLayer)
      end if
      
      // 全面にかかる透明度の高いグラデーション
      Dim gradientLayer As Ptr = NSClassFromString("CAGradientLayer")
      gradientLayer = layer(gradientLayer)
      setCornerRadius(gradientLayer, 184.0)
      setCornerCurve(gradientLayer, "continuous")  // kCACornerCurveContinuous = スムースカーブ
      setFrame(gradientLayer, CGRectMake(100, 100, 824, 824))
      Dim colors As Ptr = NSClassFromString("NSMutableArray")
      Declare Function myArray Lib "Cocoa" Selector "array" (receiver As Ptr) As Ptr
      colors = myArray(colors)
      Declare Sub addObject Lib "Cocoa" Selector "addObject:" (receiver As Ptr, obj As Ptr)
      addObject(colors, black)
      addObject(colors, white)
      Declare Sub setColors Lib "Cocoa" Selector "setColors:" (receiver As Ptr, val As Ptr)
      setColors(gradientLayer, colors)
      Declare Sub setStartPoint Lib "Cocoa" Selector "setStartPoint:" (receiver As Ptr, val As CGPoint)
      setStartPoint(gradientLayer, CGPointMake(0.0, grStPnt))
      Declare Sub setEndPoint Lib "Cocoa" Selector "setEndPoint:" (receiver As Ptr, val As CGPoint)
      setEndPoint(gradientLayer, CGPointMake(0.0, grEdPnt))
      Declare Sub setOpacity Lib "Cocoa" Selector "setOpacity:" (receiver As Ptr, val As Single)
      setOpacity(gradientLayer, grOpacity)
      // iconViewのlayerにgradientLayerを追加
      addSublayer(layer1, gradientLayer)
      
      // まず、以前のIconViewを削除
      RemoveIconView(view)
      
      // IconViewを表示用ビューに追加
      Declare Sub addSubview Lib "Cocoa" selector "addSubview:" (class_id As Ptr, view As Ptr)
      addSubview(view, iconView)
      release(iconView)
      
      // 表示用ビューを再描画
      Declare Sub display Lib "Cocoa" selector "display" (class_id As Ptr)
      display(view)
    End Sub
    
    注)gradientLayerのsetFrameがNSRectになっていたのでCGRectに修正、image0のreleaseが抜けていたので追加した。(2025.02.20)
  22. 以下をWindow1にペースト
    Protected Sub RemoveIconView(view As Ptr)
      Declare Function subviews Lib "Cocoa" selector "subviews" (obj_id As Ptr) As Ptr
      Dim views As Ptr = subviews(view)
      
      Declare Function count Lib "Cocoa" selector "count" (obj_id As Ptr) As Integer
      Dim cnt As Integer = count(views)
      
      if cnt=0 then  // subviewがなければ戻る(初回時が該当)
        return
      end if
      
      Declare Function ObjectAtIndex lib "Cocoa" selector "objectAtIndex:" (obj_id as Ptr, idx as Integer) As Ptr
      Dim pnt4 As Ptr = objectAtIndex(views, cnt-1)  // 最後が目的のviewと決め打ち
      
      Declare Sub removeFromSuperview Lib "Cocoa" selector "removeFromSuperview" (obj_id As Ptr)
      removeFromSuperview(pnt4)
    End Sub
    
  23. 以下をWindow1にペースト
    Protected Sub SaveImage(view As Ptr, pix As Integer, res As Integer, path As String)
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // viewサイズの取得とセット
      Declare Function myBounds Lib "Cocoa" Selector "bounds" (receiver As Ptr) As NSRect
      Dim bounds As NSRect = myBounds(view)
      Dim viewSize As NSSize = NSMakeSize(bounds.w,bounds.h)
      Dim imgSize As NSSize = NSMakeSize(viewSize.width,viewSize.height)
      
      // viewからNSBitmapImageRepを取得
      Declare Function bitmapImageRepForCachingDisplayInRect Lib "Cocoa" Selector "bitmapImageRepForCachingDisplayInRect:" (receiver As Ptr, identifier As NSRect) As Ptr
      Dim bir As Ptr = bitmapImageRepForCachingDisplayInRect(view, bounds)
      Declare Sub setSize Lib "Cocoa" Selector "setSize:" (receiver As Ptr, size As NSSize)
      setSize(bir, imgSize)
      Declare Sub cacheDisplayInRect Lib "Cocoa" Selector "cacheDisplayInRect:toBitmapImageRep:" (receiver As Ptr, val As NSRect, imgrep As Ptr)
      cacheDisplayInRect(view, bounds, bir)
      
      // NSBitmapImageRepからCGBitmapContextを生成して、画像を描画(この時、必要に応じて縮小される)したCGImageを生成
      Declare Function myCGImage Lib "Cocoa" Selector "CGImage" (receiver As Ptr) As Ptr
      Dim cgimg As Ptr = myCGImage(bir)
      Dim width As UInteger= pix*res
      Dim height As UInteger= pix*res
      Dim bitsPerComponent As UInteger= 8
      Dim bytesPerRow As UInteger= 4*width
      Declare Function CGColorSpaceCreateDeviceRGB lib "Carbon" () As Ptr
      Dim colorSpace As Ptr = CGColorSpaceCreateDeviceRGB()
      Dim bitmapInfo As UInt32 = 1
      Declare Function CGBitmapContextCreate lib "Carbon" (obj as Ptr, w As UInteger, h As UInteger, bpc As UInteger, bpr As UInteger, cs As Ptr, bif As UInt32) As Ptr
      Dim bitmapContext As Ptr = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo)
      Dim bitmapRect  As NSRect = NSMakeRect(0.0, 0.0, width, height)
      Declare Sub CGContextDrawImage lib "Carbon" (obj as Ptr, cs As NSRect, bif As Ptr)
      CGContextDrawImage(bitmapContext, bitmapRect, cgimg)
      Declare Function CGBitmapContextCreateImage lib "Carbon" (obj as Ptr) As Ptr
      Dim newImageRef As Ptr = CGBitmapContextCreateImage(bitmapContext)
      
      // CGImageからNSImageを生成
      Dim image As Ptr = NSClassFromString("NSImage")
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      image = alloc(image)
      Declare Function initWithCGImage Lib "Cocoa" Selector "initWithCGImage:size:" (receiver As Ptr, imgref As Ptr, size As NSSize) As Ptr
      image = initWithCGImage(image, newImageRef, NSMakeSize(pix, pix))
      
      // TIFFに変換
      Declare Function TIFFRepresentation Lib "Cocoa" Selector "TIFFRepresentation" (receiver As Ptr) As Ptr
      Dim tiffData As Ptr = TIFFRepresentation(image)
      Declare Sub release Lib "Cocoa" Selector "release" (receiver As Ptr)
      release(image)
      
      // TIFFからBitmapImageRepを生成(png形式にするには、TIFFからNSBitmapImageRepを経由しないといけない)
      Dim imageRep As Ptr = NSClassFromString("NSBitmapImageRep")
      imageRep = alloc(imageRep)
      Declare Function initWithCGImage Lib "Cocoa" Selector "initWithData:" (receiver As Ptr, imgref As Ptr) As Ptr
      imageRep = initWithCGImage(imageRep, tiffData)
      
      // 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")
      
      // BitmapImageRepからPNG形式のData生成(dictはPNG用オプション)
      Declare Function representationUsingType Lib "Cocoa" Selector "representationUsingType:properties:" (receiver As Ptr, type As Integer, prop As Ptr) As Ptr
      Dim pngData As Ptr = representationUsingType(imageRep, 4, dict)  // NSPNGFileType = 4
      release(imageRep)
      
      // PNGをファイル保存
      Dim mngr As Ptr = NSClassFromString("NSFileManager")
      Declare Function defaultManager Lib "Cocoa" selector "defaultManager" (class_id As Ptr) As Ptr
      mngr = defaultManager(mngr)
      Declare Function createFileAtPath Lib "Cocoa" Selector "createFileAtPath:contents:attributes:" (receiver As Ptr, path As CFStringRef, cont As Ptr, attr As Ptr) As Boolean
      Dim ret As Boolean = createFileAtPath(mngr, path, pngData, nil)
    End Sub
    
  24. 以下をWindow1にペースト
    Protected Sub SaveParam(flg As Boolean)
      Var f As FolderItem
      if flg then
        Dim nam As String = "AppIconMakeParam.txt"
        if pFIparam <> Nil Then
          nam = pFIparam.Name
        end if
        f=FolderItem.ShowSaveFileDialog("text/plain", nam)
      else
        f=pFIparam
      end if
      If f = Nil Then
        return
      End If
      
      Try
        
        Var t As TextOutputStream = TextOutputStream.Create(f)
        
        Dim vv As String
        if myRadioButton1.Value then
          vv="0"
        else
          vv="1"
        end if
        t.WriteLine(vv)  // 1,2
        if myRadioButton3.Value then
          vv="0"
        elseif myRadioButton4.Value then
          vv="1"
        else
          vv="2"
        end if
        t.WriteLine(vv)  // 3,4,5
        t.WriteLine(TextFieldBY.Text)
        t.WriteLine(Str(gClrCtoI(ClrCanvas1.pClr)))
        t.WriteLine(TextFieldBX.Text)
        t.WriteLine(Str(gClrCtoI(ClrCanvas2.pClr)))
        
        t.WriteLine(Str(CheckBox1.Value))
        
        t.WriteLine(TextFieldBW.Text)
        t.WriteLine(Str(gClrCtoI(ClrCanvas3.pClr)))
        
        t.WriteLine(TextFieldSO.Text)
        t.WriteLine(TextFieldSR.Text)
        t.WriteLine(TextFieldSP.Text)
        
        t.WriteLine(TextFieldGS.Text)
        t.WriteLine(TextFieldGE.Text)
        t.WriteLine(TextFieldGO.Text)
        
        t.WriteLine(TextFieldTX.Text)
        t.WriteLine(TextFieldTY.Text)
        t.WriteLine(ComboBox1.Text)
        t.WriteLine(TextFieldTF.Text)
        t.WriteLine(TextFieldTS.Text)
        t.WriteLine(Str(gClrCtoI(ClrCanvas4.pClr)))
        
        t.WriteLine(pPath)  // Dropped File Path
        
        t.Close
        
      Catch e As IOException
        ' handle error
      End Try
    End Sub
    
    注)外部画像のパスが抜けていたので追加した。(2025.02.15)
  25. 以下をWindow1にペースト
    Protected Sub SetIconView(flg As Boolean)
      if flg then  // 初期値に戻すなら
        
        // デフォルト値のセット
        myRadioButton1.Value = true         // Internal [ myRadioButton2 = Dropped File ]
        myRadioButton3.Value=true           // Plane [ myRadioButton4 = H Split, myRadioButton5 = V Split ]
        TextFieldBY.Text = "512.0"          // H Split y
        ClrCanvas1.pClr = RGB(255,255,255)  // H Split Color ( Up or Rgt )
        TextFieldBX.Text = "512.0"          // H Split x
        ClrCanvas2.pClr = RGB(255,255,255)  // V Split Color ( Lo or Lft )
        CheckBox1.Value = false             // Fringe
        TextFieldBW.Text = "64.0"           // Fringe Width
        ClrCanvas3.pClr = RGB(255,255,255)  // Fringe Color
        
        TextFieldSO.Text = "-10.0"          // Shadow Offset
        TextFieldSR.Text = "11.0"           // Shadow Radius
        TextFieldSP.Text = "0.3"            // Shadow Opacity
        
        TextFieldGS.Text = "0.0"            // Gradient StartPoint
        TextFieldGE.Text = "1.0"            // Gradient EndPoint
        TextFieldGO.Text = "0.08"           // Gradient Opacity
        
        TextFieldTX.Text = "100.0"          // Font x
        TextFieldTY.Text = "422.0"          // Font y
        ComboBox1.SelectedRowIndex=0        // Font Name
        TextFieldTF.Text = "240.0"          // Font Size
        TextFieldTS.Text = ""               // String
        ClrCanvas4.pClr = RGB(0,0,0)        // Font Color
        
        ClrCanvas1.Refresh
        ClrCanvas2.Refresh
        ClrCanvas3.Refresh
        ClrCanvas4.Refresh
        
      end if
      
      // ベース画像の種別
      Dim vv1 As Integer
      if myRadioButton1.Value then
        vv1=0
      else
        vv1=1
      end if
      
      // 分割の有無
      Dim vv2 As Integer
      if myRadioButton3.Value then
        vv2=0
      elseif myRadioButton4.Value then
        vv2=1
      else
        vv2=2
      end if
      
      Dim v18 As CGFloat = Val(TextFieldBY.Text)
      Dim v13 As Ptr     = gClrCtoP(ClrCanvas1.pClr)
      Dim v19 As CGFloat = Val(TextFieldBX.Text)
      Dim v14 As Ptr     = gClrCtoP(ClrCanvas2.pClr)
      Dim v15 As Boolean = CheckBox1.Value
      Dim v16 As CGFloat = Val(TextFieldBW.Text)
      Dim v17 As Ptr     = gClrCtoP(ClrCanvas3.pClr)
      
      Dim v1 As CGFloat  = Val(TextFieldSO.Text)
      Dim v2 As CGFloat  = Val(TextFieldSR.Text)
      Dim v3 As Single   = Val(TextFieldSP.Text)
      
      Dim v4 As CGFloat  = Val(TextFieldGS.Text)
      Dim v5 As CGFloat  = Val(TextFieldGE.Text)
      Dim v6 As Single   = Val(TextFieldGO.Text)
      
      Dim v7 As CGFloat  = Val(TextFieldTX.Text)
      Dim v8 As CGFloat  = Val(TextFieldTY.Text)
      Dim v9 As String   = ComboBox1.Text
      Dim v10 As CGFloat = Val(TextFieldTF.Text)
      Dim v11 As String  = TextFieldTS.Text
      Dim v12 As Ptr     = gClrCtoP(ClrCanvas4.pClr)
      
      // ベース画像モードごとの処理
      if vv1=0 then  // 内蔵
        
        if vv2=0 then  // 内部処理でアイコンビュー生成
          MakeIconViewGen(pViewInst,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v15,v16,v17)  // 全面ベタ
        else
          MakeIconViewGenSplit(pViewInst,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v19,v18,vv2,v15,v16,v17)  // 2分割
        end if
        
      else  // 外部ファイル
        
        if pPath="" then  // ベース画像が取得されていなければ、何もしなくていいが、モードが切り替えられた場合は、現在表示しているビューをクリアする必要がある。
          RemoveIconView(pViewInst)  // まず、以前のIconViewを削除
          Declare Sub display Lib "Cocoa" selector "display" (class_id As Ptr)  // 表示用ビューを再描画
          display(pViewInst)
        else  // 画像ファイルをベースとしたアイコンビュー生成
          MakeIconViewPng(pViewInst,pPath,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v15,v16,v17)
        end if
        
      end if
    End Sub
    
  26. 以下をWindow1にペースト(できなければPropertyに、Name:pFIparam、Type:FolderItem、を追加)
    Protected Property pFIparam As FolderItem
    
  27. 以下をWindow1にペースト(できなければPropertyに、Name:pPath、Type:String、を追加)
    Protected Property pPath As String
    
  28. 以下をWindow1にペースト(できなければPropertyに、Name:pViewInst、Type:Ptr、を追加)
    Protected Property pViewInst As Ptr
    
  29. 以下をWindow1にペースト(できなければConstantsに、Name:kLeave、Default Value:false、を追加)
    Protected Const kLeave as Boolean = false
    
  30. 以下をWindow1にペースト(できなければConstantsに、Name:kReset、Default Value:true、を追加)
    Protected Const kReset as Boolean = true
    
  31. MainMenuBarのFileMenuに、FileOpen、FileSave、FileSaveAsを作成する。
  32. 新規クラス(名前は、ここでは「NSViewCanvas」)を作成する。
  33. 以下をNSViewCanvasにペースト
    Public Sub Constructor(byRef inst As Ptr, win As DesktopWindow, rect As NSRect)
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // NSViewを継承したカスタムクラスを作成。初回のみ
      makeClass()
      
      // インスタンスを作成
      Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr
      Declare Function initWithFrame Lib "Cocoa" selector "initWithFrame:" (obj_id As Ptr, frame As NSRect) As Ptr
      Dim subclassId As Ptr = initWithFrame(alloc(NSVewClass), rect)
      
      // ウィンドウのビューを取得
      Declare Function contentView Lib "Cocoa" selector "contentView" (class_id As Ptr) As Ptr
      Dim pnt2 As Ptr = contentView(win.handle)
      
      // 生成したコントロールをビューに追加
      Declare Sub addSubview Lib "Cocoa" selector "addSubview:" (class_id As Ptr, view As Ptr)
      addSubview(pnt2, subclassId)
      
      // インスタンスを返す
      inst = subclassId
    End Sub
    
  34. 以下をNSViewCanvasにペースト
    Private Shared Sub makeClass()
      // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr
      
      // Declare宣言
      Declare Function objc_allocateClassPair Lib "Cocoa" (superclass As Ptr, name As CString, extraBytes As Integer) as Ptr
      Declare Sub objc_registerClassPair Lib "Cocoa" (cls As Ptr)
      Declare Function class_addMethod Lib "Cocoa" (cls As Ptr, name As Ptr, imp As Ptr, types As CString) As Boolean
      
      // 既にクラス作成済なら戻る
      if NSVewClass <> nil then
        return
      end if
      
      // クラス名をmyNSView、メタクラス名をNSViewにして、生成
      Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSView"), "myNSView", 0)
      // ランタイムに登録(参照を可能とするため)
      objc_registerClassPair newClassId
      
      // クラスを保持
      NSVewClass = newClassId
    End Sub
    
  35. 以下をNSViewCanvasにペースト(できなければShared Propertyに、Name:NSVewClass、Type:Ptr、を追加)
    Private Shared Property NSVewClass As Ptr
    
  36. 新規モジュール(名前は、ここでは「Globals」)を作成する。
  37. 以下をGlobalsにペースト
    Public Function gClrCtoI(clr As Color) As Integer
      // 変換
      return clr.Red*65536+clr.Green*256+clr.Blue
    End Function
    
  38. 以下をGlobalsにペースト
    Public Function gClrCtoP(clrC As Color) As Ptr
      Dim r, g, b As CGFloat
      
      // 0〜255 を 0.0〜1.0 にマッピング
      r=clrC.Red/255
      g=clrC.Green/255
      b=clrC.Blue/255
      
      // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。
      Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr
      
      // カラーの取得
      Dim clr As Ptr = NSClassFromString("NSColor")
      
      // RGB値からカラーを生成
      Declare Function colorWithCalibrate Lib "Cocoa" Selector "colorWithCalibratedRed:green:blue:alpha:" (receiver As Ptr, red As CGFloat, green As CGFloat, blue As CGFloat, alpha As CGFloat) As Ptr
      clr = colorWithCalibrate(clr, r, g, b, 1.0)  // alpha値は常に1.0
      
      // カラーを返す
      return clr
    End Function
    
  39. 以下をGlobalsにペースト
    Public Function gClrItoC(i As integer) As Color
      Dim r, g, b as Integer
      
      // RGBの分離
      r=i\65536
      g=(i-r*65536)\256
      b=i-r*65536-g*256
      
      // 変換
      return RGB(r,g,b)
    End Function
    
  40. 以下をGlobalsにペースト
    Public Function gClrPtoC(pnt As Ptr) As Color
      Dim rA As New MemoryBlock(8)
      Dim gA As New MemoryBlock(8)
      Dim bA As New MemoryBlock(8)
      Dim aA As New MemoryBlock(8)
      Dim rF, gF, bF As CGFloat
      
      Declare Sub getRGB Lib "Cocoa" Selector "getRed:green:blue:alpha:" (receiver As Ptr, red As Ptr, green As Ptr, blue As Ptr, alpha As Ptr)
      getRGB(pnt, rA, gA, bA, aA)  // rgb値を取得
      
      rF = rA.DoubleValue(0)
      gF = gA.DoubleValue(0)
      bF = bA.DoubleValue(0)
      
      return RGB(rF*255,gF*255,bF*255)  // Color形式に変換して返す
    End Function
    
  41. 新規クラス(名前は、ここでは「ClrCanvas」)を作成し、Superを「DesktopCanvas」にする。
  42. 以下をClrCanvasにペースト(できなければ、Sub - Endの間をMouseDownイベントに記述)
    Function MouseDown(x As Integer, y As Integer) Handles MouseDown as Boolean
      Dim c as Color
      Dim r as Boolean
      
      // 現在のカラー値をコピー
      c=pClr
      
      // カラーピッカー表示
      r = Color.SelectedFromDialog(c,"色を指定して下さい。")
      if r then
        pClr=c  // OKが押されたら色を保存
      end if
      
      me.Refresh  // 再描画
    End Function
    
  43. 以下をClrCanvasにペースト(できなければ、Sub - Endの間をOpeningイベントに記述)
    Sub Opening() Handles Opening
      pClr = RGB(255,255,255)
    End Sub
    
  44. 以下をClrCanvasにペースト(できなければ、Sub - Endの間をPaintイベントに記述)
    Sub Paint(g As Graphics, areas() As Rect) Handles Paint
      // 外枠を黒で描画
      g.ForeColor=RGB(0,0,0)
      g.FillRect 0,0,me.width,me.height
      
      // 内部を指定色で描画
      g.ForeColor=pClr
      g.FillRect 1,1,me.width-2,me.height-2
    End Sub
    
  45. 以下をClrCanvasにペースト(できなければPropertyに、Name:pClr、Type:Color、を追加)
    Public Property pClr As Color = &c000000
    
  46. Library最下部のProject Controlsから、ClrCanvasをWindow1に4個ドロップする。(インスタンスが作成される。)
  47. 新規クラス(名前は、ここでは「myRadioButton」)を作成し、Superを「DesktopRadioButton」にする。
  48. Library最下部のProject Controlsから、myRadioButtonをWindow1に5個ドロップする。(インスタンスが作成される。)
  49. 他に、CGPointMake/CGRectMake/CGSizeMake/NSMakeRect/NSMakeSize(メソッド)、CGPoint/CGRect/CGSize/NSRect/NSSize(構造体)が必要ですが、それらはmacoslibからコピーさせて頂きました。(別途モジュールを用意してコピーする。)
    注)macoslibではメソッドの引数、構造体のメンバーの型に、Singleが割り当てられているものがあるが、それらはCGFloatに書き換える。
 実行してみたところ、アプリアイコンを作成し、icnsファイルを書き出せることを確認しました。
S Shot1
(クリックで拡大)
 試作したサンプルは、以下の通りです。
S Shot41 S Shot42 S Shot43 S Shot44 S Shot45 S Shot46
icon_32x32@2x.pngを、解像度のみ72dpiに変換しています。(左3個は内部生成、右3個は外部画像)

 おわりに

 段階的に作成したため、単色/2分割/外部画像でバラバラのコードとなっていますが、もう少し整理した方がいいかもしれません。
追記:バラバラになった理由の一つは、単色では問題ない(ので残した)が、2分割/外部画像ではグラデーションが綺麗にかからない場合がある(濃度によっては境界に白浮き/黒沈みが発生する)という問題があるためで、本件は引き続き調査中。(ベストとは言えないが、対処法は見えつつある。)
 アイコンの中にはテキストエディットやプレビューのように、一部がベースをはみ出すものもありますが、これらには対応していません。
 アイコン本体はグラフィックソフトで作って、外部画像として取り込めばいいのですが、シャドウやグラデーションは、はみ出し部には適用されませんので、別途マスクを作る等の対応が必要で、ちょっと厄介なことになりそうです。

 あと、アイコン画像はFinderが覚えているので、微調整して変更しても、ただちには反映されない場合があるので、注意が必要です。


 お世話になったサイト

 貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)

 参考サイト(1):アプリアイコン | Apple Developer Documentation
 参考サイト(2):Apple Design Resources - Apple Developer(テンプレートのダウンロード)
 参考サイト(3):【100回作って100回壊す?】Mac App Icon制作に初挑戦!半年間の軌跡と芽生えたデザインのスタンス|Goodpatch Blog グッドパッチブログ
 参考サイト(4):ios - smooth rounded corners in swift - Stack Overflow
 参考サイト(5):macos - NSImage size not real size with some pictures? - Stack Overflow


 更新履歴

 2025.02.20 おわりに、に追記を追加。Xojoでの実装、の19,20,21項を改訂。
 2025.02.15 Xojoでの実装、の16,24項を改訂。
 2025.02.10 新規作成


[Home]  [MacSoft]  [Donation]  [History]  [Privacy Policy]  [Affiliate Policy]