Nic*_*cki 12 gdi rounding-error metafile
我有一个生成元文件(EMF)的应用程序.它使用参考设备(也就是屏幕)来渲染这些元文件,因此元文件的DPI会根据运行代码的机器而改变.
假设我的代码打算创建一个8.5英寸x 11英寸的图元文件.使用我的开发工作站作为参考,我最终获得了一个EMF
好的,所以rclFrame告诉我EMF的大小应该是
对吧.使用这些信息,我们也可以确定我的显示器的物理DPI,如果我的数学是正确的:
凡是播放此图元文件 - 一个EMF到PDF转换,在"摘要"页上的Windows资源管理器等的EMF时,单击鼠标右键 - 似乎截断计算的DPI值,显示87的,而不是87.9231(甚至88会很好).
当播放元文件时,这会导致页面的物理大小为8.48 x 10.98 in(使用87 dpi)而不是8.5 in x 11 in(使用88 dpi).
感谢您的任何见解.
Nic*_*cki 16
我现在学到的东西比我关心的metafiles还要多.
Metafile类的构造函数重载效果不佳,并且会在截断的DPI值上运行.考虑以下:
protected Graphics GetNextPage(SizeF pageSize)
{
IntPtr deviceContextHandle;
Graphics offScreenBufferGraphics;
Graphics metafileGraphics;
MetafileHeader metafileHeader;
this.currentStream = new MemoryStream();
using (offScreenBufferGraphics = Graphics.FromHwnd(IntPtr.Zero))
{
deviceContextHandle = offScreenBufferGraphics.GetHdc();
this.currentMetafile = new Metafile(
this.currentStream,
deviceContextHandle,
new RectangleF(0, 0, pageSize.Width, pageSize.Height),
MetafileFrameUnit.Inch,
EmfType.EmfOnly);
metafileGraphics = Graphics.FromImage(this.currentMetafile);
offScreenBufferGraphics.ReleaseHdc();
}
return metafileGraphics;
}
Run Code Online (Sandbox Code Playgroud)
如果您在通过SizeF的{8.5,11},你可能希望得到一个Metafile有一个rclFrame的{21590,27940}.毕竟,将英寸转换为毫米并不难.但你可能不会.根据您的分辨率,GDI +似乎会在转换inches参数时使用截断的DPI值.为了做到这一点,我必须自己完成百分之一毫米,这是GDI +刚刚通过的,因为它是如何原生存储在元文件头中的:
this.currentMetafile = new Metafile(
this.currentStream,
deviceContextHandle,
new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
MetafileFrameUnit.GdiCompatible,
EmfType.EmfOnly);
Run Code Online (Sandbox Code Playgroud)
舍入错误#1已解决 - rclFrame我的元文件现在是正确的.
Graphics记录到a 的实例上的DPI Metafile总是错误的.metafileGraphics通过调用Graphics.FromImage()元文件来查看我设置的变量?好吧,似乎该Graphics实例的DPI总是为96 dpi.(如果我不得不猜测,它总是设置为逻辑 DPI,而不是物理 DPI .)
您可以想象,当您Graphics在96 dpi下运行的实例上绘制并记录到Metafile其标题中"已记录"87.9231 dpi 的实例时,会出现欢闹.(我说"记录"因为它是根据其他值计算的.)元文件的"像素"(记住,存储在图元文件中的GDI命令以像素为单位指定)更大,所以你诅咒并嘀咕为什么你要画一些东西一英寸长,最终只有一英寸长.
解决方案是缩小Graphics实例:
metafileGraphics = Graphics.FromImage(this.currentMetafile);
metafileHeader = this.currentMetafile.GetMetafileHeader();
metafileGraphics.ScaleTransform(
metafileHeader.DpiX / metafileGraphics.DpiX,
metafileHeader.DpiY / metafileGraphics.DpiY);
Run Code Online (Sandbox Code Playgroud)
不是那个叫声吗?但似乎有效.
"舍入"错误#2解决了 - 当我说在88 dpi时画出"1英寸"的东西时,那个像素最好是$%$ ^!记录为像素#88.
szlMillimeters可以变化很大; 远程桌面带来很多乐趣.因此,我们发现(根据Mark的回答),有时,Windows会查询显示器的EDID,并且实际上知道它的物理尺寸.GDI + HORZSIZE在填写szlMillimeters属性时有用地使用它(等).
现在想象一下,你回家调试这个远程桌面代码.假设您的家用电脑配备了16:9宽屏显示器.
显然,Windows无法查询远程显示器的EDID.因此它使用了320 x 240 mm的古老默认值,这很好,除了它恰好是4:3的宽高比,现在完全相同的代码在显示器上生成一个元文件,该文件应该是非方形物理像素:水平DPI和垂直DPI是不同的,我不记得上次我看到的那种情况发生了.
我现在的解决方法是:"好吧,不要在远程桌面下运行它."
rclFrame标题时出现了舍入错误.这是导致这个问题的问题的主要原因.我的图元文件一直是"正确的"(好吧,在修复前两个问题之后正确),并且所有这些创建"高分辨率"元文件的搜索都是一个红色的鲱鱼.确实,在低分辨率显示设备上记录图元文件时会丢失一些保真度.这是因为元文件中指定的GDI命令以像素为单位指定.它是矢量格式并且可以向上或向下缩放并不重要,当GDI +决定将操作捕捉到哪个"像素"时,某些信息在实际记录期间丢失.
我联系了供应商,他们给了我一个更正的版本.
舍入错误#3已解决.
恰好这个截断的值表示EMF-to-PDF工具在内部使用的相同错误值.除此之外,这个怪癖对讨论没有任何意义.
由于我的问题是关于在设备环境中使用DPI进行预测,Mark's是一个很好的答案.
我很好奇 Windows 如何知道显示器的物理尺寸。您一定在某处更改了配置?也许您可以将其更改为更方便的值,可以很好地划分。
正如名称所暗示的,“设备上下文”必须连接到系统设备。但是,这不一定是硬件驱动程序,它可以是设备模拟器,例如 PDF 编写器打印驱动程序。我见过至少有一个可以让您设置任意 DPI。
| 归档时间: |
|
| 查看次数: |
7141 次 |
| 最近记录: |