wpf 在不同DPI下如何在DrawingVisual中画出清晰的图形

环境Win10 VS2017 .Net Framework4.7.1

本文仅讨论在DrawingVisual中进行的画图.

  • WPF单位,系统DPI,显示器DPI三者的定义及关系
    • WPF单位:一种与设备无关的单位,以1/96逻辑英寸为一个单位,也就是说如果将一个对象的长度设为96,那么在任何设备上WPF都会试图将其显示为1逻辑英寸长.
  • 系统DPI:将多少个显示器的像素点定义为1逻辑英寸,默认是96个点

在win10中,图中所设置的

100%即为96DPI;

125%即为120DPI;

150%即为144DPI;

175%即为168DPI.

  • 显示器DPI:每实际英寸有多少个像素点. 大多数显示器每实际英寸可显示96个点,不过近年开始流行的高分辨率屏可能会高出很多.

WPF单位决定可视元素的尺寸是多少逻辑英寸,但逻辑每英寸是多少则由系统DPI决定.(此处的"逻辑英寸"已不是生活中的那个绝对不变的单位了,而是由系统DPI决定,只不过默认是与实际英寸一致的)

在默认情况下:

WPF的一个单位=1/96逻辑英寸=1/96实际英寸 =显示器的1个像素.此时完美点对点,所以不会模糊.

但如果在系统中更改了DPI设置,比如144(150%)DPI,此时

WPF的一个单位=1/96逻辑英寸=1.5/96实际英寸=显示器的1.5个像素.此时点对点被破坏,所以模糊.

  • DrawingVisual如何才能做到与显示器点对点?
    • 所有的绘图参数都需要根据系统DPI进行调整,比如在96系统DPI环境下的宽度为1的Pen对象,想要在144系统DPI下继续精确保持1个像素的宽度,那么就要将其缩小为1/(144/96)≈ 0.66666
    • 当WPF对DrawingVisual进行渲染时,渲染DPI设置要与系统DPI一致.如果不一致则WPF会先以渲染设置的DPI进行渲染 ,再缩放到系统DPI,一旦缩放就破坏了点对点,进而导致模糊.比如下面通过RenderTargetBitmap对象来使用DrawingVisual,其中的144就是要与系统DPI一致.
Dim RenderTargetBitmap As New RenderTargetBitmap(1920, 1080, 144, 144,PixelFormats.Pbgra32)
RenderTargetBitmap.Render(drawingVisual)
  • 例子

现在假设系统DPI为默认100%设置,在DrawingVisual画一条一个像素宽的清晰横线

Public Class Window1

    Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
        Dim drawingVisual As New DrawingVisual()
        Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
        Dim Thickness As Double = 1 ‘线宽1个单位,此处等同于1个像素
        Dim HalfThickness As Double = Thickness * 0.5
        drawingContext.DrawLine(New Pen(Brushes.Black, Thickness),
                                New Point(0, 100 - HalfThickness),
                                New Point(1920, 100 - HalfThickness)
        )
        drawingContext.Close()

        Dim RenderTargetBitmap As New RenderTargetBitmap(1920, 1080, 96, 96, PixelFormats.Pbgra32) ‘注意这里创建的是96DPI的图像对象
        RenderTargetBitmap.Render(drawingVisual)

        Dim image As New Image
        image.Source = RenderTargetBitmap

        Canvas1.Children.Add(image)
    End Sub

End Class
<Window
    x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp3"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="Window1"
    Width="300"
    Height="300"
    mc:Ignorable="d" Loaded="Window_Loaded">
    <Canvas x:Name="Canvas1">
    </Canvas>
</Window>

运行结果:线是清晰锐利的

接下来把系统DPI改为150%,仍然是要求在相同位置画出一条一个像素宽的清晰横线.

Public Class Window1

    Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
        Dim drawingVisual As New DrawingVisual()
        Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
        Dim Thickness As Double = 1 / 1.5 ‘在144(150%)DPI的情况下,线宽1个像素
        Dim HalfThickness As Double = Thickness * 0.5
        drawingContext.DrawLine(New Pen(Brushes.Black, Thickness),
                                New Point(0, 100 / 1.5 - HalfThickness),
                                New Point(1920, 100 / 1.5 - HalfThickness)
        )
        drawingContext.Close()

        Dim RenderTargetBitmap As New RenderTargetBitmap(1920, 1080, 144, 144, PixelFormats.Pbgra32) ‘注意这里创建的是144DPI的图像对象
        RenderTargetBitmap.Render(drawingVisual)

        Dim image As New Image
        image.Source = RenderTargetBitmap

        Canvas1.Children.Add(image)
    End Sub

End Class

运行结果:线依然是是清晰锐利的

  • 其它

在写此笔记之前在网上查阅过很多文章,基本上都是以使用参考线(GuidelineSet)进行像素对齐的方式避免模糊,但参考线也不是完美的解决方案,因为当要求在两个显示器像素之间进行绘制时WPF到底会将其对齐到哪个像素呢?其结果并不一定是我们想要的,

以MSDN的此文为例https://msdn.microsoft.com/zh-cn/library/aa348553(v=vs.110).aspx

左侧是使用了参考线得到的清晰图形,右侧是没有使用参考线得到的模糊图形(注意原文中写反了).

请仔细放大观察左侧的清晰方框,可以发现四边的每条线段宽度并不相同.

