通过EM_FORMATRANGE缩放富编辑控件的渲染输出时的舍入错误

时间:2012-06-26 19:40:06

标签: c++ c windows winapi richtextbox

我正在使用EM_FORMATRANGE消息将富文本控件的输出呈现给任意设备上下文。但是,当渲染到位图时,位图的设备上下文的每英寸点数与显示设备的DPI相同,即每英寸96点。这比我想呈现的要低得多。我宁愿以更高的DPI渲染,以便用户可以放大,也可以在以后的高DPI打印机上打印。

我怀疑发生的情况是RTF控件使用LOGPIXELSXLOGPIXELSY调用GetDeviceCaps来获取设备每英寸的像素数。然后,它使用此DPI值以100%缩放级别呈现文档。 Windows显示设备始终返回96 DPI的值,除非在系统上使用大字体(在控制面板中设置)并且应用程序支持DPI。

互联网上的许多示例都建议缩放EM_FORMATRANGE的输出。这样可以实现任何任意的DPI分辨率。大多数示例通常涉及使用SetMapModeSetWindowExtExSetViewportExtEx(例如,请参阅http://social.msdn.microsoft.com/Forums/en-us/netfxbcl/thread/37fd1bfb-f07b-421d-9b5e-5f4492ffbbc3)。这些函数可用于缩放富文本控件的渲染输出:例如,如果我指定400%缩放,那么如果富文本控件呈现5像素宽的内容,则实际上将变为20像素宽。

不幸的是,旧的GDI函数使用整数而不是浮点数。例如,假设RTF控件决定应在(12.7,15.3)像素处绘制元素。这将四舍五入到(13,15)的位置。这些圆角坐标传递给GDI,GDI然后使用SetMapMode指定的缩放比例放大图像:对于400%的示例,它将是(13 * 4,15 * 4)或(52,60) 。但这不准确:元素最好放在(12.7 * 4,15.3 * 4)或(51,61)。最糟糕的是,在某些情况下,错误会累积起来。

我认为这是缩放一些简单文本时这个非常明显的错误的根本原因:

Rendered using EM_FORMATRANGE<code>and scaled with</code>SetMapMode

上面的示例是8点Segoe UI,在96 DPI显示设备上下文中使用EM_FORMATRANGESetMapMode缩放到400%。文本现在变成32磅大小,但每个角色之间的空间太高,看起来不自然。

Zoomed to 400% in WordPad

上述示例是在写字板中创建的,输入文本为8点Segoe UI,然后使用缩放控件设置为400%缩放级别。每个角色之间的空间看起来很正常。使用32磅字体和100%缩放级别可以获得完全相同的结果。

要解决此问题,我尝试了以下方法。对于尝试的每件事,当缩放到400%时,结果同样不令人满意。

  • 使用SetWorldTransform设置缩放变换集,而不是使用SetMapModeSetWindowExtEx等进行缩放。
  • 将元文件的设备上下文传递给EM_FORMATRANGE,然后稍后缩放元文件。
  • 使用SetMapMode与渲染到图元文件一起缩放,然后在不缩放的情况下显示图元文件。

我相信结果总是不能令人满意,因为问题归结为这样一个事实:富编辑控件四舍五入到最接近的整数并呈现它认为的96 DPI设备 - 忽略变换到位。我查看了元文件格式,我发现个别字符位置实际上是以像素级别的分辨率存储在图元文件中 - 这就是为什么缩放图元文件显然不起作用,因为在那一点已经发生了舍入。

我可以想到两个可以解决这个问题的真正解决方案:

  • 使用具有更高用户指定点数/英寸的设备上下文,以便GetDeviceCaps返回不同的值。 (注意:一些示例建议使用打印机设备,因为它们通常具有更高的DPI,但我希望我的代码可以在没有打印机并能够渲染到屏幕外缓冲区的系统上工作。)
  • 告诉富编辑控件假设设备上下文的每英寸点数与GetDeviceCaps报告的点数不同。

其他任何事情似乎都会受到这些舍入错误的影响。

有没有人(1)知道如何实现我提出的任何一种解决方案,或者(2)对如何实现将精确的高DPI输出放入缓冲区的目标有另一种想法? / p>

4 个答案:

答案 0 :(得分:0)

我遇到了同样的问题。

快速解决方案是将文本绘制到100%比例位图中,然后缩放位图。 它不是最好的解决方案,但它可能适合你。

您找到了更好的解决方案吗?如果是的话,请在这里分享。

另请注意,当您将文本绘制到100%元文件然后将元文件缩放到屏幕时也会出现此问题 - 我相信这与GDI文本绘制功能有关与缩放一起工作。

Roey

答案 1 :(得分:0)

您可以将控件中所有文本的磅值乘以因子4,并将控件渲染为比4倍大的位图。

如果您自己填充控件,这将非常简单。如果您支持用户输入的任意内容,则需要做更多的工作,并且需要额外的努力来处理非文本的任何内容(例如嵌入式位图)。

答案 2 :(得分:0)

我只花了两周时间来解决类似的问题。我需要一个可以扩展WYSISWG编辑的Rich Edit。由于我们发现Windows丰富的编辑控件不支持使用EM_FORMATRANGE正确缩放,并且字符间间距在缩放级别和字体大小之间不会改变,因此只能按离散字体大小步长进行缩放。

由于我不需要在规模上存在很大差异,我解决的解决方案是使用ITextServices的无窗口文本编辑界面以固定分辨率渲染到内部位图。然后我使用GDI +通过三线性过滤将内部位图重新采样到所需的屏幕尺寸。只要比例差异不是太大,结果就可以很好地模拟可扩展的丰富编辑,足以满足我的需求。

在尝试了许多不同的选项之后,我确信你无法使用富含Windows的编辑控件进行精确缩放。您可以编写自己的控件来呈现文本。但是,您需要为具有不同样式的每个文本单独进行绘制调用。此外,您需要处理所有丰富的编辑句柄,如突出显示文本,放置光标,处理鼠标和键盘输入,解析rtf文本等等。在这种情况下,最好只购买第三方组件(我找不到任何合适的免费开源组件)。如果有人想要尝试它,我会指出不同API的文本呈现的相关起点。

  • GDI - TextOut未正确设置字符间距。你需要GetCharacterPlacement和ExTextOut。您还需要自己计算缩放比例。您可能不想使用GDI
  • GDI + - DrawString正确处理缩放。 GDI +是一个合理的选择
  • DirectWrite - 如果您愿意将自己限制在Vista Platform Update或更高版本,DirectWrite是Microsoft提供的最新文本API。

此处的链接描述了GDI和GDI +之间文本呈现的不同之处:

http://windowsclient.net/articles/gdiptext.aspx

答案 3 :(得分:0)

尝试使用EM_SETZOOM message让富编辑控件缩放输出本身。