{"id":18603,"date":"2009-04-08T10:00:00","date_gmt":"2009-04-08T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2009\/04\/08\/let-gdi-do-your-rle-compression-for-you\/"},"modified":"2009-04-08T10:00:00","modified_gmt":"2009-04-08T10:00:00","slug":"let-gdi-do-your-rle-compression-for-you","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20090408-00\/?p=18603","title":{"rendered":"Let GDI do your RLE compression for you"},"content":{"rendered":"<p><P>\nThis is another trick along the lines of\n<A HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2006\/11\/16\/1086835.aspx\">\nusing DIB sections to perform bulk color mapping<\/A>.\nGDI will do it for you; you just have to know how to ask.\nToday&#8217;s mission is to take a 4bpp bitmap and compress it in\n<CODE>BI_RLE4<\/CODE> format.\nNow, sure, there are programs out there which already do this conversion,\nbut the lesson is in the journey, not in the destination.\n<\/P>\n<P>\nThe secret is the <CODE>GetDIBits<\/CODE> function.\nYou give this function a bitmap and a bitmap format,\nand out come the bits in the format you requested;\nGDI will convert as necessary.\n<\/P>\n<P>\n<B>Note<\/B>: I&#8217;m going to take a risk and write &#8220;sloppy&#8221; code.\nThis is code that is not production quality but is enough to get\nthe point across,\nso put your nitpicking notepads away.\n<\/P>\n<PRE>\nvoid ConvertToRLE4(LPCTSTR pszSrc, LPCTSTR pszDst)\n{\n    \/\/ error checking elided for expository purposes\n    HBITMAP hbm = (HBITMAP)LoadImage(NULL, pszSrc, IMAGE_BITMAP,\n                                     0, 0,\n                                     LR_LOADFROMFILE |\n                                     LR_CREATEDIBSECTION);<\/p>\n<p>    DIBSECTION ds;<\/p>\n<p>    \/\/ error checking elided for expository purposes\n    GetObject(hbm, sizeof(ds), &amp;ds);<\/p>\n<p>    if (ds.dsBmih.biBitCount != 4) {\n        \/\/ error &#8211; source bitmap is not 4bpp\n    }<\/p>\n<p>    struct BITMAPINFO16COLOR {\n        BITMAPINFOHEADER bmih;\n        RGBQUAD rgrgb[16];\n    } bmi16;\n    bmi16.bmih = ds.dsBmih;<\/p>\n<p>    bmi16.bmih.biCompression = BI_RLE4;<\/p>\n<p>    BYTE *rgbPixels = new BYTE[bmi16.bmih.biSizeImage];\n    HDC hdc = GetDC(NULL);\n    if (GetDIBits(hdc, hbm, 0, bmi16.bmih.biHeight, rgbPixels,\n                  (LPBITMAPINFO)&amp;bmi16, DIB_RGB_COLORS)\n        != bmi16.bmih.biHeight) {\n        \/\/ error &#8211; bitmap not compressible\n    }\n    ReleaseDC(NULL, hdc);<\/p>\n<p>    BITMAPFILEHEADER bfh = { 0 };\n    bfh.bfType = MAKEWORD(&#8216;B&#8217;, &#8216;M&#8217;);\n    bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(bmi16);\n    bfh.bfSize = bfh.bfOffBits + bmi16.bmih.biSizeImage;<\/p>\n<p>    \/\/ error checking elided for expository purposes\n    HANDLE h = CreateFile(pszDst, GENERIC_WRITE, 0, NULL,\n                          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\n    DWORD dwWritten;\n    WriteFile(h, &amp;bfh, sizeof(bfh), &amp;dwWritten, NULL);\n    WriteFile(h, &amp;bmi16, sizeof(bmi16), &amp;dwWritten, NULL);\n    WriteFile(h, rgbPixels, bmi16.bmih.biSizeImage, &amp;dwWritten, NULL);\n    CloseHandle(h);\n    delete[] rgbPixels;\n}\n<\/PRE>\n<P>\nLet&#8217;s start from the top.\nAfter loading the bitmap and verifying that it is a 4bpp bitmap,\nwe declare a <CODE>BITMAPINFO16COLOR<\/CODE> structure that is\njust a <CODE>BITMAPINFO<\/CODE> structure that holds 16&nbsp;colors\ninstead of just one.\nWe copy the <CODE>BITMAPINFOHEADER<\/CODE> from the\n<CODE>DIBSECTION<\/CODE> to our structure for two reasons:\n<\/P>\n<OL>\n<LI>We want to make some changes, and\n<LI>GDI expects the color table to come immediately after the\n    <CODE>BITMAPINFOHEADER<\/CODE>.\n<\/OL>\n<P>\nThe second reason is the more important one.\nWe can&#8217;t use the <CODE>BITMAPINFOHEADER<\/CODE> that is part of the\n<CODE>DIBSECTION<\/CODE> structure because\nthe <CODE>DIBSECTION<\/CODE> structure puts <CODE>dsBitfields<\/CODE>\nafter the <CODE>BITMAPINFOHEADER<\/CODE> instead of a color table.\n<\/P>\n<P>\nAfter copying the <CODE>BITMAPINFOHEADER<\/CODE>, we make the key change:\nChanging the compression type to <CODE>BI_RLE4<\/CODE>.\nWe allocate a pixel buffer of a size equal to the uncompressed size\nof the original bitmap and use <CODE>GetDIBits<\/CODE> to fill it\nwith compressed data.\nKey points:\n<\/P>\n<UL>\n<LI>Before calling the <CODE>GetDIBits<\/CODE> function,\n    we must set the <CODE>biSizeImage<\/CODE> member of the\n    <CODE>BITMAPINFO<\/CODE> structure to the size of\n    the buffer we passed as <CODE>rgbPixels<\/CODE>.\n    In our case, this happened implicitly since we allocated\n    <CODE>rgbPixels<\/CODE> based on the value of\n    <CODE>bmi16.bmih.biSizeImage<\/CODE>.\n<LI>On successful exit from the <CODE>GetDIBits<\/CODE> function,\n    the <CODE>GetDIBits<\/CODE> function sets the\n    <CODE>biSizeImage<\/CODE> member of the\n    <CODE>BITMAPINFO<\/CODE> structure to the number of bytes\n    actually written to the buffer.\n<LI>On successful exit from the <CODE>GetDIBits<\/CODE> function,\n    the <CODE>GetDIBits<\/CODE> function fills the color table\n    if you&#8217;re using a bitmap format that requires a color table.\n    It&#8217;s important that you allocate enough memory to hold\n    the color table; if you forget, then you have a buffer overflow\n    bug.\n<\/UL>\n<P>\nSince the <CODE>GetDIBits<\/CODE> function returns the number of\nscan lines successfully read,\nif the value is different from the value we requested, then\nsomething went wrong.\nIn our case, the most likely reason is that the bitmap\nis not compressible according to the\n<CODE>BI_RLE4<\/CODE> algorithm.\n<\/P>\n<P>\nNow that we have the compressed bits, it&#8217;s just grunt work\nto turn it into a <CODE>BMP<\/CODE> file.\nThe <CODE>BMP<\/CODE> file format specifies that the file\nbegins with a <CODE>BITMAPFILEHEADER<\/CODE>,\nfollowed by the <CODE>BITMAPINFOHEADER<\/CODE>,\nthe color table, and the pixels.\nSo we write them out in that order.\n<\/P>\n<P>\nEasy peasy.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is another trick along the lines of using DIB sections to perform bulk color mapping. GDI will do it for you; you just have to know how to ask. Today&#8217;s mission is to take a 4bpp bitmap and compress it in BI_RLE4 format. Now, sure, there are programs out there which already do this [&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":[25],"class_list":["post-18603","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>This is another trick along the lines of using DIB sections to perform bulk color mapping. GDI will do it for you; you just have to know how to ask. Today&#8217;s mission is to take a 4bpp bitmap and compress it in BI_RLE4 format. Now, sure, there are programs out there which already do this [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/18603","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=18603"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/18603\/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=18603"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=18603"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=18603"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}