{"id":12513,"date":"2010-10-18T07:00:00","date_gmt":"2010-10-18T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/10\/18\/the-evolution-of-the-ico-file-format-part-1-monochrome-beginnings\/"},"modified":"2010-10-18T07:00:00","modified_gmt":"2010-10-18T07:00:00","slug":"the-evolution-of-the-ico-file-format-part-1-monochrome-beginnings","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20101018-00\/?p=12513","title":{"rendered":"The evolution of the ICO file format, part 1: Monochrome beginnings"},"content":{"rendered":"<p>\nThis week is devoted to the evolution of the ICO file format.\nNote that the icon resource format is different from the ICO file format;\nI&#8217;ll save that topic for another day.\n<\/p>\n<p>\nThe ICO file begins with a fixed header:\n<\/p>\n<pre>\ntypedef struct ICONDIR {\n    WORD          idReserved;\n    WORD          idType;\n    WORD          idCount;\n    ICONDIRENTRY  idEntries[];\n} ICONHEADER;\n<\/pre>\n<p>\n<code>idReserved<\/code> must be zero, and <code>idType<\/code> must be 1.\nThe <code>idCount<\/code> describes how many images are included in this\nICO file.\nAn ICO file is really a collection of images;\nthe theory is that each image is an alternate representation of the same\nunderlying concept, but at different sizes and color depths.\nThere is nothing to prevent you, in principle, from creating an ICO file\nwhere the 16&times;16 image looks nothing like the 32&times;32 image,\nbut your users will probably be confused.\n<\/p>\n<p>\nAfter the <code>idCount<\/code> is an array of <code>ICONDIRECTORY<\/code>\nentries whose length is given by <code>idCount<\/code>.\n<\/p>\n<pre>\nstruct IconDirectoryEntry {\n    BYTE  bWidth;\n    BYTE  bHeight;\n    BYTE  bColorCount;\n    BYTE  bReserved;\n    WORD  wPlanes;\n    WORD  wBitCount;\n    DWORD dwBytesInRes;\n    DWORD dwImageOffset;\n};\n<\/pre>\n<p>\nThe <code>bWidth<\/code> and <code>bHeight<\/code> are the dimensions of\nthe image.\nOriginally, the supported range was 1 through 255,\nbut starting in Windows&nbsp;95 (and Windows&nbsp;NT&nbsp;4),\nthe value 0 is accepted as representing a width or height of 256.\n<\/p>\n<p>\nThe <code>wBitCount<\/code> and\n<code><a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/12\/01\/273018.aspx\">wPlanes<\/a><\/code>\ndescribe the color depth of the image;\nfor monochrome icons, these value are both&nbsp;1.\nThe <code>bReserved<\/code> must be zero.\nThe <code>dwImageOffset<\/code> and <code>dwBytesInRes<\/code>\ndescribe the location (relative to the start of the ICO file)\nand size in bytes of the actual image data.\n<\/p>\n<p>\nAnd then there&#8217;s <code>bColorCount<\/code>.\nPoor <code>bColorCount<\/code>.\nIt&#8217;s supposed to be equal to the number of colors in the image;\nin other words,\n<\/p>\n<p ALIGN=\"center\">\n<code>bColorCount = 1 &lt;&lt; (wBitCount * wPlanes)<\/code>\n<\/p>\n<p>If <code>wBitCount * wPlanes<\/code> is greater than or equal to 8,\nthen <code>bColorCount<\/code> is zero.<\/p>\n<p>\nIn practice, a lot of people get lazy about filling in the\n<code>bColorCount<\/code> and set it to zero,\neven for 4-color or 16-color icons.\nStarting in Windows&nbsp;XP,\nWindows autodetects this common error,\nbut its autocorrection is slightly buggy in the case of planar bitmaps.\nFortunately, almost nobody uses planar bitmaps any more,\nbut still, it would be in your best interest not to rely on the\nautocorrection performed by Windows and just set your <code>bColorCount<\/code>\ncorrectly in the first place.\nAn incorrect <code>bColorCount<\/code> means that when Windows tries to\nfind the best image for your icon, it may choose a suboptimal one\nbecause it based its decision on incorrect color depth information.\n<\/p>\n<p>\nAlthough it probably isn&#8217;t true,\nI will pretend that monochrome icons existed before color icons,\nbecause it makes the storytelling easier.\n<\/p>\n<p>\nA monochome icon is described by two bitmaps, called <i>AND<\/i>\n(or <i>mask<\/i>)\nand <i>XOR<\/i> (or <i>image<\/i>, or when we get to color icons, <i>color<\/i>).\nDrawing an icon takes place in two steps:\nFirst, the mask is ANDed with the screen, then the image is XORed.\nIn other words,\n<\/p>\n<p ALIGN=\"center\">\n<code>pixel = (screen AND mask) XOR image<\/code>\n<\/p>\n<p>\nBy choosing appropriate values for <i>mask<\/i> and <i>image<\/i>,\nyou can cover all the possible monochrome BLT operations.\n<\/p>\n<table BORDER=\"1\" STYLE=\"border-collapse: collapse\" CELLPADDING=\"3\">\n<col ALIGN=\"center\">\n<col ALIGN=\"center\">\n<col ALIGN=\"left\">\n<table>\n<tbody>\n<tr>\n<th>mask<\/th>\n<th>image<\/th>\n<th ALIGN=\"center\">result<\/th>\n<th ALIGN=\"center\">operation<\/th>\n<\/tr>\n<tr>\n<td>0<\/td>\n<td>0<\/td>\n<td>(screen AND 0) XOR 0 = 0<\/td>\n<td>blackness<\/td>\n<\/tr>\n<tr>\n<td>0<\/td>\n<td>1<\/td>\n<td>(screen AND 0) XOR 1 = 1<\/td>\n<td>whiteness<\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>0<\/td>\n<td>(screen AND 1) XOR 0 = screen<\/td>\n<td>nop<\/td>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>1<\/td>\n<td>(screen AND 1) XOR 1 = NOT screen<\/td>\n<td>invert<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\nConceptually, the <i>mask<\/i> specifies which pixels from the\n<i>image<\/i> should be copied to the destination:\nA black pixel in the mask means that the corresponding pixel\nin the image is copied.\n<\/p>\n<p>\nThe mask and image bitmaps are physically stored as one single\ndouble-height DIB.\nThe image bitmap comes first, followed by the mask.\n(But since DIBs are stored\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/10\/04\/10070943.aspx\">\nbottom-up<\/a>, if you actually look at\nthe bitmap, the mask is in the top half of the bitmap and the\nimage is in the bottom half).\n<\/p>\n<p>\nIn terms of file format, each icon image is stored in the form\nof a <code>BITMAPINFO<\/code> (which itself takes the form of\na <code>BITMAPINFOHEADER<\/code> followed by a color table),\nfollowed by the image pixels, followed by the mask pixels.\nThe <code>biCompression<\/code> must be <code>BI_RGB<\/code>.\nSince this is a double-height bitmap, the <code>biWidth<\/code>\nis the width of the image, but the <code>biHeight<\/code>\nis <i>double<\/i> the image height.\nFor example, a 16&times;16 icon would specify a width of&nbsp;16\nbut a height of 16&nbsp;&times;&nbsp;2&nbsp;=&nbsp;32.\n<\/p>\n<p>\nThat&#8217;s pretty much it for classic monochrome icons.\nNext time we&#8217;ll look at color icons.\n<\/p>\n<p>\nStill, given what you know now, the following story will make sense.\n<\/p>\n<p>\nA customer contacted the shell team to report that despite all\ntheir best efforts, they could not get Windows to use the image\nthey wanted from their .ICO file.\nWindows for some reason always chose a low-color icon\ninstead of using the high-color icon.\nFor example,\neven though the .ICO file had a 32bpp image available,\nWindows always chose to use the 16-color (4bpp) image,\neven when running on a 32bpp display.\n<\/p>\n<p>\nA closer inspection of the offending .ICO file revealed\nthat the <code>bColorCount<\/code> in the\n<code>IconDirectoryEntry<\/code>\nfor all the images was set to&nbsp;1,\nregardless of the actual color depth of the image.\nThe table of contents for the .ICO file said\n&#8220;Yeah, all I&#8217;ve got are monochrome images.\nI&#8217;ve got three 48&times;48 monochrome images,\nthree 32&times;32 monochrome images,\nand three 16&times;16 monochrome images.&#8221;\nGiven this information, Windows figured,\n&#8220;Well,\n<a HREF=\"http:\/\/www.flickr.com\/photos\/57669468@N00\/329295021\/\">\ngiven those choices<\/a>,\nI guess that means I&#8217;ll use the monochrome one.&#8221;\nIt chose one of images (at pseudo-random),\nand went to the bitmap data and found,\n&#8220;Oh, hey, how about that, it&#8217;s actually a 16-color image.\nOkay, well, I guess I can load that.&#8221;\n<\/p>\n<p>\nIn summary, the .ICO file was improperly authored.\nPatching each <code>IconDirectoryEntry<\/code> in a hex editor\nmade the icon work as intended.\nThe customer thanked us for our investigation and said\nthat they would take the issue up with their graphic design team.\n<\/p>\n<\/col>\n<\/col>\n<\/col>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>This week is devoted to the evolution of the ICO file format. Note that the icon resource format is different from the ICO file format; I&#8217;ll save that topic for another day. The ICO file begins with a fixed header: typedef struct ICONDIR { WORD idReserved; WORD idType; WORD idCount; ICONDIRENTRY idEntries[]; } ICONHEADER; idReserved [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-12513","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>This week is devoted to the evolution of the ICO file format. Note that the icon resource format is different from the ICO file format; I&#8217;ll save that topic for another day. The ICO file begins with a fixed header: typedef struct ICONDIR { WORD idReserved; WORD idType; WORD idCount; ICONDIRENTRY idEntries[]; } ICONHEADER; idReserved [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/12513","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=12513"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/12513\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=12513"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=12513"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=12513"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}