1. Introduction

Icons in AmigaOS and its derivatives (MorphOS, AROS), are basically the
same thing as on other operating systems – a small images identifying
files, directories, disks and so on. Except of image data, Amiga icons
contain some additional informations. File icons can contain a path to
an application used to open the project file when it is doubleclicked (the
default tool). They can also contain so-called tooltypes, which are just
strings used as program parameters. Dawer (folder) and disk icons contain
position, size and viewmode of a window opened on the desktop after
clicking the icon. All icons can store their position in parent drawer (or
desktop) window.

I've decided to write this unofficial specification, after I've found
none on the Internet. While Amiga icons are not very popular, as AmigaOS
itself is rather a niche, hobby OS, someone may find this article useful.
Note that there are a few kinds of Amiga icons. This article describes an
old, bitplane based format. Later NewIcons and PNG icons (as used in AmigaOS
4, MorphOS and AROS) are not described here.

2. Real and default icons

Icons in AmigaOS are separate files, with the same name as the 'main'
file, and '.info' extension at the end. Note that if a file has an extension
by itself (for example 'archive.lha'), the '.info' extension does not
replace file extension, but is appended at the end, so an icon for
'archive.lha' has the name 'archive.lha.info'. It may look strange for a
Windows user, but comes from the fact, that Amiga system does not use
extensions for file type recognition at all. An executable file without an
'.exe' is perfectly OK for example. To say more, executable files on
Amiga have no extension at all usually.

When a file has no icon associated, it is invisible in Workbench window
until "Show all files" viewmode is selected. Then a default icon for the
file is used. Older AmigaOS versions have stored default icons in
"ENVARC:sys/def_xxxx.info", where xxxx is 'drawer', 'project', 'tool' or
'disk'. Some extensions to this system have been developed, which allowed
different icons for different file types. What is important here, default
icons have the same file format as real ones. One can easily create a real
icon from a default one, using system tools, or just by copying the icon and
changing its name.

3. Amiga icon structure

The icon file is composed directly of some system structures. It has an
advantage of fast icon loading and displaying, the file is just loaded to
memory, structures addresses are extracted and may be passed directly to
Intuition, the main Amiga UI library. On the other hand many fields of these
system structures are unused or redundant, making the icon file bigger.
Some data in these structures are referenced via pointers in memory, in an
icon file, the data are stored after structures in a defined order.

The selected state of an Amiga icon is not generated programatically, but
is specified in the icon itself. Often the selected state is a second, separate
image, sometimes it is specified as palette entries swap.

3.1. Format overview.

The icon file starts from a DiskObject structure, which embeds Gadget
structure. Then there may be a DrawerData structure, followed by one or two
Image structures. After them raster image data for Image(s) follows.
Then there is a default tool string and tooltypes strings. DrawerData
contains a NewWindow structure. Here is a block diagram of an Amiga icon:

In memory a pointer
to the first Image, used for not selected state. In file it should be any non-zero
value. Zero here should not happen.

0026

uint32

do_Gadget.SelectRender

In memory a pointer
to the second Image, used for selected state. In file non-zero value means that the
icon has the second Image and raster data.

0030

uint32

do_Gadget.GadgetText

Unused. Usually 0.

0034

uint32

do_Gadget.MutualExclude

Unused. Usually 0.

0038

uint32

do_Gadget.SpecialInfo

Unused. Usually 0.

0042

uint16

do_Gadget.GadgetID

Unused. Usually 0.

0044

uint32

do_Gadget.UserData

Used for icon revision. 0 for
OS 1.x icons. 1 for OS 2.x/3.x icons.

0048

uint8

do_Type

A type of icon:

1 – disk or volume.

2 – drawer (folder).

3 – tool (executable).

4 – project (data file).

5 – trashcan.

6 – device.

7 – Kickstart ROM image.

8 – an appicon (placed on the desktop by application).

0049

uint8

padding

Just padding byte.

0050

uint32

do_DefaultTool

In memory a pointer to a
default tool path string. In file should be interpreted as boolean field indicating
default tool presence.

0054

uint32

do_ToolTypes

In memory a pointer to a table
containing pointers to tooltype strings. In file should be interptered as boolean
field indicating tooltypes table presence.

0058

int32

do_CurrentX

Virtual horizontal position of
the icon in the drawer window.

0062

int32

do_CurrentY

Virtual vertical position of the
icon in the drawer window.

0066

uint32

do_DrawerData

In memory a pointer to
DrawerData structure. In file should be interpreted as a boolean field indicating
DrawerData presence.

0070

uint32

do_ToolWindow

Unused.

0074

uint32

do_StackSize

Task stack size for an application.
(in case of project file, this size is for default tool application).

Total size: 78 bytes.

3.3. DrawerData structure

The structure starts from a NewWindow structure. Note that DrawerData may be just skipped when
only icon image is to be decoded. I've put the information here just for completness.

0000

int16

dd_NewWindow.LeftEdge

Drawer
window left edge relative to the Workbench screen.

0002

int16

dd_NewWindow.TopEdge

Drawer window top edge relative to the
Workbench screen.

0004

int16

dd_NewWindow.Width

Drawer window width.

0006

int32

dd_NewWindow.Height

Drawer window height.

0008

uint8

dd_NewWindow.DetailPen

Number of graphics pen used to render
window details.

0009

uint8

dd_NewWindow.BlockPen

Number of graphics pen used to
render window frame background.

0010

uint32

dd_NewWindow.IDCMPFlags

Kinds of IDCMP (GUI -> application)
events requested.

0014

uint32

dd_NewWindow.Flags

Various window flags (borders, system
gadgets etc.).

0018

uint32

dd_NewWindow.FirstGadget

