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:
|DiskObject||DrawerData||Image 1||raster data
for Image 1
|Image 2||raster data
for Image 2
Fig. 1. A block diagram of Amiga icon file structure (gray elements are optional).
NOTE: All multi-byte fields of an Amiga icon are big-endian. x86 programmers have to swap bytes.
3.2. DiskObject structure
|0000||uint16||do_Magic||A file identifier. All icons have 0xE310 here.|
|0002||uint16||do_Version||Icon version. The current version is 1.|
|0004||uint32||do_Gadget.NextGadget||Unused. Contains 0 usually.|
|0008||int16||do_Gadget.LeftEdge||Horizontal position of the icon left edge relative to its parent window left edge. This field is only used when an icon is loaded into memory.|
|0010||int16||do_Gadget.TopEdge||Vertical position of the icon top edge relative to its parent window top edge. This field is only used when an icon is loaded into memory.|
|0012||uint16||do_Gadget.Width||Icon width in pixels.|
|0014||uint16||do_Gadget.Height||Icon height in pixels.|
|0016||uint16||do_Gadget.Flags||Gadget flags. Used only by Intuition, when the icon is loaded into memory. Usually set to 5.|
|0018||uint16||do_Gadget.Activation||Gadget activation flags. The usual value is 3 here (both 'immediate' and 'relverify' activation methods set).|
|0020||uint16||do_Gadget.GadgetType||Gadget type. The usual value is 1 here (means boolean gadget).|
|0022||uint32||do_Gadget.GadgetRender||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:
|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.|
|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||int16||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:
- Standard AmigaOS 1.x palette, 4 colors.
color 0, R=0, G=85, B=170 (0x0055AA)
color 1, R=255, G=255, B=255 (0xFFFFFF)
color 2, R=0, G=0, B=0 (0x000000)
color 3, R=255, G=136, B=0 (0xFF8800)
- Standard AmigaOS 2.x palette, 4 colors.
color 0, R=149, G=149, B=149 (0x959595)
color 1, R=0, G=0, B=0 (0x000000)
color 2, R=255, G=255, B=255 (0xFFFFFF)
color 3, R=59, G=103, B=162 (0x3B67A2)
- MagicWB palette, 8 colors. It extends AmigaOS 2.x palette with 4 additional colors.
color 4, R=123, G=123, B=123 (0x7B7B7B)
color 5, R=175, G=175, B=175 (0xAFAFAF)
color 6, R=170, G=144, B=124 (0xAA907C)
color 7, R=255, G=169, B=151 (0xFFA997)
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.