I'm looking at implementing the Image class from browsers, and are currently implementing ICO support. However, I'm stuck on figuring out the algorithm to decide which image from the ICO-file to load.
This is the code that I'm using inside the browser:
const img = new Image()
img.src = 'test.ico'
img.onload = () => {
console.log(img.width, img.height)
document.body.append(img)
}
When testing out some different images I have made the following observations:
- Apples favicon contains 4 images of sizes: 32, 16, 32, and 16. The first one is used in both Chrome and Safari.
- GitHubs favicon contains 2 images of sizes: 16, and 32. The second one is used in both Chrome and Safari.
- Windows98Scaled Icon 02.ico contains 14 images of sizes: 32, 16, 256, 128, 48, 32, 24, 16, 256, 128, 48, 32, 24, and 16. The third one is used in both Chrome and Safari.
One guess is that it takes the first images that has the largest size (e.g. first find the maximum size, then pick the first image that matches that size).
Is there any standards document governing how this should be done? Or any other source of documentation to follow?
According to the source code of Chrome and Firefox, images are first prioritised based on size, and secondarily based on bit depth.
Firefox:
https://github.com/mozilla/gecko-dev/blob/10a46f9dacc39a9305ef9cbfb27f8b68e25eccc9/image/decoders/nsICODecoder.cpp#L249-L258
Chrome:
https://github.com/chromium/chromium/blob/a23a83b6c0e9295e10b3d9e88c40da7395df5a18/third_party/blink/renderer/platform/image-decoders/ico/ico_image_decoder.cc#L129-L136
Note that in the case of images with equal size and bit-depth, the behaviour is slightly different. Firefox will always select the last image, whereas Chrome uses
std::sortwhich makes no guarantees on the ordering of equal elements.