In memory a pointer to the first
window gadget in a linked list. Unused in an icon file.

0022

uint32

dd_NewWindow.CheckMark

In memory a pointer to checkmark
imagery for the window. Unused in an icon file.

0026

uint32

dd_NewWindow.Title

In memory a pointer to the window title
string. Unused in an icon file.

0030

uint32

dd_NewWindow.Screen

In memory a pointer to system Screen
a window is to be opened on. Unused in an icon file.

0034

uint32

dd_NewWindow.BitMap

In memory points to a system BitMap
for the window. Unused in an icon file.

0038

int16

dd_NewWindow.MinWidth

Minimum width for the window.

0040

int16

dd_NewWindow.MinHeight

Minimum height for the window.

0042

uint16

dd_NewWindow.MaxWidth

Maximum width for the window.

0044

uint16

dd_NewWindow.MaxHeight

Maximum height for the window.

0046

uint16

dd_NewWindow.Type

Window type (public/custom screen).

0048

int32

dd_CurrentX

Horizontal position of originating icon.

0052

int32

dd_CurrentY

Vertical position of originating icon.

Total size: 56 bytes.

3.4. Image structure

0000

int16

LeftEdge

Image
left edge position relative to the icon left edge. Image clipping should be done for negative
values.

0002

int16

TopEdge

Image top edge position relative to
the icon top edge. Image clipping should be done for negative values.

0004

uint16

Width

Image width in pixels. May be less
than icon width (stored in DiskObject.Gadget), missing columns use color 0. If it is
bigger than icon width I recommend to clip the image.

0006

uint16

Height

Image height in pixels. May be less
than icon height (stored in DiskObject.Gadget), missing rows use color 0. If it is
bigger than icon height I recommend to clip the image.

0008

uint16

Depth

Number of image bitplanes (see chapter
3.5.).

0010

uint32

ImageData

In memory it is a pointer to
bitplanes, in file it should be treated as a boolean value (if not zero, bitplane
data are stored as shown on fig. 1.).

0014

uint8

PlanePick

A bitfield controlling which
image bitplane is copied to which screen bitplane. Used only by classic Amiga
graphics chipset. Meaningless in a file, as it is interpreted in Amiga chipset
context displaying particular screen.

0015

uint8

PlaneOnOff

A bitfield controlling
screen bitplanes not fed with icon data. They may be either filled by zeros or
by ones. Used only by classic Amiga graphics chipset. Meaningless in a file,
as it is interpreted in Amiga chipset context displaying particular screen.

0016

uint32

NextImage

Unused. Usually 0.

Total size: 20 bytes.

3.5. Image data and palette

Icon image data are stored as bitplanes. While a bit cumbersome for nowadays display devices,
this format is a native one for Amiga graphics chipsets. Then image data, once loaded into
graphics ("chip") memory, can be directly blitted to a screen. On bitplanes, every pixel
occupies one bit, regardless of number of palette colors. Palette size is determined by a
number of bitplanes, for N bitplanes there are 2N colors available.
To determine a pixel color, one has to gather this pixel bits from all bitplanes (bitplane 0,
which comes first in the data, is the most significant one) as shown on fig. 2, form a number
and use it as an index to the palette table.

Fig. 2. Bitplaned image data.

Amiga icon bitplanes are not interleaved, complete planes are stored one after one. Pixel
scanning order is usual left-to-right, top-to-bottom. Plane rows are padded to 16-bit words,
there is no vertical padding.

In spite of image data are palette-based, there is no palette stored in
the icon, just some standard palette is assumed. Unfortunately there are a few
"standard" palettes used:

There were some other palettes proposed, usually extending MagicWB with more colors, but
they have not gained popularity. NewIcons format solved the problem finally, storing a palette
inside an icon. Which palette one should use converting the icon image to RGB color space?
If we limit possibilities to palettes shown above, icon revision allows to choose between OS 1.x and OS 2.x
palette. Then if revision is 1 and number of bitplanes is 3, the icon is MagicWB one.

4. Decoding icon images

Usually you will be only interested in extracting images from an icon, and convert them to
the RGB color space (unless you are writing an Amiga Workbench replacement...). Here is a short guide
how to do it:

Take into account, that most of information in the icon is unused. All fields in DiskObject and Image
structures really needed to decode imagery are marked gray
in the above tables. White entries may be ignored. If you are on x86 or other little-endian platform,
do not forget about byte-swapping.

Start from loading a fixed size DiskObject structure. Verify do_Magic and do_Version. Then
check for presence of DrawerData. Non zero do_DrawerData is the primary indicator, do_Type of
1, 2 or 5 is the secondary one. Check for presence of the first image (do_Gadget.GadgetRender). The
second image primary indicator is do_Gadget.SelectRender, the secondary one is that gadget highlight
bits are set to 2 (do_Gadget.Flags & 0x0003 == 0x0002).

If DrawerData is present, skip it.

Read the first Image structure. Extract offsets, width, height and number of bitplanes. Calculate bitplane width
in bytes taking horizontal padding into account. It may be calculated as
((Width + 15) >> 4) << 1.

Check for ImageData. If it is 0, it may mean that the image is empty, or the file is damaged. If not,
load bitplanes to memory.

If you've detected the second Image, repeat 4. and 5. Be prepared for different offsets and dimensions (may
happen often) or different bitplanes number (very unlikely, but who knows).

Select the palette for every image, based on icon revision and number of bitplanes. Convert bitplanes to
a RGB pixelmap, using selected palette. Note that padding bits should be ignored even if non-zero.

Create an empty rectangle of icon size (do_Gadget.Width × do_Gadget_Height). Fill it
with palette color 0. Then impose the normal or selected image on it using Image LeftEdge and
TopEdge as offsets. Perform clipping if neccesary.