{"id":34733,"date":"2005-08-03T10:00:16","date_gmt":"2005-08-03T10:00:16","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/08\/03\/drawing-a-monochrome-bitmap-with-transparency\/"},"modified":"2005-08-03T10:00:16","modified_gmt":"2005-08-03T10:00:16","slug":"drawing-a-monochrome-bitmap-with-transparency","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050803-16\/?p=34733","title":{"rendered":"Drawing a monochrome bitmap with transparency"},"content":{"rendered":"<p><A HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/08\/02\/446605.aspx\">\nLast time, I left you with a brief puzzle<\/A>.\nHere are two approaches.\nI am not a GDI expert, so there may be even better solutions\nout there.\nTo emphasize the transparency, I&#8217;ll change the window background\ncolor to the application workspace color.\n<\/P>\n<PRE>\n BOOL WinRegisterClass(WNDCLASS *pwc)\n {\n  pwc-&gt;hbrBackground = (HBRUSH)(COLOR_<FONT COLOR=\"blue\">APPWORKSPACE<\/FONT> + 1);\n  return __super::WinRegisterClass(pwc);\n }\n<\/PRE>\n<P>\n<STRONG>Method&nbsp;1<\/STRONG>:\nA big\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/gdi\/bitmaps_229g.asp\">\n<CODE>MaskBlt<\/CODE><\/A>.\n<\/P>\n<PRE>\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n HDC hdcMem = CreateCompatibleDC(pps-&gt;hdc);\n if (hdcMem) {\n  int cxCheck = GetSystemMetrics(SM_CXMENUCHECK);\n  int cyCheck = GetSystemMetrics(SM_CYMENUCHECK);\n  HBITMAP hbmMono = CreateBitmap(cxCheck, cyCheck, 1, 1, NULL);\n  if (hbmMono) {\n   HBITMAP hbmPrev = SelectBitmap(hdcMem, hbmMono);\n   if (hbmPrev) {\n    RECT rc = { 0, 0, cxCheck, cyCheck };\n    DrawFrameControl(hdcMem, &amp;rc, DFC_MENU, DFCS_MENUCHECK);\n    COLORREF clrTextPrev = SetTextColor(pps-&gt;hdc,\n                                     GetSysColor(COLOR_MENUTEXT));\n    <FONT COLOR=\"blue\"><STRIKE>\/\/ COLORREF clrBkPrev = SetBkColor(pps-&gt;hdc,<\/STRIKE>\n    <STRIKE>\/\/                                  GetSysColor(COLOR_MENU));<\/STRIKE>\n    MaskBlt(pps-&gt;hdc, 0, 0, cxCheck, cyCheck,\n           hdcMem, 0, 0, hbmMono, 0, 0\n           MAKEROP4(0x00AA0029, SRCCOPY));\n    <STRIKE>\/\/ SetBkColor(pps-&gt;hdc, clrBkPrev);<\/STRIKE><\/FONT>\n    SetTextColor(pps-&gt;hdc, clrTextPrev);\n    SelectBitmap(hdcMem, hbmPrev);\n   }\n   DeleteObject(hbmMono);\n  }\n  DeleteDC(hdcMem);\n }\n}\n<\/PRE>\n<P>\nThis has the least amount of typing but feels like\noverkill to me, using a quaternary raster operation\nas if were a ternary, just because I didn&#8217;t want to create a\npattern brush.\n(The raster operation 0x00AA0029 is the NOP operator; it leaves\nthe destination alone.  I didn&#8217;t have this memorized;\nI looked it up\n<A HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/gdi\/pantdraw_6n77.asp\">\nin the documentation<\/a>.)\nThe <CODE>MAKEROP4<\/CODE> says that for each white pixel in the mask,\ndo nothing (NOP), and for each black pixel, do a <CODE>SRCCOPY<\/CODE>.\n<\/P>\n<P>\nNotice that the background color is never used\n(since it&#8217;s supposed to be transparent); consequently, we can delete\nthe code that sets and restores the DC&#8217;s background color.\n<\/P>\n<P>\n<STRONG>Method&nbsp;2<\/STRONG>:\nThe traditional two-step.\n<\/P>\n<PRE>\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n HDC hdcMem = CreateCompatibleDC(pps-&gt;hdc);\n if (hdcMem) {\n  int cxCheck = GetSystemMetrics(SM_CXMENUCHECK);\n  int cyCheck = GetSystemMetrics(SM_CYMENUCHECK);\n  HBITMAP hbmMono = CreateBitmap(cxCheck, cyCheck, 1, 1, NULL);\n  if (hbmMono) {\n   HBITMAP hbmPrev = SelectBitmap(hdcMem, hbmMono);\n   if (hbmPrev) {\n    RECT rc = { 0, 0, cxCheck, cyCheck };\n    DrawFrameControl(hdcMem, &amp;rc, DFC_MENU, DFCS_MENUCHECK);\n    COLORREF clrTextPrev = SetTextColor(pps-&gt;hdc, <FONT COLOR=\"blue\">RGB(0,0,0)<\/FONT>);\n    COLORREF clrBkPrev = SetBkColor(pps-&gt;hdc, <FONT COLOR=\"blue\">RGB(255,255,255)<\/FONT>);\n    <FONT COLOR=\"blue\">BitBlt(pps-&gt;hdc, cxCheck, 0, cxCheck, cyCheck,\n           hdcMem, 0, 0, SRCAND);\n    SetTextColor(pps-&gt;hdc, GetSysColor(COLOR_MENUTEXT));\n    SetBkColor(pps-&gt;hdc, RGB(0,0,0));\n    BitBlt(pps-&gt;hdc, cxCheck, 0, cxCheck, cyCheck,\n           hdcMem, 0, 0, SRCPAINT);<\/FONT>\n    SetBkColor(pps-&gt;hdc, clrBkPrev);\n    SetTextColor(pps-&gt;hdc, clrTextPrev);\n    SelectBitmap(hdcMem, hbmPrev);\n   }\n   DeleteObject(hbmMono);\n  }\n  DeleteDC(hdcMem);\n }\n}\n<\/PRE>\n<P>\nThis is the traditional two-step blit.\nThe first erases the pixels that are about to be overwritten\nby setting the foreground to black and background to white,\nthen using <CODE>SRCAND<\/CODE>.\nThis has the effect of erasing all the foreground pixels to zero\nwhile leaving the background intact.\nThe second blit does the same, but with <CODE>SRCPAINT<\/CODE>.\nThis means that the background pixels need to be treated as black,\nso that when they are &#8220;or&#8221;d with the destination, the destination\npixels are unchanged.\nThe foreground pixels get the desired foreground color.\n<\/P>\n<P>\nThis method can be shortened by negating the first blit,\nreversing the sense of foreground and background, so that the\ncolor black doesn&#8217;t have to move between the background color\nand the text color.\n<\/P>\n<PRE>\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n HDC hdcMem = CreateCompatibleDC(pps-&gt;hdc);\n if (hdcMem) {\n  int cxCheck = GetSystemMetrics(SM_CXMENUCHECK);\n  int cyCheck = GetSystemMetrics(SM_CYMENUCHECK);\n  HBITMAP hbmMono = CreateBitmap(cxCheck, cyCheck, 1, 1, NULL);\n  if (hbmMono) {\n   HBITMAP hbmPrev = SelectBitmap(hdcMem, hbmMono);\n   if (hbmPrev) {\n    RECT rc = { 0, 0, cxCheck, cyCheck };\n    DrawFrameControl(hdcMem, &amp;rc, DFC_MENU, DFCS_MENUCHECK);\n    COLORREF clrTextPrev = SetTextColor(pps-&gt;hdc, <FONT COLOR=\"blue\">RGB(255,255,255)<\/FONT>);\n    COLORREF clrBkPrev = SetBkColor(pps-&gt;hdc, <FONT COLOR=\"blue\">RGB(0,0,0)<\/FONT>);\n    <FONT COLOR=\"blue\">BitBlt(pps-&gt;hdc, cxCheck, 0, cxCheck, cyCheck,\n           hdcMem, 0, 0, 0x00220326); \/\/ DSna\n    SetTextColor(pps-&gt;hdc, GetSysColor(COLOR_MENUTEXT));\n    BitBlt(pps-&gt;hdc, cxCheck, 0, cxCheck, cyCheck,\n           hdcMem, 0, 0, SRCPAINT);<\/FONT>\n    SetBkColor(pps-&gt;hdc, clrBkPrev);\n    SetTextColor(pps-&gt;hdc, clrTextPrev);\n    SelectBitmap(hdcMem, hbmPrev);\n   }\n   DeleteObject(hbmMono);\n  }\n  DeleteDC(hdcMem);\n }\n}\n<\/PRE>\n<P>\nWhether this shortening is actually an overall\nimprovement is difficult to tell.\nIt&#8217;s possible that some display drivers have a highly optimized\n<CODE>SRCAND<\/CODE> handler whereas they are less likely to have\nan optimized <CODE>0x00220326<\/CODE> handler.\n<\/P>\n<P>\n(Exercise: Why can&#8217;t you instead reverse the second blit,\nconverting it to a <CODE>MERGEPAINT<\/CODE>?)\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, I left you with a brief puzzle. Here are two approaches. I am not a GDI expert, so there may be even better solutions out there. To emphasize the transparency, I&#8217;ll change the window background color to the application workspace color. BOOL WinRegisterClass(WNDCLASS *pwc) { pwc-&gt;hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); return __super::WinRegisterClass(pwc); } [&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-34733","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, I left you with a brief puzzle. Here are two approaches. I am not a GDI expert, so there may be even better solutions out there. To emphasize the transparency, I&#8217;ll change the window background color to the application workspace color. BOOL WinRegisterClass(WNDCLASS *pwc) { pwc-&gt;hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); return __super::WinRegisterClass(pwc); } [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/34733","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=34733"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/34733\/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=34733"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=34733"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=34733"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}