使用参考线进行像素对齐的方法实际上就是对绘制坐标参数进行类似四舍五入的舍入,这个方框的四边虽然是使用同样参数绘制的线条,但在具体绘制时有的舍了有的入了,导致最终宽度不一样.

原文地址:https://www.cnblogs.com/8u7tgyjire7890/p/9502991.html

时间: 2024-09-29 11:12:10

wpf 在不同DPI下如何在DrawingVisual中画出清晰的图形的相关文章

Windows平台下如何在C#中调用Python

最近迷上了Python,发现它能够做很多C#无法完成的事情,比如,调用CMD或者在CMD中执行一个exe文件命令行并获得输出的结果.过程简单,处理起来也非常方便,但如果要用C#调用Python文件呢,没关系,你想到的肯定早就有也人想到过.网上Google一下,超级多.索性拿来实践吧. 首先要用到的就是这个软件:IronPython,官方下载地址:http://ironpython.codeplex.com 安装在Windows下之后去它的安装地址查找下面这两个文件: IronPython.dll

[译] 如何在React中写出更优秀的代码

目录 我们先来看 Linting 利用组件模块性.复用性和组合性 propTypes 和 defaultProps 知道何时创建新组件 组件 vs 纯组件 vs 无状态函数组件 无状态函数组件 纯组件 使用 React 开发工具 使用内联条件语句 尽可能使用代码片段库 React 本质 - 学习 React 是如何工作的 快速回顾 在React中写出更好代码的9条建议:学习关于 Linting, propTypes, PureComponent 等. Rajat S · 2018 年 4 月 1

linux系统下如何在vscode中调试C++代码

本篇博客以一个简单的hello world程序,介绍在vscode中调试C++代码的配置过程. 1. 安装编译器 vscode是一个轻量的代码编辑器,并不具备代码编译功能,代码编译需要交给编译器完成.linux下最常用的编译器是gcc,通过如下命令安装: sudo apt-get install build-essential 安装成功之后,在终端中执行gcc --version或者g++ --version,可以看到编译器的版本信息,说明安装成功. 2. 安装必要的插件 在vscode中编写C

如何在PS中画圆

有两种: 用直线工具里的椭圆工具. 这是用路径来画.按住Shift键,拉出正圆形. 点击"路径",点下面的"用前景色描绘路径". 删除工作路径. 这种画法的圆型线宽度只有1像素.如果要加宽,要用"描边"来做.适合细线圆形图像.   另一种是用"选框工具"里的"椭圆选框工具"来做.先拉出水平和垂直两条参考线.点"视图",勾选"对齐". 用椭圆选框工具,按住Shift键,

如何在Visio中画矩阵

原创文章,欢迎转载,但是要写明出处哦! http://blog.csdn.net/chang_yuan_2011/article/details/45340685 今晚在画图时想要用Visio画一个矩阵,一开始不知道怎么画,在网上找了很久也没有找到教程,最后自己研究出来了,做一下笔记,希望对其他同学有帮助! 在Visio画一个矩阵的具体步骤如下: 1.现在visio中插入word对象 2.在插入word对象中插入公式 3.在公式中选择插入矩阵 大功告成!

如何在github中写出自己的readme文件

一.问题 最近想做一个论文阅读的github库,想到写一个readme文件,但怎么能写得和一些官方readme文件一样呢,在网上找了很多教程,世界上都是千篇一律,连改都不改,直接复制,真是太垃圾了. 二.解决 在找的过程中,一个人的回答真的是很不错,直接把写得好 的文件扒下来,理解一下,照着写就是了.例如问答中推荐的readme文件,把它copy下来,在typora中的源码模式中paste完了之后关闭源码模式,就可以显示.然后找到你想要的,直接比对着做就好了. 原文地址:https://www.

如何在.Net中使用Redis

Redis是一个key-value存储系统.和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表).sets(集合)和zsets(有序集合)几种数据类型.这些数据类型都支持push/pop.add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的. Redis的代码遵循ANSI-C编写,可以在所有POSIX系统(如Linux, *BSD, Mac OS X, Solaris等)上安装运

如何在MyEclipse中通过hibernate使用jtds驱动连接数据库,并向数据库添加数据的方法

最近学习了下如何在MyEclipse中通过hibernate使用jtds驱动连接数据库,并向数据库添加数据的方法,虽然MyEclipse中自带了连接数据库的方法,我也尝试了下其他方法,如有不当之处请指教,具体如下: 需要的驱动包为:jtds-1.2.jar 首先创建Java项目,就叫testjtds,同时为工程添加hibernate支持 对testjtds鼠标右键,在最下面找到MyEclipse-->Add Hibernate Capabilities...之后 出现如图窗口 当然了,直接nex

如何在Word中排出漂亮的代码

引言 学数学和计算机,当然还是用LaTeX排版技术文章更方便.但有时候还是迫不得已需要用Word写作,另外Word其实也有Word的好处,比如细节上的修改要比LaTeX方便. 从Matlab高亮代码复制到Word,中文会乱码开始,我就很想研究下如何在Word中展示漂亮的代码.今年寒假利用Vim,有些突破,10月3日的时候又有了比较大的进展,自己设计了一款Vim的代码高亮配色方案,然后利用Vim的:TOhtml命令生成html文件,再用浏览器打开html文件,复制里面的代码到Word,就能保留原始