Create .icns file?

Hi,
is it possible in Xojo to make a .icns file from various png files?
If so, could someone point me in the right direction, such as what to look for in the Language Reference.

Thanks.

did you check IconMBS class and IconFamilyMBS class in our plugins?

[quote=105597:@Richard Summers]Hi,
is it possible in Xojo to make a .icns file from various png files?
If so, could someone point me in the right direction, such as what to look for in the Language Reference.[/quote]

When you build a program, it creates automatically the icns for you. You find it in the Resources folder inside the app content. Click the icon in the App inspector and drop the png over the diverse resolutions.

For Retina compatible icons, App Wrapper Mini does that very well from 1024x1024 pictures.

Or use the iconutil that comes with OSX for free

Thanks Christian and Michel.

Christian, due to the possibility of plugin authors becoming upset because their serial registration numbers were not obfuscated enough in the compiled app - I no longer use plugins at all.

Very annoying, as I have purchased a lot of plugins from various authors, but now no longer have the confidence to use them.

Michel, thanks but I want MY app to be able to do this - not Xojo or a third party app.

Thank you both for the advice :slight_smile:

If the target machine have sips and iconutil here is a tip I’ve found:

To convert a 1024x1024 png (named “Icon1024.png”) to a icns file, here is some steps.

mkdir MyIcon.iconset
sips -z 16 16 Icon1024.png --out MyIcon.iconset/icon_16x16.png
sips -z 32 32 Icon1024.png --out MyIcon.iconset/icon_16x16@2x.png
sips -z 32 32 Icon1024.png --out MyIcon.iconset/icon_32x32.png
sips -z 64 64 Icon1024.png --out MyIcon.iconset/icon_32x32@2x.png
sips -z 128 128 Icon1024.png --out MyIcon.iconset/icon_128x128.png
sips -z 256 256 Icon1024.png --out MyIcon.iconset/icon_128x128@2x.png
sips -z 256 256 Icon1024.png --out MyIcon.iconset/icon_256x256.png
sips -z 512 512 Icon1024.png --out MyIcon.iconset/icon_256x256@2x.png
sips -z 512 512 Icon1024.png --out MyIcon.iconset/icon_512x512.png
cp Icon1024.png MyIcon.iconset/icon_512x512@2x.png
iconutil -c icns MyIcon.iconset
rm -R MyIcon.iconset

Thanks Dave, I will look into the dreaded command lines :frowning:

Errata: here are some steps

Thanks Rick.
I already have all my various sized png files - I just need to somehow package them into a .icns file.

I presume then, that I just need to execute your last 2 lines of code in a shell command?
Considering that your first line of code uses mkdir, I also presume that MyIcon.iconset is just a regular folder?

The summary is:

Create a temporary iconset folder,
copy the proper sized and named icons to it
use iconutil to create the icns file from this iconset folder
remove the temporary folder and files.

So myIcon.iconset is a regular folder?

This can be done via declares, but it gets a bit painful if you need to support @2x resolutions. Are you already using macoslib in your project?

Yes.

mkdir MyIcon.iconset

creates a regular folder, but name it like myDesiredICNSbaseName.iconset

Joe,
I already have all my png files, both normal and @2, inside a folder.
All I need to do is change the name of the folder to include .iconset, and then execute these 2 shell commands:

iconutil -c icns MyIcon.iconset rm -R MyIcon.iconset

Yes - I have macoslib, but considering I only need to add 2 shell commands, would your idea of using macoslib and declares be better?

Typically when there’s a system provided API, it’s better to use that than the shell. They have a reasonable guarantee of not breaking across OS versions where as it’s not guaranteed that iconutil will even exist on the system.

So what is your suggestion?
Can you point me to your solution.

Thanks.

This is a bit ugly due to the forum’s lack of syntax highlighting and the language’s lack of a way to link directly to external variables, but it works in my limited testing. I was going to use parts of macoslib, but it turned out to be more hassle than it saved.

[code]Private Function WriteIcon(outputFile as FolderItem, pics() As Picture) As Boolean
Const kSystemLib = “/usr/lib/libSystem.B.dylib”
Declare Function dlopen Lib kSystemLib ( path As CString, mode As Integer ) As ptr
Declare Function dlsym Lib kSystemLib ( handle As Ptr, name As CString ) As ptr
Const RTLD_LAZY = 1
Const RTLD_GLOBAL = 8

