<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: FlipConverto</title>
    <description>The latest articles on DEV Community by FlipConverto (@flipconverto).</description>
    <link>https://dev.to/flipconverto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4207690%2F463931eb-bd3c-46f7-a2c5-56563830f8a1.png</url>
      <title>DEV Community: FlipConverto</title>
      <link>https://dev.to/flipconverto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flipconverto"/>
    <language>en</language>
    <item>
      <title>What Surprised Us Writing an ICO and CUR Parser by Hand</title>
      <dc:creator>FlipConverto</dc:creator>
      <pubDate>Mon, 02 Nov 2026 06:54:23 +0000</pubDate>
      <link>https://dev.to/flipconverto/what-surprised-us-writing-an-ico-and-cur-parser-by-hand-1edk</link>
      <guid>https://dev.to/flipconverto/what-surprised-us-writing-an-ico-and-cur-parser-by-hand-1edk</guid>
      <description>&lt;p&gt;Browsers will decode an .ico for you, but you get one image back. We wanted every image in the file, so we wrote the parser ourselves in TypeScript. The format is small, and it still had plenty of surprises.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyctg12ny1tet12wez7k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyctg12ny1tet12wez7k7.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Zero means 256
Each image has a directory entry with its width and height in single bytes. A byte can't hold 256, so 0 means 256.&lt;/li&gt;
&lt;li&gt;The same bytes mean different things
Bytes 4 to 7 of an entry are the color planes and bits per pixel in an icon, but the hotspot X and Y in a cursor. The two formats differ only by a type field in the header (1 or 2). That's why a .ico renamed to .cur loads with a nonsense hotspot: Windows reported 1, 32 for a 32 px icon in our test.&lt;/li&gt;
&lt;li&gt;There are two image kinds
Since Vista, an entry can be a complete PNG, which you can copy out byte for byte. Otherwise it's BMP-style rows in 1, 4, 8, 24 or 32 bits, stored bottom-up and followed by a 1-bit AND mask.&lt;/li&gt;
&lt;li&gt;All-zero alpha means opaque
A 32-bit image can carry an alpha channel. But if every alpha value is 0, the icon counts as opaque and the mask applies. Skip that rule and some icons come out invisible.&lt;/li&gt;
&lt;li&gt;Inverted pixels can't be represented
Classic cursors can mark pixels that invert whatever is beneath them. PNG can't express that, so we make them transparent, as Chrome does.&lt;/li&gt;
&lt;li&gt;Canvas isn't exact for partial transparency
Browsers store canvas pixels premultiplied, so partly transparent pixels can shift slightly. We wrote our own PNG encoder using the Compression Streams API to keep exact values, with canvas as a fallback.
How we tested it
We ran the parser over every cursor in a Windows 11 PC's Cursors folder plus three app icons: 186 files holding 940 images. Every image matched ImageMagick's reading pixel for pixel. Median time was 3.9 ms per file, run outside the browser.
You can try it with flipconverto.com/convert/ico-to-png, which runs entirely client-side. Disclosure: FlipConverto is our site.
Further reading: Raymond Chen's "The evolution of the ICO file format" series (&lt;a href="https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513&lt;/a&gt;).
Note: this article was drafted with AI assistance and checked against our own test results.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>fileformats</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
