{"id":30873,"date":"2006-06-14T07:00:00","date_gmt":"2006-06-14T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/06\/14\/pitfalls-of-transparent-rendering-of-anti-aliased-fonts\/"},"modified":"2006-06-14T07:00:00","modified_gmt":"2006-06-14T07:00:00","slug":"pitfalls-of-transparent-rendering-of-anti-aliased-fonts","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060614-00\/?p=30873","title":{"rendered":"Pitfalls of transparent rendering of anti-aliased fonts"},"content":{"rendered":"<p>\nWindows provides a variety of technologies for rendering\nmonochrome text on color displays, taking advantage of\ndisplay characteristics to provide smoother results.\nThese include grayscale anti-aliasing as well as the more\nadvanced\n<a HREF=\"http:\/\/www.microsoft.com\/typography\/ClearTypeInfo.mspx\">\nClearType<\/a> technique.\nBoth of these methods\nread from the background pixels to decide what pixels to draw\nin the foreground.\nThis means that rendering text requires extra attention.\n<\/p>\n<p>\nIf you draw text with an opaque background, there is no problem\nbecause you are explicitly drawing the background pixels as part\nof the text-drawing call, so the results are consistent regardless\nof what the previous background pixels were.\nBut if you draw text with a transparent background, then you must\nmake sure the background pixels that you draw against are the ones\nyou really want.\n<\/p>\n<p>\nThe most common way people mess this up is by drawing text multiple times.\nI&#8217;ve seen programs which draw text darker and darker the longer you use it.\nWe&#8217;ll see here how this can happen and what you need to do to avoid it.\nStart with\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2003\/07\/23\/54576.aspx\">\nthe scratch program<\/a> and make these changes:\n<\/p>\n<pre>\n<font COLOR=\"blue\">HFONT g_hfAntialias;\nHFONT g_hfClearType;<\/font>\nBOOL\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\n{\n <font COLOR=\"blue\">g_hfAntialias = CreateFont(-20, 0, 0, 0, FW_NORMAL, 0, 0, 0,\n    DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,\n    ANTIALIASED_QUALITY, DEFAULT_PITCH, TEXT(\"Tahoma\"));\n g_hfClearType = CreateFont(-20, 0, 0, 0, FW_NORMAL, 0, 0, 0,\n    DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,\n    CLEARTYPE_QUALITY, DEFAULT_PITCH, TEXT(\"Tahoma\"));\n return g_hfAntialias &amp;&amp; g_hfClearType;<\/font>\n}\nvoid\nOnDestroy(HWND hwnd)\n{\n <font COLOR=\"blue\">if (g_hfAntialias) DeleteObject(g_hfAntialias);\n if (g_hfClearType) DeleteObject(g_hfClearType);<\/font>\n PostQuitMessage(0);\n}\n<font COLOR=\"blue\">\nvoid MultiPaint(HDC hdc, int x, int y, int n)\n{\n LPCTSTR psz = TEXT(\"The quick brown fox jumps over the lazy dog.\");\n int cch = lstrlen(psz);\n for (int i = 0; i &lt; n; i++) {\n   TextOut(hdc, x, y, psz, cch);\n }\n}<\/font>\nvoid\nPaintContent(HWND hwnd, PAINTSTRUCT *pps)\n{\n <font COLOR=\"blue\">int iModePrev = SetBkMode(pps-&gt;hdc, TRANSPARENT);\n HFONT hfPrev = SelectFont(pps-&gt;hdc, g_hfAntialias);\n MultiPaint(pps-&gt;hdc, 10,  0, 1);\n MultiPaint(pps-&gt;hdc, 10, 20, 2);\n MultiPaint(pps-&gt;hdc, 10, 40, 3);\n SelectFont(pps-&gt;hdc, g_hfClearType);\n MultiPaint(pps-&gt;hdc, 10, 80, 1);\n MultiPaint(pps-&gt;hdc, 10,100, 2);\n MultiPaint(pps-&gt;hdc, 10,120, 3);\n SelectFont(pps-&gt;hdc, hfPrev);\n SetBkMode(pps-&gt;hdc, iModePrev);<\/font>\n}\n<\/pre>\n<p>\nThis program creates two fonts, one with anti-aliased (grayscale)\nquality and another with ClearType quality.\n(I have no idea why\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/articles\/407234.aspx#535523\">\npeople claim that\nthere is no thread-safe way to enable ClearType on an individual basis<\/a>.\nWe&#8217;re doing it just fine here.)\n<\/p>\n<p>\nRun this program and take a close look at the results.\nObserve that in each set of three rows of text,\nthe more times we overprint, the darker the text.\nIn particular, notice that overprinting the anti-aliased\nfont makes the result significantly uglier and uglier!\n<\/p>\n<p>\nWhat went wrong?\n<\/p>\n<p>\nThe first time we drew the text, the background was a solid fill\nof the window background color.\nBut when the text is drawn over itself,\nthe background it sees is the previous text output.\nWhen the algorithm decides that\n&#8220;This pixel should be drawn by making the existing pixel\n50% darker,&#8221;\nit actually comes out 75% darker since the pixel is darkened twice.\nAnd if you draw it three times, the pixel comes out 88% darker.\n<\/p>\n<p>\nWhen you draw text, draw it exactly once, and draw it over the\nbackground you ultimately want.\nThis allows the anti-aliasing and ClearType engines to perform\ntheir work with accurate information.\n<\/p>\n<p>\nThe programs that darken the text\nare falling afoul of the overprinting problem.\nWhen the programs decide that some screen content needs to be redrawn\n(for example, if the focus rectangle needs to be added or removed),\nthey &#8220;save time&#8221; by refraining from erasing the background and\nmerely drawing the text again (but with\/without the focus rectangle).\nUnfortunately, if you don&#8217;t erase the background, then the text\nends up drawn over a previous copy of itself, resulting in\ndarkening.\n<\/p>\n<p>\nThe solution is to draw text over the correct background.\nIf you don&#8217;t know what background is on the screen right now,\nthen you need to erase it in order to set it to a known state.\nOtherwise, you will be blending text against an unknown quantity,\nwhich leads to inconsistent (and ugly) results.\n<\/p>\n<p>\nIf you keep your eagle eyes open, you can often spot another case\nwhere people make the overprinting mistake:\nWhen text\nin a control (say, a check box)\nbecomes darker and darker the more times you tab through it.\nThis happens when programs\ndon&#8217;t pay close attention to the flags passed in the\n<code>DRAWITEMSTRUCT<\/code> that is passed\nto the <code>WM_DRAWITEM<\/code> message.\nFor example, some people simply draw the entire item\nin response to the <code>WM_DRAWITEM<\/code> message,\neven though the window manager passed the <code>ODA_FOCUS<\/code> flag,\nindicating that you should only draw or erase the focus rectangle.\nThis is not a problem if drawing the entire item includes\nerasing the background,\nbut if you assume that the <code>WM_ERASEBKGND<\/code> message\nhad erased the background,\nthen you will end up overprinting your text\nin the case where you were asked only to draw the focus rectangle.\nIn that case, the control is not erased;\nall you have to do is draw the focus rectangle.\nIf you also draw the text,\nyou are doing what the <code>MultiPaint<\/code> function did:\nDrawing text over text,\nand the result is text that gets darker each time it repaints.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Windows provides a variety of technologies for rendering monochrome text on color displays, taking advantage of display characteristics to provide smoother results. These include grayscale anti-aliasing as well as the more advanced ClearType technique. Both of these methods read from the background pixels to decide what pixels to draw in the foreground. This means that [&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-30873","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Windows provides a variety of technologies for rendering monochrome text on color displays, taking advantage of display characteristics to provide smoother results. These include grayscale anti-aliasing as well as the more advanced ClearType technique. Both of these methods read from the background pixels to decide what pixels to draw in the foreground. This means that [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/30873","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=30873"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/30873\/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=30873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=30873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=30873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}