Const kCFLib = “/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation”
Declare Function CFURLCreateWithString Lib kCFLib ( allocator As Ptr, URLString As CFStringRef, baseURL As Ptr ) As Ptr
Declare Function CFDictionaryCreateMutable lib kCFLib ( allocator As Ptr, capacity As Integer, keyCallBacks As Ptr, valueCallBacks As Ptr ) As Ptr
Declare Sub CFDictionarySetValue Lib kCFLib ( theDict As Ptr, key As Ptr, value As Ptr )
Declare Function CFNumberCreate Lib kCFLib ( allocator As Ptr, theType As Integer, ByRef valuePtr As Int32) As Ptr
Declare Sub CFRelease Lib kCFLib ( cf As Ptr )
Const kCFNumberSInt32Type = 3

Const kAppServicesLib = “/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices”
Declare Function CGImageDestinationCreateWithURL Lib kAppServicesLib ( url As Ptr, type As Ptr, count As Integer, options As Ptr ) As Ptr
Declare Sub CGImageDestinationAddImage Lib kAppServicesLib ( idst As Ptr, image As Ptr, properties As Ptr )
Declare Function CGImageDestinationFinalize Lib kAppServicesLib ( idst As Ptr ) As Boolean

// Unfortunately Xojo has no direct way to link against external variables
// like it can functions, so load things dynamically by hand.
Static sInitializedVars As Boolean
Static kCGImagePropertyDPIHeight As Ptr
Static kCGImagePropertyDPIWidth As Ptr
Static kCFTypeDictionaryKeyCallBacks As Ptr
Static kCFTypeDictionaryValueCallBacks As Ptr
Static kUTTypeAppleICNS As Ptr

If Not sInitializedVars Then
Dim libPtr As ptr = dlopen( kCFLib, RTLD_LAZY Or RTLD_GLOBAL )
kCFTypeDictionaryKeyCallBacks = dlsym(libPtr, “kCFTypeDictionaryKeyCallBacks”)
kCFTypeDictionaryValueCallBacks = dlsym(libPtr, “kCFTypeDictionaryValueCallBacks”)

libPtr = dlopen( kAppServicesLib, RTLD_LAZY Or RTLD_GLOBAL )
kCGImagePropertyDPIHeight = dlsym(libPtr, "kCGImagePropertyDPIHeight").Ptr
kCGImagePropertyDPIWidth = dlsym(libPtr, "kCGImagePropertyDPIWidth").Ptr

libPtr = dlopen( "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", RTLD_LAZY Or RTLD_GLOBAL )
kUTTypeAppleICNS = dlsym(libPtr, "kUTTypeAppleICNS").Ptr

sInitializedVars = True

End If

// If we have no input images then it’s a flat out error.
If pics.Ubound < 0 Then Return False
If outputFile = Nil Then Raise New NilObjectException

// Now for the actual work: create an image destination, add our images, and finalize it.
Dim success As Boolean
Dim url As Ptr = CFURLCreateWithString(Nil, outputFile.URLPath, Nil)
Dim dest As Ptr = CGImageDestinationCreateWithURL(url, kUTTypeAppleICNS, UBound(pics) + 1, Nil)
If dest <> Nil Then
For Each pic As Picture in pics
// CGImages have no concept of DPI, so we need to tell ImageIO about it
Dim properties As Ptr = CFDictionaryCreateMutable(Nil, 0, kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks)

  Dim dpiWidth As Int32 = pic.HorizontalResolution
  Dim dpiWidthObject As Ptr = CFNumberCreate(Nil, kCFNumberSInt32Type, dpiWidth)
  CFDictionarySetValue(properties, kCGImagePropertyDPIWidth, dpiWidthObject)
  CFRelease(dpiWidthObject)
  
  Dim dpiHeight As Int32 = pic.VerticalResolution
  Dim dpiHeightObject As Ptr = CFNumberCreate(Nil, kCFNumberSInt32Type, dpiHeight)
  CFDictionarySetValue(properties, kCGImagePropertyDPIHeight, dpiHeightObject)
  CFRelease(dpiHeightObject)
  
  // Finally we can grab the Picture's CGImage and add it to the destination
  Dim handle As Ptr = pic.CopyOSHandle(Picture.HandleType.MacCGImage)
  CGImageDestinationAddImage(dest, handle, properties)
  CFRelease(handle)
  CFRelease(properties)
Next

success = CGImageDestinationFinalize(dest)
CFRelease(dest)

End If
CFRelease(url)

Return success
End Function[/code]

Now I feel guilty because you spent the time and effort to create all that, and I have no idea where to put it or how to use it :frowning:

In the IDE, create a new method named ‘WriteIcon’, set its parameters to be ‘outputFile As FolderItem, pics() As Picture’, then copy all of the code between then the first and last lines in my post and paste it into the method.

Basically, Richard, any time you see code that starts

Sub myFunction(paramaters)

or

Function myFunction(paramaters) as ReturnType

You want to do this in the IDE: