{"id":10613,"date":"2011-05-20T07:00:00","date_gmt":"2011-05-20T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/05\/20\/beginbufferedpaint-its-not-just-for-buffered-painting-any-more\/"},"modified":"2011-05-20T07:00:00","modified_gmt":"2011-05-20T07:00:00","slug":"beginbufferedpaint-its-not-just-for-buffered-painting-any-more","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110520-00\/?p=10613","title":{"rendered":"BeginBufferedPaint: It&#039;s not just for buffered painting any more"},"content":{"rendered":"<p>\nI covered the <code>BeginBufferedPaint<\/code> function in\n<a HREF=\"https:\/\/channel9.msdn.com\/pdc2008\/PC43\/\">\nmy 2008 PDC presentation<\/a>,\nbut one thing I didn&#8217;t mention is that the buffered paint functions\nare very handy even if you have no intention of painting.\n<\/p>\n<p>\nSince the buffered paint functions maintain a cache\n(provided that you remembed to call\n<code>Buffered&shy;Paint&shy;Init<\/code>),\nyou can use\n<code>Begin&shy;Buffered&shy;Paint<\/code> to get a temporary bitmap\neven if you have no intention of actually painting to the screen.\nYou might want a bitmap to do some off-screen composition,\nor for some other temporary purpose,\nin which case you can ask\n<code>Begin&shy;Buffered&shy;Paint<\/code> to give\nyou a bitmap,\nuse the bitmap for whatever you like,\nand then pass <code>fUpdateTarget = FALSE<\/code> when you call\n<code>End&shy;Buffered&shy;Paint<\/code>\nto say &#8220;Ha ha, just kidding.&#8221;\n<\/p>\n<p>\nOne thing to have to be aware of is that the bitmap provided\nby <code>Begin&shy;Buffered&shy;Paint<\/code>\nis not guaranteed to be exactly\nthe size you requested; it only promises that the bitmap will be\n<i>at least<\/i> the size you requested.\nMost of the time,\nyour code won&#8217;t care (there are just pixels out there that you aren&#8217;t\nusing),\nbut if you use the\n<code>Get&shy;Buffered&shy;Paint&shy;Bits<\/code>\nfunction to obtain direct access to the bits,\ndon&#8217;t forget to take the stride into account.\n<\/p>\n<p>Consider this artifical example of a program that uses\n<code>Create&shy;DIB&shy;Section<\/code> to create a temporary 32bpp bitmap\nfor the purpose of updating a layered window.\nStart with the\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nscratch program<\/a>\nand make these changes:\n<\/p>\n<pre>\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n <font COLOR=\"blue\">BOOL fRc = FALSE;\n HDC hdcWin = GetDC(hwnd);\n if (hdcWin) {\n  HDC hdcMem = CreateCompatibleDC(hdcWin);\n  if (hdcMem) {\n   const int cx = 200;\n   const int cy = 200;\n   RECT rc = { 0, 0, cx, cy };\n   BITMAPINFO bmi = { 0 };\n   bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);\n   bmi.bmiHeader.biWidth = cx;\n   bmi.bmiHeader.biHeight = cy;\n   bmi.bmiHeader.biPlanes = 1;\n   bmi.bmiHeader.biBitCount = 32;\n   bmi.bmiHeader.biCompression = BI_RGB;\n   RGBQUAD *prgbBits;\n   HBITMAP hbm = CreateDIBSection(hdcWin, &amp;bmi,\n             DIB_RGB_COLORS, &amp;reinterpret_cast&lt;void*&amp;&gt;(prgbBits),\n                                                        NULL, 0);\n   if (hbm) {\n    HBITMAP hbmPrev = SelectBitmap(hdcMem, hbm);\n    \/\/ Draw a simple picture\n    FillRect(hdcMem, &amp;rc,\n                     reinterpret_cast&lt;HBRUSH&gt;(COLOR_INFOBK + 1));\n    rc.left = cx \/ 4;\n    rc.right -= rc.left;\n    rc.top = cy \/ 4;\n    rc.bottom -= rc.top;\n    FillRect(hdcMem, &amp;rc,\n                   reinterpret_cast&lt;HBRUSH&gt;(COLOR_INFOTEXT + 1));\n    \/\/ Apply the alpha channel (and premultiply)\n    for (int y = 0; y &lt; cy; y++) {\n     for (int x = 0; x &lt; cx; x++) {\n      RGBQUAD *prgb = &amp;prgbBits[y * cx + x];\n      BYTE bAlpha = static_cast&lt;BYTE&gt;(cx * x \/ cx);\n      prgb-&gt;rgbRed = static_cast&lt;BYTE&gt;(prgb-&gt;rgbRed * bAlpha \/ 255);\n      prgb-&gt;rgbBlue = static_cast&lt;BYTE&gt;(prgb-&gt;rgbBlue * bAlpha \/ 255);\n      prgb-&gt;rgbGreen = static_cast&lt;BYTE&gt;(prgb-&gt;rgbGreen * bAlpha \/ 255);\n      prgb-&gt;rgbReserved = bAlpha;\n     }\n    }\n    \/\/ update the layered window\n    POINT ptZero = { 0, 0 };\n    SIZE siz = { cx, cy };\n    BLENDFUNCTION bf =  { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\n    fRc = UpdateLayeredWindow(hwnd, NULL, &amp;ptZero, &amp;siz, hdcMem,\n                              &amp;ptZero, 0, &amp;bf, ULW_ALPHA);\n    SelectBitmap(hdcMem, hbmPrev);\n    DeleteObject(hbm);\n   }\n   DeleteDC(hdcMem);\n  }\n  ReleaseDC(hwnd, hdcWin);\n }\n return fRc;<\/font>\n}\n<\/pre>\n<p>\nPretty standard stuff.\nBut let&#8217;s convert this to use the buffered paint functions\nto take advantage of the buffered paint bitmap cache.\n<\/p>\n<pre>\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n BOOL fRc = FALSE;\n HDC hdcWin = GetDC(hwnd);\n if (hdcWin) {\n  <font COLOR=\"blue\">HDC hdcMem;<\/font>\n  <font COLOR=\"red\"><strike>\/\/ HDC hdcMem = CreateCompatibleDC(hdcWin);<\/strike><\/font>\n  <font COLOR=\"red\"><strike>\/\/ if (hdcMem) {<\/strike><\/font>\n   const int cx = 200;\n   const int cy = 200;\n   RECT rc = { 0, 0, cx, cy };\n   <font COLOR=\"red\"><strike>\/\/ BITMAPINFO bmi = { 0 };<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ bmi.bmiHeader.biWidth = cx;<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ bmi.bmiHeader.biHeight = cy;<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ bmi.bmiHeader.biPlanes = 1;<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ bmi.bmiHeader.biBitCount = 32;<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ bmi.bmiHeader.biCompression = BI_RGB;<\/strike><\/font>\n   RGBQUAD *prgbBits;\n   <font COLOR=\"blue\">BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP };\n   HPAINTBUFFER hbp = BeginBufferedPaint(hdcWin, &amp;rc,\n                              BPBF_TOPDOWNDIB, &amp;params, &amp;hdcMem);\n   if (hbp) {\n    int cxRow;\n    if (SUCCEEDED(GetBufferedPaintBits(hpb, &amp;prgbBits, &amp;cxRow))) {<\/font>\n   <font COLOR=\"red\"><strike>\/\/ HBITMAP hbm = CreateDIBSection(hdcWin, &amp;bmi,<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/        DIB_RGB_COLORS, &amp;reinterpret_cast&lt;void*&amp;&gt;(prgbBits),<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/                                                   NULL, 0);<\/strike><\/font>\n   <font COLOR=\"red\"><strike>\/\/ if (hbm) {<\/strike><\/font>\n    <font COLOR=\"red\"><strike>\/\/ HBITMAP hbmPrev = SelectBitmap(hdcMem, hbm);<\/strike><\/font>\n    \/\/ Draw a simple picture\n    FillRect(hdcMem, &amp;rc,\n                     reinterpret_cast&lt;HBRUSH&gt;(COLOR_INFOBK + 1));\n    rc.left = cx \/ 4;\n    rc.right -= rc.left;\n    rc.top = cy \/ 4;\n    rc.bottom -= rc.top;\n    FillRect(hdcMem, &amp;rc,\n                   reinterpret_cast&lt;HBRUSH&gt;(COLOR_INFOTEXT + 1));\n    \/\/ Apply the alpha channel (and premultiply)\n    for (int y = 0; y &lt; cy; y++) {\n     for (int x = 0; x &lt; cx; x++) {\n      RGBQUAD *prgb = &amp;prgbBits[y * <font COLOR=\"blue\">cxRow<\/font> + x];\n      BYTE bAlpha = static_cast&lt;BYTE&gt;(cx * x \/ cx);\n      prgb-&gt;rgbRed = static_cast&lt;BYTE&gt;(prgb-&gt;rgbRed * bAlpha \/ 255);\n      prgb-&gt;rgbBlue = static_cast&lt;BYTE&gt;(prgb-&gt;rgbBlue * bAlpha \/ 255);\n      prgb-&gt;rgbGreen = static_cast&lt;BYTE&gt;(prgb-&gt;rgbGreen * bAlpha \/ 255);\n      prgb-&gt;rgbReserved = bAlpha;\n     }\n    }\n    \/\/ update the layered window\n    POINT ptZero = { 0, 0 };\n    SIZE siz = { cx, cy };\n    BLENDFUNCTION bf =  { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };\n    fRc = UpdateLayeredWindow(hwnd, NULL, &amp;ptZero, &amp;siz, hdcMem,\n                              &amp;ptZero, 0, &amp;bf, ULW_ALPHA);\n    <font COLOR=\"red\"><strike>\/\/ SelectBitmap(hdcMem, hbmPrev);<\/strike><\/font>\n    <font COLOR=\"red\"><strike>\/\/ DeleteObject(hbm);<\/strike><\/font>\n   }\n   <font COLOR=\"blue\">EndBufferedPaint(hpb, FALSE);<\/font>\n   <font COLOR=\"red\"><strike>\/\/ DeleteDC(hdcMem);<\/strike><\/font>\n  }\n  ReleaseDC(hwnd, hdcWin);\n }\n return fRc;<\/font>\n}\n\/\/ changes to WinMain\n <font COLOR=\"blue\">if (SUCCEEDED(BufferedPaintInit())) {<\/font>\n <font COLOR=\"red\"><strike>\/\/ if (SUCCEEDED(CoInitialize(NULL))) {\/* In case we use COM *\/<\/strike><\/font>\n  <font COLOR=\"blue\">hwnd = CreateWindowEx(WS_EX_LAYERED,<\/font>\n  <font COLOR=\"red\"><strike>\/\/ hwnd = CreateWindow(<\/strike><\/font>\n  ...\n  <font COLOR=\"blue\">BufferedPaintUnInit();<\/font>\n  <font COLOR=\"red\"><strike>\/\/ CoUninitialize();<\/strike><\/font>\n  ...\n<\/pre>\n<p>\nWe&#8217;re using the buffered paint API not for buffered painting\nbut just as a convenient way to get a bitmap and a DC at one shot.\nIt saves some typing (you don&#8217;t have to create the bitmap and the DC\nand select the bitmap in and out),\nand when you return the paint buffer to the cache, some other\nwindow that calls <code>Begin&shy;Buffered&shy;Paint<\/code> may be able\nto re-use that bitmap.\n<\/p>\n<p>\nThere are a few tricky parts here.\nFirst, if you&#8217;re going to be accessing\nthe bits directly, you need to call\n<code>Get&shy;Buffered&shy;Paint&shy;Bits<\/code>\nand use the <code>cxRow<\/code> to determine the bitmap stride.\nNext, when we&#8217;re done, we pass <code>FALSE<\/code> to\n<code>End&shy;Buffered&shy;Paint<\/code> to say,\n&#8220;Yeah, um, thanks for the bitmap, but don&#8217;t <code>Bit&shy;Blt<\/code> the\nresults back into the DC we passed to\n<code>Begin&shy;Buffered&shy;Paint<\/code>.\nSorry for the confusion.&#8221;\n<\/p>\n<p>\nA less obvious trick is that we used <code>BPPF_NOCLIP<\/code>\nto get a full bitmap.\nBy default, <code>Begin&shy;Buffered&shy;Paint<\/code>\nreturns you a bitmap which\nis clipped to the DC you pass as the first parameter.\nThis is an optimization to avoid allocating memory for pixels that\ncan&#8217;t be seen anyway when <code>End&shy;Buffered&shy;Paint<\/code>\ngoes to copy\nthe bits back to the original DC.\nWe don&#8217;t want this optimization, however, since we have no intention\nof blitting the results back to the original DC.\nThe clip region of the original DC is irrelevant to us because\nwe just want a temporary bitmap for some internal calculations.\n<\/p>\n<p>\nAnyway, there you have it, an example of using\n<code>Begin&shy;Buffered&shy;Paint<\/code> to obtain a temporary bitmap.\nIt doesn&#8217;t win much in this example (since we call it only once,\nat window creation time),\nbut if you have code which creates a lot of DIB sections for temporary\nuse, you can use this trick to take advantage of the buffered paint\ncache and reduce the overhead of bitmap creation and deletion.\n<\/p>\n<p>\n<b>Pre-emptive snarky comment<\/b>:\n&#8220;<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/04\/08\/10151258.aspx#10151864\">How dare you show us an alternative method that isn&#8217;t\navailable on Windows 2000<\/a>!&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I covered the BeginBufferedPaint function in my 2008 PDC presentation, but one thing I didn&#8217;t mention is that the buffered paint functions are very handy even if you have no intention of painting. Since the buffered paint functions maintain a cache (provided that you remembed to call Buffered&shy;Paint&shy;Init), you can use Begin&shy;Buffered&shy;Paint to get a [&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-10613","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>I covered the BeginBufferedPaint function in my 2008 PDC presentation, but one thing I didn&#8217;t mention is that the buffered paint functions are very handy even if you have no intention of painting. Since the buffered paint functions maintain a cache (provided that you remembed to call Buffered&shy;Paint&shy;Init), you can use Begin&shy;Buffered&shy;Paint to get a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10613","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=10613"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/10613\/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=10613"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=10613"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=10613"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}