Open Bug 314651 Opened 14 years ago Updated 10 years ago

API to generate mac .icns from PNGs

Categories

(Core :: Widget: Cocoa, enhancement)

PowerPC
macOS
enhancement
Not set

Tracking

()

People

(Reporter: benjamin, Unassigned)

Details

I need an API to generate mac .icns from PNGs. This will have a couple
applications:

XULRunner application icons should probably be in a cross-platform format (I'm
leaning towards PNG), and at install-time I'd like to generate the bundle icon (and document icons if applicable) from that PNG.

I also want a generic dock-icon API where XUL apps can put up customized dock icons similar to the way Thunderbird customizes the dock icon with the new-mail notification. This could even be a dynamically-drawn <canvas>-like element or icon overlay if appropriate.

My basic thought was to create an nsIMacIcon interface which would wrap a
IconFamilyHandle. I see the WriteIconFile API, so the only part of this that is mysterious to me is how to get the PNG image bits into a mac icon format.

This is a parallel bug to bug 314030.
I'm not sure if I wrote this before. But I think we should extend imgIEncoder to support encoding imgIContainers, and use that here.
Can an imgIEncoder encode *multiple* imgIContainers (since most icon formats need multiple sizes and image depths).
if you design the interface appropriately :) (and if pavlov agrees)
You need to call SetIconFamilyData on the icon types you want to add.  Icon types are in IconStorage.h, but their definitions aren't.  The type definitions for most of them are in the older Inside Macintosh volumes, but the older formats are just bitmaps.  Very tractable.  The newer formats can use compressed data on disk, but you pass bitmaps in to SetIconFamilyData for them.

Bear with me for a moment, this is kind of bogus, but assuming you're looking at ARGB data, the implementation's not difficult.

  #include <Carbon/Carbon.h>
  // For a good read, see IconStorage.h

  // First, create an IconFamilyHandle placeholder
  OSErr err = noErr;
  IconFamilyHandle icns = (IconFamilyHandle)NewHandle(8);
  // err = MemError();
  (**icns).resourceType = kIconFamilyType; // 'icns'
  (**icns).resourceSize = 8;

  // Then add each icon of the appropriate type
  int size = 64; // size of an ics# resource
  Handle bitmap = NewHandle(size);
  memset(*bitmap, 0, 32); // no 1-bit icon
  memset(*bitmap+32, 0xff, 32); // the whole square is the hit area
  err = SetIconFamilyData(icns, kSmall1BitMask, bitmap); // 'ics#'
  DisposeHandle(bitmap);

  // Write the icns file
  FSSpec someFSSpec; // ...
  err = WriteIconFile(icns, &someFSSpec);
  DisposeHandle(icns);

Icons are available in various sizes.  The only sizes we care about are small (16x16), large (32x32), huge (48x48), and "thumbnail" (128x128).  If an icon is available at exactly a specific size, it should be used.  Otherwise, don't add it to the file, the OS will do scaling.

For each icon size, there are a few different icons that can be provided, corresponding to different bit depths and mask types.  The only ones we care about are the 32-bit color icon, the 8-bit transparency mask (alpha channel), and the 1-bit hit mask.

With all of that in mind, the types we need are:
small (16x16)       data 'is32', alpha 's8mk', hit 'ics#'
large (32x32)       data 'il32', alpha 'l8mk', hit 'ICN#'
huge  (48x48)       data 'ih32', alpha 'h8mk', hit 'ich#'
thumbnail (128x128) data 'it32', alpha 't8mk', hit derived from alpha

These are the types used for the second argument to SetIconFamilyData.  You should probably use the symbolic constant names from IconStorage.h instead, but please put the resource type in a comment because that's what people recognize these things by.

For small, medium, and large icons, if alpha isn't present, derive it from the hit mask, since I'm not sure that all OS versions are bright enough to do this automatically.

32-bit icon data is supplied in a big-endian 4Bpp ARGB bitmap, row major.  The alpha is along for the ride only and isn't used.  Best advice is to set it to match the 8-bit mask anyway (or 1-bit mask if there is no 8-bit mask).  Watch out for byte sex.

8-bit alpha channels are 1Bpp, row major, 0 is fully transparent and 0xff is fully visible.

1-bit alpha masks are 8ppB, row major, 0 for no hit and 1 for hit.  The structure of these guys is a little weird: the ics#, ICN#, and ich# resources are actually a 1-bit icon bitmap followed by the 1-bit mask, so the byte size of the resource must be (rows * cols) / 4.  Since we won't have 1-bit icons, just zero out the first (rows * cols) / 8 bytes, and put the hit mask after that.  This oddity (and some others) are by-products of the fact that an icns file is a flattened icns resource, which in turn is a collection of other resource types that have been with the Mac since color displays were considered luxuries.

There are also provisions for 4- and 8-bit icons indexed to system palettes that nobody uses any more.  They're a throwback to the pre-OS X days and we can ignore them.  There's also a 16Wx12H "mini" icon size that nobody needs to worry about.

Could I interest you guys in allowing developers the option of supplying icons in some vector format (SVG?) so that all of the sizes of icons can be dynamically created at install time?
Would icon authors be able to provide a file for each icon size in the .icns/.ico file or would they all be generated from a single image file? It would be nice to be able to provide an image for each size, because icons can get muddy and blurry when simply scaled down from the original 128x128 image to 16x16 or 32x32, even if the original is in vector format. A good example is the Firefox icon, which has hand-tweaked 32x32 and 16x16 versions.
Kevin, the discussion imgIEncoder is all about being able to provide multiple sizes.

Mark, can we skip the 48x48 icon in normal cases (dock/document icons)? Since PNGs don't have hit-test information (AFAIK), I think we're going to have to generate the hittest data from the alpha transparency, and not the other way 'round.
(In reply to comment #6)
> Mark, can we skip the 48x48 icon in normal cases (dock/document icons)?

You CAN leave out any size you want and the system will scale other sizes up and down as needed.  I wouldn't recommend skipping 48x48, though.  It's the default size of icons on the desktop.  That's why I made a call for the option to supply vector icons, which I'll make again here.  At Kevin's request, a vector source should be overridable by bitmaps on a per-size basis.

> Since
> PNGs don't have hit-test information (AFAIK), I think we're going to have to
> generate the hittest data from the alpha transparency, and not the other way
> 'round.

That's fine.
Summary: API to generated mac .icns from PNGs → API to generate mac .icns from PNGs
Icons from SVG would be nice, but are a much lower priority... I'm sure that cairo-graphics will at some point in the near future have a way to do SVG->arbitrary sized bitmap.
Assignee: joshmoz → cbarrett
(In reply to comment #8)
> Icons from SVG would be nice, but are a much lower priority... I'm sure that
> cairo-graphics will at some point in the near future have a way to do
> SVG->arbitrary sized bitmap.

Is this currently the case? Is the imgIEncoder route still recommended? It would be really nice to have this functionality. As Mento said, the actual native code to generate the icns isn't terribly hard -- it's the API that seems problematic.
Assignee: mozcbarrett → joshmoz
Assignee: joshmoz → nobody
Matt - does Prism have some code to do this?
Yeah... see https://bugzilla.mozilla.org/show_bug.cgi?id=400164. The code is in components/src/mac (nsICNSEncoder.mm).
You need to log in before you can comment on or make changes to this bug.