Ouvrez un fichier ICO dans un éditeur hexadécimal. Les six premiers octets :
00 00 01 00 03 00
C'est l'intégralité de l'en-tête du fichier. 00 00 est le champ Réservé — toujours zéro. 01 00 est le champ Type — 1 signifie qu'il s'agit d'un fichier icône (les fichiers curseur utilisent 2). 03 00 est le champ Compteur — little-endian pour 3, ce qui signifie que cet ICO contient trois images distinctes. Six octets pour décrire un conteneur qui héberge trois fichiers image indépendants. Le reste du fichier est constitué de métadonnées à propos de ces images, suivies des données image elles-mêmes.
ICO n'est ni un algorithme de compression ni un espace colorimétrique : c'est un conteneur, le format de conteneur le plus simple et le plus rudimentaire encore utilisé quotidiennement sur toutes les machines Windows et dans tous les navigateurs majeurs.
D'où Vient ICO
Microsoft a introduit ICO avec Windows 1.0 en 1985. L'écosystème PC de 1985 n'avait aucune notion de fichier image standardisé. Pas de JPEG, pas de PNG, pas de GIF. Les données bitmap étaient des tableaux bruts de pixels, et chaque programme gérait son propre stockage. Microsoft avait besoin d'un moyen de livrer des icônes pour les programmes, les dossiers et les éléments d'interface système. Les exigences étaient simples :
- Un fichier par icône, quel que soit le nombre de tailles dont l'OS avait besoin
- Recherche rapide à l'exécution — l'OS ne devait pas avoir à décoder une image pour connaître ses dimensions
- Empreinte mémoire réduite sur des systèmes dotés de 256 Ko de RAM
La solution était une structure de répertoire. Le fichier commence par un en-tête indiquant combien d'images il contient. Puis vient un tableau d'entrées, chacune décrivant la largeur, la hauteur, la profondeur de couleur et le décalage dans le fichier d'une image. L'OS lit le répertoire, choisit l'entrée correspondant au contexte d'affichage actuel, se positionne à ce décalage, et affiche le bitmap.
Cette conception est antérieure de deux ans à ZIP, de deux ans à GIF, et de sept ans à JPEG. ICO était un système de fichiers miniature avant que les systèmes de fichiers ne deviennent intéressants.
Pourquoi ICO Existe Tout Court
Une icône, ce n'est pas une image unique, mais un ensemble d'images aux tailles différentes. Windows affiche la même icône en 16 × 16 dans une liste de fichiers, en 32 × 32 sur le bureau, en 48 × 48 dans le volet détails de l'Explorateur, et en 256 × 256 dans la vue « très grandes icônes ». Un écran haute DPI à 200 % a besoin d'une icône 32 × 32 rendue à partir d'une source 64 × 64. L'OS choisit la taille adaptée au contexte, et ce choix doit s'effectuer en un temps imperceptible.
Si les icônes étaient stockées sous forme de fichiers PNG individuels, l'OS devrait ouvrir plusieurs fichiers, décoder chacun, et mettre en cache les résultats. Avec ICO, l'OS ouvre un seul fichier, lit une entrée de répertoire de 16 octets, et saute directement aux données bitmap. Le répertoire rend le format auto-descriptif. Pas de décodeur nécessaire pour les métadonnées.
Le cas des favicons est identique. Quand un navigateur demande /favicon.ico, il obtient un seul fichier contenant chaque taille dont il pourrait avoir besoin — 16 × 16 pour l'onglet, 32 × 32 pour la barre de favoris, 180 × 180 pour les raccourcis sur l'écran d'accueil iOS. Le navigateur choisit la bonne entrée sans analyser les en-têtes image.
L'En-tête du Fichier
L'en-tête ICO comporte deux parties : l'ICONDIR et le tableau ICONDIRENTRY.
ICONDIR (6 octets)
Bytes 0-1: Reserved (must be 0)
Bytes 2-3: Type (1 = icon, 2 = cursor)
Bytes 4-5: Count (number of images, little-endian uint16)
Chaque fichier ICO commence par 00 00 01 00. Les deux octets suivants indiquent le nombre d'images qui suivent.
ICONDIRENTRY (16 octets chacune)
Chaque image reçoit une entrée :
Bytes 0: Width (in pixels; 0 means 256)
Bytes 1: Height (in pixels; 0 means 256)
Bytes 2: ColorCount (0 if more than 256 colors)
Bytes 3: Reserved (always 0)
Bytes 4-5: Planes (1 for icons)
Bytes 6-7: BitCount (bits per pixel: 1, 4, 8, 24, or 32)
Bytes 8-11: BytesInRes (size of image data in bytes, uint32)
Bytes 12-15: ImageOffset (byte offset to image data from file start, uint32)
Les champs Largeur et Hauteur font un octet chacun. La valeur maximale explicitement stockable est 255. Quand une image fait 256 x 256 pixels, le champ stocke 0x00, que les décodeurs interprètent comme 256. Cette particularité fait partie de la spécification depuis 1985.
Voici à quoi ressemble le répertoire pour un ICO avec trois images — un BMP 16 x 16, un BMP 32 x 32, et un PNG 256 x 256 :
Offset 0x00: 00 00 01 00 03 00 -- ICONDIR: 3 images
Offset 0x06: 10 10 00 00 01 00 18 00 2C 01 00 00 16 00 00 00 -- 16x16, 24bpp, BMP, 300 bytes at 0x16
Offset 0x16: 20 20 00 00 01 00 18 00 A8 02 00 00 42 01 00 00 -- 32x32, 24bpp, BMP, 680 bytes at 0x142
Offset 0x26: 00 00 00 00 01 00 20 00 B4 1C 00 00 EA 03 00 00 -- 256x256, 32bpp, PNG, 7348 bytes at 0x3EA
L'OS lit le répertoire, repère l'entrée qui correspond à ses besoins et se positionne directement à l'ImageOffset, sans phase d'analyse ni d'hypothèse — un accès mémoire immédiat.
Pourquoi PNG, WebP et JPEG N'ont Jamais Remplacé ICO
À chaque métrique moderne, PNG est un meilleur format que l'encodage bitmap interne d'ICO. PNG offre une meilleure compression, une vraie transparence alpha, et des outils largement répandus. WebP est encore plus compact. JPEG gère les photographies. Pourtant ICO persiste. Trois raisons :
1. Multi-résolution en un seul fichier. PNG stocke une image. ICO en stocke un nombre arbitraire. Un site web pourrait servir une archive ZIP de PNG, mais aucun navigateur ne saurait comment choisir le bon pour un favicon. La structure de répertoire d'ICO résout cela en six octets.
2. Liaison aux API système. LoadIcon, ExtractIcon, et SHGetFileInfo de Windows attendent toutes des données ICO. L'API Win32 n'a pas d'équivalent pour des conteneurs d'icônes PNG. Changer cela rendrait incompatibles toutes les applications Windows compilées depuis 1985. Microsoft ne rompt jamais la compatibilité descendante.
3. Le standard favicon. La balise HTML <link rel="icon"> accepte PNG, SVG et ICO, mais la requête implicite par défaut pour /favicon.ico est antérieure à ces options. Chaque navigateur depuis Internet Explorer 5 (1999) demande /favicon.ico par défaut. Les sites qui ne déclarent pas explicitement un lien de favicon ont toujours besoin d'un ICO à ce chemin, sinon le navigateur obtient un 404.
PNG n'a pas remplacé ICO parce qu'ICO n'a jamais concouru sur la qualité d'image. Il concourait sur la sémantique de conteneur, et aucun autre format n'offre le même modèle de répertoire-dans-un-fichier avec trente ans de support système.
Ce qui se Trouve à l'Intérieur du Conteneur
Chaque entrée d'un fichier ICO pointe vers une image indépendante. Les données image peuvent être l'un des deux formats suivants :
Encodage BMP (Hérité)
Avant Windows Vista, toutes les images ICO étaient des bitmaps BMP non compressés ou compressés RLE. Les données stockées sont un DIB (Device Independent Bitmap) — un BMP sans le BITMAPFILEHEADER. Il commence directement par le BITMAPINFOHEADER :
Bytes 0-3: Header size (40 for BITMAPINFOHEADER)
Bytes 4-7: Width (uint32)
Bytes 8-11: Height (uint32, doubled for XOR + AND masks)
Bytes 12-13: Planes (1)
Bytes 14-15: BitCount (1, 4, 8, 24, or 32)
Bytes 16-19: Compression (0 for uncompressed, 1 or 2 for RLE)
Bytes 20-23: Image size (0 if uncompressed)
Bytes 24-27: X pixels per meter
Bytes 28-31: Y pixels per meter
Bytes 32-35: Colors used
Bytes 36-39: Important colors
Le champ Hauteur dans l'en-tête DIB est le double de la hauteur réelle de l'icône. Une icône 32 x 32 stocke 64 dans le champ Hauteur. La moitié supérieure est le masque XOR (l'image couleur réelle), et la moitié inférieure est le masque AND (un bitmap de transparence 1 bit). Pour les icônes 32 bits avec canal alpha, le masque AND est généralement ignoré.
Pour les icônes encodées BMP en 24 bits et moins sans alpha, le masque AND est le seul mécanisme de transparence. Un 1 dans le masque AND signifie transparent ; un 0 signifie opaque. C'est ainsi que Windows obtenait la transparence avant que la couleur 32 bits ne devienne standard.
Encodage PNG (Windows Vista+)
À partir de Windows Vista, les fichiers ICO peuvent stocker des images encodées en PNG. Les données image au décalage spécifié dans ICONDIRENTRY sont un fichier PNG brut — complet avec sa propre signature 89 50 4E 47 et sa structure complète de chunks PNG.
C'est le format à utiliser pour les icônes 256 x 256. Un BMP 32 bits 256 x 256 non compressé ferait environ 262 Ko. La même image en PNG fait typiquement 20 à 60 Ko. Les outils d'icônes modernes génèrent des entrées encodées PNG pour le 256 x 256 et des entrées encodées BMP pour les tailles inférieures, offrant le meilleur équilibre entre compatibilité et taille de fichier.
Comparaison des Encodages
| Encodage | Apparition | Compression | Gestion de l'alpha | Usage recommandé |
|---|---|---|---|---|
| BMP | Windows 1.0 (1985) | Aucune ou RLE | Masque AND 1 bit | 16 × 16 à 48 × 48 |
| PNG | Windows Vista (2006) | DEFLATE | Alpha 8 bits | 64 × 64 à 256 × 256 |
Détecter et Inspecter les Fichiers ICO
Ne faites pas confiance à l'extension .ico. Lisez les six premiers octets et analysez le répertoire.
TypeScript
interface IcoEntry {
width: number
height: number
bitCount: number
bytesInRes: number
imageOffset: number
}
interface IcoInfo {
valid: boolean
type: "icon" | "cursor" | "unknown"
count: number
entries: IcoEntry[]
}
async function inspectIco(file: File): Promise<IcoInfo> {
const buffer = await file.slice(0, 1024).arrayBuffer()
const bytes = new Uint8Array(buffer)
if (bytes.length < 6)
return { valid: false, type: "unknown", count: 0, entries: [] }
const reserved = bytes[0] | (bytes[1] << 8)
const type = bytes[2] | (bytes[3] << 8)
const count = bytes[4] | (bytes[5] << 8)
if (reserved !== 0 || (type !== 1 && type !== 2)) {
return { valid: false, type: "unknown", count: 0, entries: [] }
}
const entries: IcoEntry[] = []
const dirSize = 6 + count * 16
if (bytes.length < dirSize) {
return { valid: false, type: "unknown", count: 0, entries: [] }
}
for (let i = 0; i < count; i++) {
const off = 6 + i * 16
entries.push({
width: bytes[off] === 0 ? 256 : bytes[off],
height: bytes[off + 1] === 0 ? 256 : bytes[off + 1],
bitCount: bytes[off + 6] | (bytes[off + 7] << 8),
bytesInRes:
bytes[off + 8] |
(bytes[off + 9] << 8) |
(bytes[off + 10] << 16) |
(bytes[off + 11] << 24),
imageOffset:
bytes[off + 12] |
(bytes[off + 13] << 8) |
(bytes[off + 14] << 16) |
(bytes[off + 15] << 24),
})
}
return {
valid: true,
type: type === 1 ? "icon" : "cursor",
count,
entries,
}
}
Python
import struct
from typing import TypedDict
class IcoEntry(TypedDict):
width: int
height: int
bit_count: int
bytes_in_res: int
image_offset: int
class IcoInfo(TypedDict):
valid: bool
type: str
count: int
entries: list[IcoEntry]
def inspect_ico(path: str) -> IcoInfo:
with open(path, "rb") as f:
header = f.read(1024)
if len(header) < 6:
return {"valid": False, "type": "unknown", "count": 0, "entries": []}
reserved, type_, count = struct.unpack("<HHH", header[:6])
if reserved != 0 or type_ not in (1, 2):
return {"valid": False, "type": "unknown", "count": 0, "entries": []}
dir_size = 6 + count * 16
if len(header) < dir_size:
return {"valid": False, "type": "unknown", "count": 0, "entries": []}
entries: list[IcoEntry] = []
for i in range(count):
off = 6 + i * 16
w, h, colors, _, planes, bit_count = struct.unpack("<BBBBHH", header[off:off+8])
bytes_in_res, image_offset = struct.unpack("<II", header[off+8:off+16])
entries.append({
"width": 256 if w == 0 else w,
"height": 256 if h == 0 else h,
"bit_count": bit_count,
"bytes_in_res": bytes_in_res,
"image_offset": image_offset,
})
return {
"valid": True,
"type": "icon" if type_ == 1 else "cursor",
"count": count,
"entries": entries,
}
Go
package main
import (
"encoding/binary"
"fmt"
"os"
)
type IcoEntry struct {
Width int
Height int
BitCount int
BytesInRes uint32
ImageOffset uint32
}
type IcoInfo struct {
Valid bool
Type string
Count int
Entries []IcoEntry
}
func inspectIco(path string) (IcoInfo, error) {
f, err := os.Open(path)
if err != nil {
return IcoInfo{}, err
}
defer f.Close()
buf := make([]byte, 1024)
n, _ := f.Read(buf)
buf = buf[:n]
if n < 6 {
return IcoInfo{Valid: false, Type: "unknown"}, nil
}
reserved := binary.LittleEndian.Uint16(buf[0:2])
typ := binary.LittleEndian.Uint16(buf[2:4])
count := binary.LittleEndian.Uint16(buf[4:6])
if reserved != 0 || (typ != 1 && typ != 2) {
return IcoInfo{Valid: false, Type: "unknown"}, nil
}
dirSize := 6 + int(count)*16
if n < dirSize {
return IcoInfo{Valid: false, Type: "unknown"}, nil
}
entries := make([]IcoEntry, 0, count)
for i := 0; i < int(count); i++ {
off := 6 + i*16
w := buf[off]
h := buf[off+1]
bitCount := binary.LittleEndian.Uint16(buf[off+6 : off+8])
bytesInRes := binary.LittleEndian.Uint32(buf[off+8 : off+12])
imageOffset := binary.LittleEndian.Uint32(buf[off+12 : off+16])
if w == 0 { w = 256 }
if h == 0 { h = 256 }
entries = append(entries, IcoEntry{
Width: int(w),
Height: int(h),
BitCount: int(bitCount),
BytesInRes: bytesInRes,
ImageOffset: imageOffset,
})
}
typStr := "icon"
if typ == 2 {
typStr = "cursor"
}
return IcoInfo{Valid: true, Type: typStr, Count: int(count), Entries: entries}, nil
}
PHP
function inspectIco(string $path): array {
$header = file_get_contents($path, false, null, 0, 1024);
if (strlen($header) < 6) {
return ["valid" => false, "type" => "unknown", "count" => 0, "entries" => []];
}
$reserved = unpack("v", substr($header, 0, 2))[1];
$type = unpack("v", substr($header, 2, 2))[1];
$count = unpack("v", substr($header, 4, 2))[1];
if ($reserved !== 0 || ($type !== 1 && $type !== 2)) {
return ["valid" => false, "type" => "unknown", "count" => 0, "entries" => []];
}
$dirSize = 6 + $count * 16;
if (strlen($header) < $dirSize) {
return ["valid" => false, "type" => "unknown", "count" => 0, "entries" => []];
}
$entries = [];
for ($i = 0; $i < $count; $i++) {
$off = 6 + $i * 16;
$w = ord($header[$off]);
$h = ord($header[$off + 1]);
$bitCount = unpack("v", substr($header, $off + 6, 2))[1];
$bytesInRes = unpack("V", substr($header, $off + 8, 4))[1];
$imageOffset = unpack("V", substr($header, $off + 12, 4))[1];
$entries[] = [
"width" => $w === 0 ? 256 : $w,
"height" => $h === 0 ? 256 : $h,
"bit_count" => $bitCount,
"bytes_in_res" => $bytesInRes,
"image_offset" => $imageOffset,
];
}
return [
"valid" => true,
"type" => $type === 1 ? "icon" : "cursor",
"count" => $count,
"entries" => $entries,
];
}
ImageMagick CLI
magick identify -verbose favicon.ico | grep -E "(Print size|Resolution|Colorspace|Type)"
Lister toutes les images internes :
magick identify favicon.ico
Extraire une taille spécifique :
magick favicon.ico[2] extracted-256x256.png
Ou simplement :
file favicon.ico
# favicon.ico: MS Windows icon resource - 5 icons, 16x16, 32 bits/pixel, 32x32, 32 bits/pixel
Bonnes Pratiques et Cas d'Usage
ICO n'est pas un format image généraliste. C'est un artefact de déploiement pour des contextes spécifiques. Utilisez-le là où le contexte l'exige, et utilisez PNG partout ailleurs.
Favicons
Pour les sites web modernes, proposez les deux formats :
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
Les navigateurs choisissent le premier format qu'ils supportent. Les navigateurs compatibles SVG obtiennent l'icône vectorielle. Les anciens navigateurs et les utilitaires de favoris basculent sur ICO. Le fichier ICO devrait contenir :
| Taille | Encodage | Usage |
|---|---|---|
| 16 × 16 | BMP | Onglet navigateur, IE ancien |
| 32 × 32 | BMP | Barre des tâches, barre de favoris |
| 48 × 48 | BMP | Raccourcis Windows |
| 180 × 180 | PNG | Icône écran d'accueil iOS |
| 256 × 256 | PNG | Vue très grandes icônes de l'Explorateur |
Un favicon ICO avec ces cinq entrées fait typiquement 30 à 50 Ko. Sans le PNG 256 x 256, il tombe sous les 10 Ko.
Icônes d'Applications Windows
Les exécutables d'applications Windows intègrent une ressource ICO. L'OS charge la taille appropriée depuis le répertoire intégré à l'exécution. Pour les applications de bureau ciblant Windows 10 et 11, incluez :
- 16 x 16, 24 x 24, 32 x 32, 48 x 48 (BMP, pour la compatibilité descendante et le DPI standard)
- 64 x 64, 128 x 128, 256 x 256 (PNG, pour les écrans haute DPI)
Windows met automatiquement à l'échelle si une taille exacte manque, mais la mise à l'échelle est plus moche qu'un rendu natif d'une taille inférieure. Chaque taille omise coûte en qualité visuelle.
Quand Ne Pas Utiliser ICO
- Images de contenu web : utilisez WebP, JPEG ou PNG. ICO n'a aucun avantage de compression et aucun bénéfice de rendu natif navigateur pour les images en ligne.
- Photographies : ICO n'est pas conçu pour les images en tons continus à haute colorimétrie. Les tailles de fichier explosent.
- Ressources multiplateformes : macOS utilise
.icns, pas ICO. Linux utilise PNG ou SVG. ICO est un format natif Windows qui fonctionne par hasard sur le web.
L'Avenir d'ICO
ICO perdurera bien au-delà des pronostics qui annoncent sa mort. La raison reste la même qu'à l'origine : la compatibilité descendante.
Les favicons SVG sont techniquement supérieurs. Ils passent à n'importe quelle résolution, se compressent mieux que n'importe quel format matriciel, et supportent l'animation et l'interactivité. Chrome, Firefox et Safari supportent tous les favicons SVG. Pourtant ICO persiste parce que des millions d'appareils, de systèmes d'entreprise et d'anciens navigateurs demandent toujours /favicon.ico par défaut. Un site sans fichier ICO génère des 404 dans les logs serveur et affiche une icône cassée dans les logiciels plus anciens.
Windows 11 utilise encore ICO pour les icônes d'application, les icônes de dossier et l'interface système. L'API Win32 attend toujours des données ICO. Microsoft n'a montré aucun intérêt à remplacer ce format — il n'y a aucune raison commerciale de casser trente ans de compatibilité applicative.
Ce qui évolue réellement, c'est la manière dont on produit les fichiers ICO. Les chaînes d'outils modernes — IconKitchen, plugins Figma, générateurs en ligne — produisent des fichiers ICO avec des entrées haute résolution encodées PNG automatiquement. Le conteneur reste le même, mais la charge utile s'améliore.
Personne n'aime vraiment le format ICO. Mais il fonctionne partout, ne casse rien et ne coûte pratiquement rien à maintenir. C'est exactement pourquoi il sera encore là en 2035.
Si vous avez besoin de créer des fichiers ICO à partir d'images existantes, JPG to ICO convertit les sources JPG, PNG et WebP en fichiers ICO multi-résolution directement dans votre navigateur — aucun téléversement, aucun traitement serveur. Il génère la matrice de tailles standard et choisit automatiquement l'encodage PNG pour le 256 x 256 et l'encodage BMP pour les tailles inférieures, vous offrant l'équilibre optimal entre compatibilité et taille de fichier.



