﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-λ-calculus（惊愕到手了欧耶，GetBlogPostIds.aspx）-随笔分类-2D</title><link>http://www.cppblog.com/vczh/category/7351.html</link><description>【QQ：343056143，504254078】【MSN：vczh@hotmail.com】【Email：vczh@163.com】【新浪微博：http://weibo.com/vczh】</description><language>zh-cn</language><lastBuildDate>Tue, 06 Nov 2012 02:02:09 GMT</lastBuildDate><pubDate>Tue, 06 Nov 2012 02:02:09 GMT</pubDate><ttl>60</ttl><item><title>一个在GacUI上直接使用GDI或者Direct2D进行绘图操作的小demo</title><link>http://www.cppblog.com/vczh/archive/2012/11/05/194696.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Mon, 05 Nov 2012 15:14:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2012/11/05/194696.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/194696.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2012/11/05/194696.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/194696.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/194696.html</trackback:ping><description><![CDATA[<p><font color="#000000"><span style="color: #000000">由于接下去要用</span></font><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dd374091(v=vs.85).aspx" target="_blank"><u>uniscribe</u></a><font color="#000000"><span style="color: #000000">（这是一个可以告诉我们在渲染一个超长unicode字符串的时候，什么地方可以换行，什么地方要换顺序，什么字符要用一种神奇的方法来渲染之类的库）做可以插入图片和其它乱七八糟东西的rich text box，为了更方便做实验，而且也考虑到很多软件也需要直接绘图的功能，所以我写了这么两个Demo：</span></font></p>
<p><font color="#000000"><span style="color: #000000">1、Rendering.RawAPI.GDI（</span></font><a title="http://www.gaclib.net/Demos/Rendering.RawAPI.GDI/Demo.html" href="http://www.gaclib.net/Demos/Rendering.RawAPI.GDI/Demo.html" target="_blank"><u>http://www.gaclib.net/Demos/Rendering.RawAPI.GDI/Demo.html</u></a><font color="#000000"><span style="color: #000000">）</span><br /><span style="color: #000000">2、Rendering.RawAPI.Direct2D（</span></font><a title="http://www.gaclib.net/Demos/Rendering.RawAPI.Direct2D/Demo.html" href="http://www.gaclib.net/Demos/Rendering.RawAPI.Direct2D/Demo.html" target="_blank"><u>http://www.gaclib.net/Demos/Rendering.RawAPI.Direct2D/Demo.html</u></a><font color="#000000"><span style="color: #000000">）</span></font></p>
<p><font color="#000000"><span style="color: #000000">由于这两个Demo很像，而且Direct2D的比较复杂，所以我在这里介绍一下这个Direct2D的demo。</span></font></p>
<p><font color="#000000"><span style="color: #000000">在Demo里面可以看到，我们可以使用GuiGDIElement或者GuiDirect2DElement来进行手工的绘图操作。这两个Element的使用有限制。当GacUI使用GDI绘图（SetupWindowsGDIRenderer）的时候才可以使用GuiGDIElement，对于Direct2D也是一样的。在使用它们进行绘图的时候，坐标用的是窗口的坐标。但是GacUI会在绘制的时候先加入一个clip，这样我们在绘制的时候就算绘制出了边界，也不会有任何不好的影响。而且这个clip的矩形范围会在渲染事件触发的时候给出。在这里我们先来看一下Direct2D的demo。</span></font></p>
<p><font color="#000000"><span style="color: #000000">首先，整个程序的框架是这样子的：</span></font></p>
<p><font color="#666666">#include "..\..\Public\Source\GacUI.h"<br />#include &lt;math.h&gt;<br />#include &lt;Windows.h&gt;</font></p>
<p><font color="#666666">int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)<br />{<br />&nbsp;&nbsp;&nbsp; // SetupWindowsDirect2DRenderer() is required for GuiDirect2DElement<br />&nbsp;&nbsp;&nbsp; return SetupWindowsDirect2DRenderer();<br />}</font></p>
<p><font color="#666666">class Direct2DWindow : public GuiWindow<br />{<br />protected:</font></p>
<p><font color="#666666">&nbsp;&nbsp;&nbsp; // arguments.rt is ID2D1RenderTarget.<br />&nbsp;&nbsp;&nbsp; void element_Rendering(GuiGraphicsComposition* sender, GuiDirect2DElementEventArgs&amp; arguments)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; }</font></p>
<p><font color="#666666">&nbsp;&nbsp;&nbsp; // The render target is going to be destroyed, any binded resources should be released.<br />&nbsp;&nbsp;&nbsp; void element_BeforeRenderTargetChanged(GuiGraphicsComposition* sender, GuiDirect2DElementEventArgs&amp; arguments)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; }</font></p>
<p><font color="#666666">&nbsp;&nbsp;&nbsp; // The new render target is prepared, any binded resources are allowed to recreate now.<br />&nbsp;&nbsp;&nbsp; void element_AfterRenderTargetChanged(GuiGraphicsComposition* sender, GuiDirect2DElementEventArgs&amp; arguments)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; }<br />public:<br />&nbsp;&nbsp;&nbsp; Direct2DWindow()<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :GuiWindow(GetCurrentTheme()-&gt;CreateWindowStyle())<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetText(L"Rendering.RawAPI.Direct2D");<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetClientSize(Size(640, 480));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetBoundsComposition()-&gt;SetPreferredMinSize(Size(640, 480));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MoveToScreenCenter();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br /></font><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#000000"><strong>GuiDirect2DElement* element=GuiDirect2DElement::Create();<br /></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#000000"><strong>element-&gt;Rendering.AttachMethod(this, &amp;Direct2DWindow::element_Rendering);<br /></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><font color="#000000"><strong>element-&gt;BeforeRenderTargetChanged.AttachMethod(this, &amp;Direct2DWindow::element_BeforeRenderTargetChanged);<br /></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>element-&gt;AfterRenderTargetChanged.AttachMethod(this, &amp;Direct2DWindow::element_AfterRenderTargetChanged);</strong></font></p>
<p><font color="#666666">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GuiBoundsComposition* composition=new GuiBoundsComposition;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; composition-&gt;SetAlignmentToParent(Margin(0, 0, 0, 0));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; composition-&gt;SetOwnedElement(element);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetContainerComposition()-&gt;AddChild(composition);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />};</font></p>
<p><font color="#666666">void GuiMain()<br />{<br />&nbsp;&nbsp;&nbsp; Direct2DWindow window;<br />&nbsp;&nbsp;&nbsp; GetApplication()-&gt;Run(&amp;window);<br />}</font></p>
<p><font color="#000000">在构造函数里面，我们创建了一个GuiDirect2DElement，然后把它放进一个会自动充满整个窗口的composition里面。然后我们需要监听三个事件（GDI只有一个，就是Rendering）：<br />1、Rendering。这个事件在窗口被绘制的时候调用。GacUI才用了一个低功耗的方法让程序不断的绘制自己，所以我们并不需要担心&#8220;如何刷新窗口&#8221;的这个问题。<br />2、BeforeRenderTargetChanged。在这个时候我们要清理掉我们创建出来的资源，譬如说画刷等等。<br />3、AfterRenderTargetChanged。在这个时候我们要建立一些绘图资源，譬如说画刷等等。</font></p>
<p><font color="#000000">为什么下面两个函数那么蛋疼呢？因为Direct2D的类似画刷这样的东西，是必须跟一个ID2D1RenderTarget绑定在一起的，不同的render target之间的画刷不能共享。而且那个可怜的render target还有可能会失效，这个时候GacUI就要重新创建他们。所以无论如何，都必须监听这三个对象，除非我们只用GuiDirect2DElement来渲染文字（因为文字相关的资源是IDWriteFactory控制的，跟render target无关）。</font></p>
<p><font color="#000000">在这个Demo里面，我们要画的是一个会动的钟。在这个钟里面我们要绘制4种线形：边框、时针、分针、秒针。因此我们需要4个不同的ID2D1SolidColorBrush。由于操作COM对象的时候总要去记得操作那个引用计数，特别的麻烦，而且还容易忘掉。所以我特地为大家提供了一个叫做ComPtr的东西。所以我们就可以这么声明、创建和释放他们：</font></p>
<p><font color="#666666">ComPtr&lt;ID2D1SolidColorBrush&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; borderBrush;<br />ComPtr&lt;ID2D1SolidColorBrush&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; secondBrush;<br />ComPtr&lt;ID2D1SolidColorBrush&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; minuteBrush;<br />ComPtr&lt;ID2D1SolidColorBrush&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hourBrush;<br /><br />// The render target is going to be destroyed, any binded resources should be released.<br />void element_BeforeRenderTargetChanged(GuiGraphicsComposition* sender, GuiDirect2DElementEventArgs&amp; arguments)<br />{<br />&nbsp;&nbsp;&nbsp; borderBrush=0;<br />&nbsp;&nbsp;&nbsp; secondBrush=0;<br />&nbsp;&nbsp;&nbsp; minuteBrush=0;<br />&nbsp;&nbsp;&nbsp; hourBrush=0;<br />}</font></p>
<p><font color="#666666">// The new render target is prepared, any binded resources are allowed to recreate now.<br />void element_AfterRenderTargetChanged(GuiGraphicsComposition* sender, GuiDirect2DElementEventArgs&amp; arguments)<br />{<br />&nbsp;&nbsp;&nbsp; ID2D1SolidColorBrush* brush;<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brush=0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arguments.rt-&gt;CreateSolidColorBrush(D2D1::ColorF(0.0f, 0.0f, 0.0f), D2D1::BrushProperties(), &amp;brush);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; borderBrush=brush;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brush=0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arguments.rt-&gt;CreateSolidColorBrush(D2D1::ColorF(0.0f, 0.0f, 1.0f), D2D1::BrushProperties(), &amp;brush);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; secondBrush=brush;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brush=0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arguments.rt-&gt;CreateSolidColorBrush(D2D1::ColorF(0.0f, 1.0f, 0.0f), D2D1::BrushProperties(), &amp;brush);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; minuteBrush=brush;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; brush=0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arguments.rt-&gt;CreateSolidColorBrush(D2D1::ColorF(1.0f, 0.0f, 0.0f), D2D1::BrushProperties(), &amp;brush);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hourBrush=brush;<br />&nbsp;&nbsp;&nbsp; }<br />}</font></p>
<p><font color="#000000">想必大家都应该看清楚了。Before和After事件里面，GacUI都会提供用来绘图的ID2D1RenderTarget，这个时候必须正确的创建和释放资源。只要这些资源都建立了起来，那么剩下的就只有把一个时钟画出来了。画一个时钟还是很容易的，只需要那么几行代码就行了：</font></p>
<p>static const int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Radius=200;<br />static const int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LongScale=10;<br />static const int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ShortScale=5;<br />static const int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SecondLength=180;<br />static const int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MinuteLength=150;<br />static const int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HourLength=120;</p>
<p>float GetAngle(float second)<br />{<br />&nbsp;&nbsp;&nbsp; return (second-15.0f)*3.1416f/30.0f;<br />}</p>
<p>void DrawLine(ID2D1RenderTarget* rt, ComPtr&lt;ID2D1SolidColorBrush&gt; brush, float angle, int width, int startLength, int endLength, int x, int y)<br />{<br />&nbsp;&nbsp;&nbsp; float s=sin(angle);<br />&nbsp;&nbsp;&nbsp; float c=cos(angle);<br />&nbsp;&nbsp;&nbsp; float x1=(c*startLength)+(float)(x+Radius);<br />&nbsp;&nbsp;&nbsp; float y1=(s*startLength)+(float)(y+Radius);<br />&nbsp;&nbsp;&nbsp; float x2=(c*endLength)+(float)(x+Radius);<br />&nbsp;&nbsp;&nbsp; float y2=(s*endLength)+(float)(y+Radius);<br />&nbsp;&nbsp;&nbsp; rt-&gt;DrawLine(D2D1::Point2F(x1, y1), D2D1::Point2F(x2, y2), brush.Obj(), (float)width);<br />}</p>
<p>// arguments.rt is ID2D1RenderTarget.<br />void element_Rendering(GuiGraphicsComposition* sender, GuiDirect2DElementEventArgs&amp; arguments)<br />{<br />&nbsp;&nbsp;&nbsp; int w=arguments.bounds.Width();<br />&nbsp;&nbsp;&nbsp; int h=arguments.bounds.Height();<br />&nbsp;&nbsp;&nbsp; int x=arguments.bounds.Left()+(w-Radius*2)/2;<br />&nbsp;&nbsp;&nbsp; int y=arguments.bounds.Left()+(h-Radius*2)/2;</p>
<p>&nbsp;&nbsp;&nbsp; arguments.rt-&gt;DrawEllipse(D2D1::Ellipse(D2D1::Point2F((float)(x+Radius), (float)(y+Radius)), (float)Radius, (float)Radius), borderBrush.Obj());<br />&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;60;i++)<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int scale=i%5==0?LongScale:ShortScale;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float angle=GetAngle((float)i);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DrawLine(arguments.rt, borderBrush, angle, 1, Radius-scale, Radius, x, y);<br />&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; DateTime dt=DateTime::LocalTime();<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float angle=GetAngle(dt.hour*5+dt.minute/12.0f+dt.second/720.0f);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DrawLine(arguments.rt, hourBrush, angle, 5, 0, HourLength, x, y);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float angle=GetAngle(dt.minute+dt.second/60.0f);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DrawLine(arguments.rt, minuteBrush, angle, 3, 0, MinuteLength, x, y);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; float angle=GetAngle((float)dt.second);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DrawLine(arguments.rt, secondBrush, angle, 1, 0, SecondLength, x, y);<br />&nbsp;&nbsp;&nbsp; }<br />}</p>
<p><font color="#000000">然后我们就获得了下图：（LiveWrite真是太好了，cppblog的傻逼编辑器每次插入图片都会插入到一个诡异的位置中去）</font></p>
<p><a href="http://www.cppblog.com/images/cppblog_com/vczh/Windows-Live-Writer/GacUI_1405F/DXGUI_58_2.jpg"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="DXGUI_58" border="0" alt="DXGUI_58" src="http://www.cppblog.com/images/cppblog_com/vczh/Windows-Live-Writer/GacUI_1405F/DXGUI_58_thumb.jpg" width="660" height="522" /></a></p>
<p><font color="#000000">这样我们就完成了一个时钟的制作了，而且也学会了如何在GacUI里面直接使用GDI和Direct2D绘图了。</font></p><img src ="http://www.cppblog.com/vczh/aggbug/194696.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2012-11-05 23:14 <a href="http://www.cppblog.com/vczh/archive/2012/11/05/194696.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>偶尔发布一下弱智知识——如何用Direct2D测量文字大小</title><link>http://www.cppblog.com/vczh/archive/2011/10/11/158090.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Tue, 11 Oct 2011 15:42:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2011/10/11/158090.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/158090.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2011/10/11/158090.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/158090.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/158090.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 之前一边做脚本引擎，一边山寨一个自绘的native C++的GUI框架并且可以切换GDI或者Direct2D渲染模式。因为抄了WPF的那种高级自动布局功能，所以必然需要知道如何测量文字大小。Direct2D测量文字大小比较麻烦，不像GDI有直接函数，并且用中英文搜好像都没人直接给出结果，还有人在博客上写&#8220;这种事情好像办不到&#8221;这样的文字。不过经过我遍历MSDN，还是找到了一个曲线救国的方法的，直接上代码：<br /><br />&nbsp;&nbsp;&nbsp; 首先是创建IDWriteTextFormat:</p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IDWriteFactory</span><span style="color: #000000">*</span><span style="color: #000000">&nbsp;dwriteFactory</span><span style="color: #000000">=</span><span style="color: #000000">GetDirectWriteFactory();<br /></span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IDWriteTextFormat</span><span style="color: #000000">*</span><span style="color: #000000">&nbsp;format</span><span style="color: #000000">=</span><span style="color: #000000">0</span><span style="color: #000000">;<br /></span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;hr</span><span style="color: #000000">=</span><span style="color: #000000">dwriteFactory</span><span style="color: #000000">-&gt;</span><span style="color: #000000">CreateTextFormat(<br /></span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fontProperties.fontFamily.Buffer(),<br /></span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL,<br /></span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(fontProperties.bold</span><span style="color: #000000">?</span><span style="color: #000000">DWRITE_FONT_WEIGHT_BOLD:DWRITE_FONT_WEIGHT_NORMAL),<br /></span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(fontProperties.italic</span><span style="color: #000000">?</span><span style="color: #000000">DWRITE_FONT_STYLE_ITALIC:DWRITE_FONT_STYLE_NORMAL),<br /></span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWRITE_FONT_STRETCH_NORMAL,<br /></span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(FLOAT)fontProperties.size,<br /></span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L</span><span style="color: #000000">""</span><span style="color: #000000">,<br /></span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000">&amp;</span><span style="color: #000000">format);<br /></span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(</span><span style="color: #000000">!</span><span style="color: #000000">FAILED(hr))<br /></span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /></span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;format</span><span style="color: #000000">-&gt;</span><span style="color: #000000">SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);<br /></span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;format;<br /></span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">else</span><span style="color: #000000"><br /></span><span style="color: #008080">18</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /></span><span style="color: #008080">19</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">;<br /></span><span style="color: #008080">20</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<p>&nbsp;&nbsp;&nbsp; fontProperties是我自定义的一个结构就不用去管它了，参考MSDN就知道CreateTextFormat如何使用了。其中fontProperties.fontFamily是字体的名字。然后IDWriteTextFormat就扮演着GDI的&#8220;字体对象&#8221;的角色，可以用ID2D1RenderTarget进行绘制。ID2D1RenderTarget除了用IDWriteTextFormat当字体以外，还可以用IDWriteTextLayout当&#8220;添加多余信息的更复杂的字体&#8221;。测量文字的关键正是在这里。<br /><br />&nbsp;&nbsp;&nbsp; 接下来我们借助IDWriteTextFormat来创建IDWriteTextLayout：</p>
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080">&nbsp;1</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IDWriteTextLayout</span><span style="color: #000000">*</span><span style="color: #000000">&nbsp;textLayout</span><span style="color: #000000">=</span><span style="color: #000000">0</span><span style="color: #000000">;<br /></span><span style="color: #008080">&nbsp;2</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HRESULT&nbsp;hr</span><span style="color: #000000">=</span><span style="color: #000000">GetDirectWriteFactory()</span><span style="color: #000000">-&gt;</span><span style="color: #000000">CreateTextLayout(<br /></span><span style="color: #008080">&nbsp;3</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oldText.Buffer(),<br /></span><span style="color: #008080">&nbsp;4</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oldText.Length(),<br /></span><span style="color: #008080">&nbsp;5</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textFormat,<br /></span><span style="color: #008080">&nbsp;6</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">,<br /></span><span style="color: #008080">&nbsp;7</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000">0</span><span style="color: #000000">,<br /></span><span style="color: #008080">&nbsp;8</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000">&amp;</span><span style="color: #000000">textLayout);<br /></span><span style="color: #008080">&nbsp;9</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(</span><span style="color: #000000">!</span><span style="color: #000000">FAILED(hr))<br /></span><span style="color: #008080">10</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /></span><span style="color: #008080">11</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DWRITE_TEXT_METRICS&nbsp;metrics;<br /></span><span style="color: #008080">12</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hr</span><span style="color: #000000">=</span><span style="color: #000000">textLayout</span><span style="color: #000000">-&gt;</span><span style="color: #000000">GetMetrics(</span><span style="color: #000000">&amp;</span><span style="color: #000000">metrics);<br /></span><span style="color: #008080">13</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">if</span><span style="color: #000000">(</span><span style="color: #000000">!</span><span style="color: #000000">FAILED(hr))<br /></span><span style="color: #008080">14</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /></span><span style="color: #008080">15</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;minSize</span><span style="color: #000000">=</span><span style="color: #000000">Size((</span><span style="color: #0000ff">int</span><span style="color: #000000">)ceil(metrics.widthIncludingTrailingWhitespace),&nbsp;(</span><span style="color: #0000ff">int</span><span style="color: #000000">)ceil(metrics.height));<br /></span><span style="color: #008080">16</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /></span><span style="color: #008080">17</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textLayout</span><span style="color: #000000">-&gt;</span><span style="color: #000000">Release();<br /></span><span style="color: #008080">18</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff">return</span><span style="color: #000000">;<br /></span><span style="color: #008080">19</span>&nbsp;<span style="color: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<p>&nbsp;&nbsp;&nbsp; 这里看minSize就知道如何测量文字的字体了。<br /><br />&nbsp;&nbsp;&nbsp; 在这里放一张暂时的图片。我抄了WPF的那种方法，从布局和绘图元素直接开始可以构造GUI，因此演示了如何使用这些东西来创造一个Win7的按钮（带动画的哦）打开<a style="text-decoration: underline" href="http://vlpp.codeplex.com" target="_blank">Vczh Library++3.0</a>，下载代码并打开Candidate\GUI\GuiDemo\GuiDemo.sln，按F5就可以看到了效果了：<br /><br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/vczh/DXGUI_01.jpg" width="387" height="295" /><br /><br />&nbsp;&nbsp;&nbsp; 这一个是Direct2D渲染的结果（我在工程文件指定了DXSDK的绝对路径，如果你们安装的地方不同改掉它既可编译了）。按钮基本上跟win7的效果一摸一样，但是这里使用Direct2D进行渲染。当按钮尺寸变化的时候，那个复杂的边框和里面的两个渐变和文字都可以自动对齐&#8212;&#8212;但是这并不是hard code的，而是GUI的&#8220;布局功能&#8221;可以配置成这个样子。像这两个按钮一直处于右下角也是&#8220;布局功能&#8221;可以提供的功能。大家可以理解为这东西类似于C#的TableLayoutPanel。<br /><br />&nbsp;&nbsp;&nbsp; 现在刚刚做好了按钮&#8212;&#8212;跟WPF一样可以更换template，不过因为反正没人需要动态更换template所以我把template写在了构造函数里面，因此换肤这种事情就变得相当简单了&#8212;&#8212;只要用布局功能跟图元拼凑成一个复杂的图形，然后实现各个控件所规定的&#8220;template接口&#8221;响应外观控制的消息就行了，内置动画支持（这个要运行的时候才能观察到）。<br /><br />&nbsp;&nbsp;&nbsp; 为了方便，我在工程里面除了Debug和Release以外还加入了DebugDirect2D和ReleaseDirect2D两个配置，可以自由切换观看demo。<br /><br /><br /></p><img src ="http://www.cppblog.com/vczh/aggbug/158090.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2011-10-11 23:42 <a href="http://www.cppblog.com/vczh/archive/2011/10/11/158090.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GUI真TAMA难做啊</title><link>http://www.cppblog.com/vczh/archive/2011/04/30/145380.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sat, 30 Apr 2011 03:50:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2011/04/30/145380.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/145380.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2011/04/30/145380.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/145380.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/145380.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 从某种意义上来说，做图形也好，做GUI也好，做编译器也好，大概都是一种情结。其实只要稍微想一想就知道，能把它们三者有机统一起来的，就只有游戏。我很久以前的确是为了想开发游戏才对编程产生兴趣的，而学习游戏开发占据了我前六年的时间。虽然现在不做了，不过偶尔总是会觉得手痒。但是做游戏没美工做不好怎么办呢？就只好写游戏代码了。但是没有资源写出来的游戏又不好玩，于是就只好写库。那写什么库呢，自然就只有渲染器、界面引擎和脚本引擎了。我的博客的大部分文章也是围绕着这三件事情建立起来，而且在中间不断切换的。<br><br>&nbsp;&nbsp;&nbsp; 撒，所以今天就轮到GUI了。我一直很想做出一个自绘的GUI出来，无奈一直设计不出一个好的架构。后来尝试用原生API，但是却又很不喜欢MFC的设计，就尝试自己照着.NET Framework和Delphi那套VCL的样子<a style="TEXT-DECORATION: underline" href="http://www.cppblog.com/vczh/archive/2008/08/25/59954.html" target=_blank>封装了一个控件库</a>出来。无奈原生API细节无敌多，后来没做把所有的功能都全部做完。因此后来一段时间凡是需要界面我都直接用C#做。然后CEGUI出了，WPF和Silverlight也出了，我发现如今要做一个漂亮的GUI非自绘已经做不到了，所以我又做了一次尝试。<br><br>&nbsp;&nbsp;&nbsp; 去年在美帝的时候曾经试图再设计一次，得到了一些结果。后来我发现其实根本没办法为GDI、DirectX、OpenGL和其它绘图设备抽象一个公用的接口，否则就会遭遇大量性能问题。因为在很多细节上，譬如说渲染文字，为了达到较高的性能，OpenGL和DirectX需要使用几乎相反的策略来做。因此这次我又换了一个方法，而且在<a style="TEXT-DECORATION: underline" href="http://vlpp.codeplex.com/" target=_blank>Vczh Library++ 3.0</a>的Candidate目录下已经checkin了一个试验品。<br><br>&nbsp;&nbsp;&nbsp; 我把一个自绘的GUI分成了下面若干个层次。<br>&nbsp;&nbsp;&nbsp; 1、NativeWindow。NativeWindow表示的是一个顶层窗口的实现。譬如说我们想用Windows的窗口作为自绘窗口的顶层窗口（游戏里面的很多顶层窗口是绘制在游戏窗口里面的，所以顶层窗口并不一定是Windows的窗口）。<br>&nbsp;&nbsp;&nbsp; 2、控件库。控件库包含了这个自绘GUI库的所有预定义控件。控件本身包含对用户输入的相应逻辑，但是每一个控件的绘制以及鼠标点中测试不在此范围内。<br>&nbsp;&nbsp;&nbsp; 3、控件皮肤接口。每一个最终控件都会拥有一个控件皮肤接口。每当控件的状态发生了变化，控件会调用皮肤接口更新控件的当前状态。每当控件需要知道某一个点是否位于一个控件里面的时候，他也会去调用该控件的皮肤获得结果。因此控件皮肤接口包含了一切关于绘制（因此理所当然也就包含了点中测试）的逻辑。<br><br>&nbsp;&nbsp;&nbsp; 为了达到最高的性能，一套皮肤的实现只能绑定在某种绘图设备上，也就是说缺省状态下一套为GDI设备设计出来的皮肤是不能直接使用在DirectX设备上面的。当然我这个框架的设计也是足够开放的，如果你非得用同一套代码来实现不同绘图设备上的皮肤，那么你是可以自己动手丰衣足食，做到给GDI和DirectX设计一个公共接口并插入我的GUI框架的（只不过这种做法一般情况下都会惨死）。<br><br>&nbsp;&nbsp;&nbsp; 那么如何添加绘图设备呢？目前NativeWindow有一个基于Windows窗口的实现，并且NativeWindow的接口要求该实现在创建、销毁、接收到很多窗口事件的时候都调用某一个回调对象。我们可以通过注册一个全局回调对象或者具体窗口的回调对象来获得NativeWindow状态的变更。基于Windows窗口的NativeWindow实现还提供了一个额外函数，可以让你获得一个NativeWindow的HWND（但这个函数并不被控件库依赖）。现在我还实现了一个基于HWND+HDC的绘图设备，主要方法就是先注册全局回调对象，每当知道一个NativeWindow被创建了，我就会注册一个NativeWindow的回调对象，用来维护一个窗口里面的一块32位DIBSections位图缓冲区。窗口的大小如果变化了，我也会在适当的时候重新创建一块合适的缓冲区。不过为了避免每一次大小变化都会创建新的缓冲区，我创建的缓冲区的大小总会比窗口大一点。然后这个GDI绘图设备就暴露了一个函数，可以获得一个NativeWindow的HDC和WinGDIElementEnvironment。<br><br>&nbsp;&nbsp;&nbsp; WinGDIElementEnvironment是基于HWND+HDC的这一套实现上专有的、为了GDI皮肤设计出来的一个公共的资源库（譬如用来保存各种面向业务逻辑的pen啊brush什么的，比如说disable的时候什么颜色，选中的时候什么颜色等等）。如果你想设计一个基于HWND+DirectX的皮肤，那么类似WinGDIElementEnvironment的这套东西要重新做一次——因为为了达到相同的性能。具体细节相差太大。当然HWND+HDC上面可以有多套皮肤，WinGDIElementEnvironment是公用的。WinGDIElementEnvironment要求绘制是通过一个具体的WinGDIElement对象达到的，而一套皮肤可以有自己的一套WinGDIElement的实现。WinGDIElement被设计成面向业务的、一套皮肤的基本元素组成部分，譬如说按钮边框啦、焦点长方形啦、文字啦，而不是带有pen和brush的长方形啊，文字啊，各种乱七八糟的最低等级的绘图元素。举一个例子，按钮边框跟菜单边框很像，都可以用Rectangle来组成。但是Element里面就直接是按钮边框和菜单边框，而不是一个可以让你自由修改颜色的Rectangle。因为不同的控件要共享配色方案，而配色方案是由业务逻辑+空间状态的集合实现的，因此WinGDIElement还是一个比较高层次的概念。当一个WinGDIElement被渲染的时候，他会给你一个HDC，然后你根据被设置的状态来调用GDI函数绘制到HDC指向的32位DIBSections位图缓冲区上面。<br><br>&nbsp;&nbsp;&nbsp; 那么，当我们使用HWND+HDC的实现，创建了一个布满了控件的窗口，那实际上是发生了什么事情呢？首先控件自己会组成一棵树。其次，控件的皮肤也会组成一棵树。现在就有控件树跟皮肤树两颗树了。控件树负责所有用户输入变更状态的逻辑部分，而皮肤树负责绘图和点中测试。而一个HWND+HDC实现的皮肤树，会在皮肤组合成树的时候，在底下又组合出了一颗WinGDIElement树。因此大局上就是：<br>&nbsp;&nbsp;&nbsp; 控件树（负责相应输入变更状态）--&gt; 皮肤树（负责储存控件状态的可视部分并决定什么时候需要刷新）--&gt;WinGDIElement树（负责绘图整个窗口）<br><br>&nbsp;&nbsp;&nbsp; 这个时候，如果我们仅仅需要简单的重新绘制窗口的话，那么控件树跟皮肤树都不需要被访问到，底层仅需要让WinGDIElement树重新绘制一遍即可。而WinGDIElement的粒度实际上也不小，因此不会每一个图元都有一个WinGDIElement从而使得创建出了一大堆对象的。<br><br>&nbsp;&nbsp;&nbsp; 最后一个设计就是在什么时候才重绘窗口的问题。假设说我们现在收到了一个WM_KEYDOWN消息，最终传播到了控件树里面去，然后修改了10个控件上面的文字。每当你修改文字的时候实际上都需要重绘，那如何将无数次不可控的重绘合并成一次呢？SendMessage(WM_PAINT)是立刻执行的，所以Windows自带的合并WM_PAINT的方法在这个时候是无效的。我所采取的解决方法就是：反正控件树的所有消息来源都是从NativeWindow里面来的，那实际上控件树发出一个重绘请求的时候，我就会把NativeWindow的HWND实现里面的一个bool变量设成true，然后当NativeWindow每一个传播到控件树的消息结束传播之后，才读一次那个变量，如果是true，那么就调用WinGDIElement进行重绘并把变量设计成false。坏处是每一个传播到控件树的消息在处理完之后都必须检查是否需要重绘，好处是这个东西被封装在了NativeWindow的HWND实现里面里面，无论是控件树、皮肤树还是WinGDIElement树也好，都在也不需要关心绘图时机的事情了。<br><br>&nbsp;&nbsp;&nbsp; 因为GUI被分割成了很多层，而且每一层的都关心业务逻辑的不同部分，所以他们都是可以被替换的。譬如说我们可以做成：<br>&nbsp;&nbsp;&nbsp; HWND+HDC实现：最普通的方法<br>&nbsp;&nbsp;&nbsp; HWND+DirectX：WPF和Silverlight地方法<br>&nbsp;&nbsp;&nbsp; 单一HWND+多个虚拟窗口+DirectX：可以在游戏里面用<br><br>&nbsp;&nbsp;&nbsp; 无论下面的绘图设备和窗口实现如何发生变化，GUI控件的逻辑部分都跟这些实现严格分离，因此不会受到影响。而且大部分情况下，我们是不需要拥有一个跨绘图设备的皮肤库的，譬如说游戏和应用程序，外表总不能做成一样的。对于那些需要同时在DirectX和OpenGL上面运行的程序（譬如说3dsmax），它已经有DirectX和OpenGL的公共接口了，因此这些软件可以利用它们的公共接口来实现GUI的绘图设备部分，从而在上面构造起来的皮肤自然是可以跨DirectX和OpenGL的。<br><br>&nbsp;&nbsp;&nbsp; 这比起一年前作的GUI实现又进了一大步。上一次的GUI尝试为不同的绘图设备抽象一套公共接口，后来惨死。不知道这次实际上做出来的效果如何，拭目以待吧。 
<img src ="http://www.cppblog.com/vczh/aggbug/145380.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2011-04-30 11:50 <a href="http://www.cppblog.com/vczh/archive/2011/04/30/145380.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++界面库：使用Graphic Element Template制作按钮模板</title><link>http://www.cppblog.com/vczh/archive/2009/08/20/93951.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 20 Aug 2009 13:38:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2009/08/20/93951.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/93951.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2009/08/20/93951.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/93951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/93951.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这一次制作的按钮模板具有通过模板属性动态配置图形的功能。模板的属性一共有6个：x、y、w、h、state、content，其中state有normal、hot和press三个取值。XML、代码和截图如下：&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2009/08/20/93951.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/93951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2009-08-20 21:38 <a href="http://www.cppblog.com/vczh/archive/2009/08/20/93951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++界面库：Graphic Element Template实现</title><link>http://www.cppblog.com/vczh/archive/2009/08/19/93848.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Wed, 19 Aug 2009 11:29:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2009/08/19/93848.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/93848.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2009/08/19/93848.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/93848.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/93848.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这篇文章描述的一个图形元素模板终于通过了冒烟测试。下面将展示模板的XML代码、调用模板的代码以及截图。&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2009/08/19/93848.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/93848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2009-08-19 19:29 <a href="http://www.cppblog.com/vczh/archive/2009/08/19/93848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++界面库：为Graphic Element Template做了一个XML Schema</title><link>http://www.cppblog.com/vczh/archive/2009/08/07/92575.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Fri, 07 Aug 2009 15:29:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2009/08/07/92575.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/92575.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2009/08/07/92575.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/92575.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/92575.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 我们知道制作控件的时候，其实最困难的不是定出那个支撑整个系统的架构，而是为各种空间写绘制的代码（囧）。为了解决这个问题，我在这套渲染库上设计了一种XML写成的模板，然后在模板内部提供一个简单的语言来进行简单但是强大的运算。这样的话，不仅可以省略很多代码，还为控件的换肤提供了强有力的支持。<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2009/08/07/92575.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/92575.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2009-08-07 23:29 <a href="http://www.cppblog.com/vczh/archive/2009/08/07/92575.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++界面库：在GDI上添加完全的alpha混合支持</title><link>http://www.cppblog.com/vczh/archive/2009/08/02/91972.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sun, 02 Aug 2009 10:41:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2009/08/02/91972.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/91972.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2009/08/02/91972.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/91972.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/91972.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     做完了小型WCF之后，就是小型的WPF了。之前那个完全避免用户大部分的错误使用导致的死锁的方案还有一点点的问题，所以先休息一下，做做别的。为了在C++上重现一套类似WPF的工具，首先要解决绘图部分。<br><br>    绘图的设备当然是需要可切换的，于是用bridge模式定义了大量的接口，这些接口用来创建画笔、画刷、字体和图形，然后图形用树的形式组织起来，最后放到一块跟窗口链接的画板上面，有需要的时候自动绘制。为了最快速地开始工作，我实现了一个GDI的绘图设备，以后有空再做DirectX的。但是我们知道GDI对alpha的支持是很弱的，只有一个叫alphablend的API用来贴bitmap，因此为了让画刷和画笔都能够支持alpha渐变、alpha位图和alpha颜色，做了很多的工作。&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2009/08/02/91972.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/91972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2009-08-02 18:41 <a href="http://www.cppblog.com/vczh/archive/2009/08/02/91972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>将树型数据结构转换为图片并自动排版</title><link>http://www.cppblog.com/vczh/archive/2008/09/07/61244.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Sun, 07 Sep 2008 12:18:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2008/09/07/61244.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/61244.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2008/09/07/61244.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/61244.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/61244.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 『啊，葱爆羊肉真是香啊。』一边回忆着这个令人感动的美食，一边心不在焉地写了个程序。这个小程序实现了一个很简单的功能，将一棵树转换成bmp图片并自动排版。&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2008/09/07/61244.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/61244.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2008-09-07 20:18 <a href="http://www.cppblog.com/vczh/archive/2008/09/07/61244.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用拓扑进行几何图形布尔运算</title><link>http://www.cppblog.com/vczh/archive/2008/06/17/53675.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Tue, 17 Jun 2008 03:20:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2008/06/17/53675.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/53675.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2008/06/17/53675.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/53675.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/53675.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     首先吐槽一下：今天考IT项目管理，100道选择题。前几天考配置管理，10道大题。如今的老师都喜欢走极端……<br><br>    这个方法是在考完试回宿舍的路上想到的，适用于2D与3D。主要想法是这样的。给定两个几何图形A、B，把A和B都分成『内『、『外』两部分。A的『内』就是处于B内部的部分。于是A和B就变成了A内、A外、B内、B外。然后就有如下公式：<br>    ·A and B=A外+B外<br>    ·A sub B=A外+B内<br>    ·A or B=A内+B内<br>    ·A xor B=A外+B外+A内+B内<br>    这种数据结构是为了满足如下算法：一个A点在图形内<==>过这个点的直线交图形与点集P，其中|{Pi|Pi<=A}|和|{Pi|Pi>=A}|都是奇数。注意我们使用的是<=和>=，这样的话两个集合的数量的奇偶性都是一致的。这个算法无论2D、3D多边形还是3D多面体都能适用，就算是这个图形有孔（镶嵌）也可以，而且跟凹凸体无关。这个算法只有一种情况是不能用的：就是自己跟自己有交叉，譬如我们习惯的5条直线构成五角星的画法。这&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2008/06/17/53675.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/53675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2008-06-17 11:20 <a href="http://www.cppblog.com/vczh/archive/2008/06/17/53675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>接下去的事情</title><link>http://www.cppblog.com/vczh/archive/2008/06/13/53081.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 12 Jun 2008 17:40:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2008/06/13/53081.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/53081.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2008/06/13/53081.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/53081.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/53081.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 1：开发将椭圆、贝塞尔曲线以及文字转换成多边形的工具<br><br>&nbsp;&nbsp;&nbsp; 2：解决多边形的布尔运算，有并集、交集和余集等<br><br>&nbsp;&nbsp;&nbsp; 3：使用2开发画笔<br><br>&nbsp;&nbsp;&nbsp; 4：开发画刷基础，实现Color Matrix、Vertex Transformation及多边形渲染<br><br>&nbsp;&nbsp;&nbsp; 5：图像工具
<img src ="http://www.cppblog.com/vczh/aggbug/53081.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2008-06-13 01:40 <a href="http://www.cppblog.com/vczh/archive/2008/06/13/53081.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>椭圆段扫描完成</title><link>http://www.cppblog.com/vczh/archive/2008/06/12/53057.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 12 Jun 2008 14:02:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2008/06/12/53057.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/53057.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2008/06/12/53057.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/53057.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/53057.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     终于完成了相当于GDI中Arc函数的功能了。这次仍然跟API有点误差，不多这里的误差是GDI的问题。这里贴出截图和代码。观看图2和图3，我们知道椭圆是对称的，但是GDI并没有做到这一点。我的算法也不能精确对称，但是左右两边仅相差1个像素。Arc是椭圆的子集，所以Arc在这个局部（椭圆的最上方）中也应该左右对称。图1中我先用GDI绘制黑色Arc，然后使用我的算法绘制红色Arc。这里的自适应步长算法跟上一篇的Bezier曲线的算法是一样的。&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2008/06/12/53057.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/53057.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2008-06-12 22:02 <a href="http://www.cppblog.com/vczh/archive/2008/06/12/53057.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>自适应步长Bezier曲线扫描</title><link>http://www.cppblog.com/vczh/archive/2008/06/12/53000.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Thu, 12 Jun 2008 05:20:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2008/06/12/53000.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/53000.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2008/06/12/53000.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/53000.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/53000.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     花了两个小时把这个东西做好了。虽然不及[LIEN87 ; SHAN87 ; SHAN89]论文厉害，不过自己弄的这个写起来倒是相当容易的。在这里贴出效果图和代码。效果图中，我先使用蓝色画笔，用PolyBezier绘制曲线，然后使用红色像素使用自己的算法绘制曲线。可以看见有一点点误差，不过效果还是可以接受的。代码仍然使用自己的那套库开发，不过曲线扫描的方法不受库的限制。这个算法保证点不会被重复绘制。<br><br>    有了这个算法之后我就可以把贝塞尔曲线转换成密度刚好的折线了。这才是最终目的。&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2008/06/12/53000.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/53000.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2008-06-12 13:20 <a href="http://www.cppblog.com/vczh/archive/2008/06/12/53000.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>终于提取到了TrueType字体的轮廓了</title><link>http://www.cppblog.com/vczh/archive/2008/06/11/52949.html</link><dc:creator>陈梓瀚(vczh)</dc:creator><author>陈梓瀚(vczh)</author><pubDate>Wed, 11 Jun 2008 15:48:00 GMT</pubDate><guid>http://www.cppblog.com/vczh/archive/2008/06/11/52949.html</guid><wfw:comment>http://www.cppblog.com/vczh/comments/52949.html</wfw:comment><comments>http://www.cppblog.com/vczh/archive/2008/06/11/52949.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cppblog.com/vczh/comments/commentRss/52949.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/vczh/services/trackbacks/52949.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要:     为了让这篇文章说的东西能够落实，无法躲避的基本东西还是要先准备一下的。今天花了6个小时查了无数资料终于把文字的边框弄出来了。<br><br>    在此贴出代码和效果图，不作过多解释。熟悉Win32API中的GDI部分的朋友们可以很容易看懂。<br><br>    效果图：<br>    提取的轮廓：红色和黑色为直线，蓝色为四次贝塞尔曲线。其中上面是先TextOut后自己画，下面是先自己画后TextOut。四次贝塞尔曲线转换成三次贝塞尔曲线之后使用PolyBezier绘制。<br><br>    代码：<br>    代码使用的框架是我自己寒假无聊的时候封装API的结果，暂时有窗口、菜单、组合键以及菜单，附带GDI。事件自己弄了一个跟C#差不多的可以同时Bind很多不同种类函数的东西。不过这个不是重点。需要重点阅读的是如何使用GetGlyphOutline。<br><br>    dtof将double转换成FIXED，ftod相反。<br>    GetPoint进行点的变换，主要是因为画字符的时候需要偏移。<br>    DrawCurve绘制边框。&nbsp;&nbsp;<a href='http://www.cppblog.com/vczh/archive/2008/06/11/52949.html'>阅读全文</a><img src ="http://www.cppblog.com/vczh/aggbug/52949.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/vczh/" target="_blank">陈梓瀚(vczh)</a> 2008-06-11 23:48 <a href="http://www.cppblog.com/vczh/archive/2008/06/11/52949.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>