- Shipped
- September 6, 2026 at 4:30 AM UTC
- Author
- Kamo
- Commit
- 3f4c60b
A tab icon uploaded as a transparent PNG-8 arrived in the browser tab as a black square with a ragged logo on it. Measured on the upload that exposed it: of the 9216 pixels in favicon-96x96.png, the 5194 that were transparent came out opaque black, and the mark that survived was down from 237 distinct colours to 46. The transparency was intact right up to the scale step. ImageIO decodes an indexed PNG to TYPE_BYTE_INDEXED with an IndexColorModel whose transparency is one palette entry — pixel (0,0) reads 0047704C, alpha zero, correctly. Thumbnailator preserves the source's image TYPE when it scales, so it built a fresh indexed image with a DEFAULT palette: the alpha entry was gone, the surviving indices addressed colours that no longer meant anything, and (0,0) came back FF000000. fitSquare's canvas was ARGB all along and faithfully copied pixels that were already black — which is why the bug looks like it lives there and does not. rasterise now normalises to TYPE_INT_ARGB before anything else touches the image. Converting once where the source is produced, rather than passing .imageType() at each of the seven call sites, makes "everything past this point is ARGB" hold for consumers added later, and costs one copy instead of seven. Not new, and not only PNG-8: GIF is always indexed, so any org whose img/logo.svg was really a GIF has had a black favicon for as long as the raster path has existed. It went unnoticed because the only source anyone fed it was an SVG, which Batik hands back as ARGB. It became reachable on purpose when tab icons started accepting raster uploads — PNG-8 is exactly what "save for web" gives you for a small flat-coloured icon. Four regression tests cover it: the PNGs, the .ico frames (which leave by a different road, packed by IcoEncoder), a GIF, and one asserting the mark keeps its own colour rather than merely its transparency. Every existing test in this class fed TYPE_INT_ARGB, the one input shape that never had the problem. Verified against the real uploaded file: 5185/9216 pixels transparent, matching the source's proportion, and 237 distinct colours where there had been 46. Organizations with a tab icon already generated need a re-provision to pick the new bytes up; the theme revision changes with it, so browsers will revalidate.