The 24-bit icon elements and the ARGB elements store their pixels with a simple
run-length encoding. It resembles PackBits, but its repeat counts are different, so a
PackBits decoder cannot read it. Apple does not publish it; what follows comes from
independent work and was checked on icon files from macOS, with Apple’s
sips command as the reference decoder.
Channels
Each channel is encoded on its own. An element holds its channels one after the other, with nothing between them and no length for each. A channel ends when it has made one byte for each pixel, and the next one starts at the following byte.
| Type | Pixels | Before the channels | Channels, in order | Bytes when unpacked |
|---|---|---|---|---|
is32 | 16 × 16 | nothing | red, green, blue | 256 each |
il32 | 32 × 32 | nothing | red, green, blue | 1,024 each |
ih32 | 48 × 48 | nothing | red, green, blue | 2,304 each |
it32 | 128 × 128 | 4 bytes, normally zero | red, green, blue | 16,384 each |
ic04 | 16 × 16 | ARGB | alpha, red, green, blue | 256 each |
ic05 | 32 × 32 | ARGB | alpha, red, green, blue | 1,024 each |
icsb | 18 × 18 | ARGB | alpha, red, green, blue | 324 each |
The four bytes before an it32’s channels are normally zero. The icnsutil survey
reports that any value is accepted and ignored; Pillow’s reader accepts only zero. Why they
are there is not known.
Elements with no alpha channel get their transparency from a separate 8-bit mask element of the same size, which is not compressed.
0Type e.g.il324 bytes4Length of the whole element, big-endian4 bytes8Red channel packets making 1,024 bytesvaries…Green channel packets making 1,024 bytesvaries…Blue channel packets making 1,024 bytesvaries
il32 element: three channels of packetsPackets
A channel is a list of packets. Each packet starts with a control byte, which says what follows:
| Control byte | Packet | Output | Bytes in the packet |
|---|---|---|---|
00 to 7F | Literal | The next control + 1 bytes, copied (1 to 128) | control + 2 |
80 to FF | Repeat | The next byte, control − 125 times (3 to 130) | 2 |
The bytes 81 07 02 AA BB CC unpack like this:
| Bytes | Packet | Output |
|---|---|---|
81 07 | Repeat: 0x81 is 129, and 129 − 125 is 4 | 07 07 07 07 |
02 AA BB CC | Literal: 2 + 1 is 3 bytes | AA BB CC |
A control byte of 80 repeats its byte three times. In
PackBits, the same value does nothing, and the bytes
81 to FF mean 128 down to 2 repeats, so the two encodings are not
interchangeable.
A decoder should stop a channel once it has one byte per pixel. Packets that would make more than that, or data that ends before a channel is finished, mean the element is damaged.
ARGB elements
The types ic04, ic05 and icsb are written in this
form when their data starts with the four letters ARGB. Four channels follow
in the order alpha, red, green, blue, each packed as above. The alpha channel makes a
separate mask element unnecessary. The same types can hold a
PNG or JPEG 2000 file instead.
In a check on macOS 27, iconutil wrote the 16 and 32 pixel files of an
.iconset as ic04 and ic05 in this ARGB form.
Uncompressed pixels
A few files checked on macOS hold is32, il32 and
it32 data with no compression at all: four bytes a pixel, in the order alpha,
red, green, blue, and no header bytes even in an it32. Such an element is
longer than three packed channels can be, so its length alone says what it is. The explorer
reads these, and takes the transparency from the element’s 8-bit mask when it has one.
Drawn this way, they matched sips on macOS 27.
Pillow’s reader, which is independent work and not Apple’s, also accepts an element whose data is exactly three bytes a pixel as plain red, green and blue bytes, interleaved. This explorer does the same, but only when the data does not read as packets, because a packed element can happen to be that long.