objective-cmacosapplescriptcore-foundationsips

How to programmatically make Mac OS X ICNS with 10 different images using sips or other


My issue is that i need have. I need to achieve this programmatically.

So for mac os x an application icon should have these sizes:

I have 10 images. Each one i placed a badge in the right corner, the placement and position of this badge does not scale. So i 10 different images.

How to make a ICNS out of this?

I thought to use sips, however sips only takes one file and it does all the scalings: http://cc.bingj.com/cache.aspx?q=mac+icns+sips+argument+list&d=5035141870911884&mkt=en-US&setlang=en-US&w=n3PZcWn6bEPxt4O96PPLd6nugtVq5jDz

Is there a way to make /usr/bin/sips take my 10 images and make a icns out of it? If sips cant do it is there any other way?


Solution

  • If you want to use iconutil, you can do that. At least on my 10.9.5 system, it's part of the base OS. It's not a special install, like developer tools. You can verify that using:

    pkgutil --file-info /usr/bin/iconutil
    

    Here, that outputs:

    volume: /
    path: /usr/bin/iconutil
    
    pkgid: com.apple.pkg.BSD
    pkg-version: 10.9.0.1.1.1306847324
    install-time: 1402788942
    uid: 0
    gid: 0
    mode: 755
    

    The important part is the pkgid. It's part of the BSD package, which is part of the base OS.

    That said, it's not hard to write a bit of code to do this.

    You can use the CGDestination API. Create a destination using CGImageDestinationCreateWithURL(). For the type, pass kUTTypeAppleICNS.

    Given that you want to add images from individual files, it's probably easiest to create a CGImageSource for each using CGImageSourceCreateWithURL(). Then, you can directly add an image from the source to the destination using CGImageDestinationAddImageFromSource(). Don't forget to CFRelease() each source after you've added its image to the destination.

    Then, call CGImageDestinationFinalize() to have the destination write out the image to the URL. Then, CFRelease() the destination.

    If each of the source images has the proper DPI set, this will be copied over intact to the destination. If the source images don't have the proper DPI set, you can override it by passing a dictionary of properties to CGImageDestinationAddImageFromSource(). Include the keys kCGImagePropertyDPIHeight and kCGImagePropertyDPIWidth, each with a corresponding value of a CFNumber object with the desired DPI. For a normal-resolution icon, use 72 DPI. For a high-resolution (@2x) icon, use 144 DPI.


    Creating ICNS files can also be done using the old IconFamily API, but it's a bit hairy. Also, it doesn't support high-resolution icons.

    First, you create a handle (pointer-to-pointer-to-resizable-buffer) for the icon family:

    IconFamilyHandle iconFamily = (IconFamilyHandle)NewHandle(0);
    

    Then, for each image size (16, 32, 128, 256, and 512), you create a handle for a raw bitmap of the image data. The bitmap should be 32 bits per pixel, 8 bits per component, ARGB non-premultiplied data with no padding.

    int size = /* 16, 32, 128, 256, or 512 */;
    Handle handle = NewHandle(size * size * 4);
    // fill handle with image data; buffer pointer is *handle
    

    Then, you add that handle to the icon family with a call like:

    SetIconFamilyData(iconFamily, kIconServices16PixelDataARGB, handle);
    

    For the other sizes, replace the "16" in kIconServices16PixelDataARGB with the appropriate value.

    Then, you write the icon family handle's data out to file. A pointer to the data is obtained by simply dereferencing the handle (i.e. *iconFamily). Its size is obtained by calling GetHandleSize((Handle)iconFamily).

    Dispose of any handles you created along the way by calling DisposeHandle().