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:
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:
if (!biggestEntry ||
(e.mBitCount >= biggestEntry->mBitCount &&
e.mSize.width * e.mSize.height >=
biggestEntry->mSize.width * biggestEntry->mSize.height)) {
biggestEntry = &e;
if (!desiredSize) {
mDirEntry = &e;
}
}
Chrome:
bool ICOImageDecoder::CompareEntries(const IconDirectoryEntry& a,
const IconDirectoryEntry& b) {
// Larger icons are better. After that, higher bit-depth icons are better.
const int a_entry_area = a.size_.width() * a.size_.height();
const int b_entry_area = b.size_.width() * b.size_.height();
return (a_entry_area == b_entry_area) ? (a.bit_count_ > b.bit_count_)
: (a_entry_area > b_entry_area);
}
// ...
// Arrange frames in decreasing quality order.
std::sort(dir_entries_.begin(), dir_entries_.end(), CompareEntries);
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::sort
which makes no guarantees on the ordering of equal elements.