环境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%(修改后别忘了注销windows的当前用户后重新登入以使DPI的修改生效),仍然是要求在相同位置画出一条一个像素宽的清晰横线.

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到底会将其对齐到哪个像素呢?其结果并不一定是我们想要的,
左侧是使用了参考线得到的清晰图形,右侧是没有使用参考线得到的模糊图形(注意原文中写反了).

请仔细放大观察左侧的清晰方框,可以发现四边的每条线段宽度并不相同.
使用参考线进行像素对齐的方法实际上就是对绘制坐标参数进行类似四舍五入的舍入,这个方框的四边虽然是使用同样参数绘制的线条,但在具体绘制时有的舍了有的入了,导致最终宽度不一样.

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

  1. 如何在canvas中画出一个太极图

    先放一个效果图: 代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /&g ...

  2. 如何在 Linux 中找出最近或今天被修改的文件

    1. 使用 ls 命令,只列出你的 home 文件夹中今天的文件. ls -al --time-style=+%D | grep `date +%D` 其中: -a- 列出所有文件,包括隐藏文件 -l ...

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

    最近迷上了Python,发现它能够做很多C#无法完成的事情,比如,调用CMD或者在CMD中执行一个exe文件命令行并获得输出的结果.过程简单,处理起来也非常方便,但如果要用C#调用Python文件呢, ...

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

    本篇博客以一个简单的hello world程序,介绍在vscode中调试C++代码的配置过程. 1. 安装编译器 vscode是一个轻量的代码编辑器,并不具备代码编译功能,代码编译需要交给编译器完成. ...

  5. 如何在 Linux 中找出 CPU 占用高的进程

    1) 怎样使用 top 命令找出 Linux 中 CPU 占用高的进程 在所有监控 Linux 系统性能的工具中,Linux 的 top 命令是最好的也是最知名的一个.top 命令提供了 Linux ...

  6. 如何在shell中打印出带颜色的字符?

    先看如下的效果: 方法: 先看如下的脚本sh3.sh: #!/bin/bash echo "peng" echo "$(color bold yellow) ------ ...

  7. 如何在word中写出赏心悦目的代码

    短学期的VHDL终于结束了,虽然代码并不是很难,但是框框条条的规矩很多,也算折腾了一会,最后要写一个技术手册,结题报告类似物.考虑到word毕竟套主题比较方便,所以也就没有用LaTeX写,但是很快就发 ...

  8. 如何在EXCEL中找出第一列中不包含的第二列数据

    1.找出第一列中不包含的第二列数据:=IFERROR(VLOOKUP(A:A,B:B,1,0),"无") 2.A列相同,B列相加:=SUMIF(G:G,G1,J:J)

  9. 如何在Unity中画抛物线

    using UnityEngine; using System.Collections; using System.Collections.Generic; [ExecuteInEditMode] p ...

随机推荐

  1. HBuilder云端打包+个推

    1.个推上登记应用. 应用名称和应用标识,在HBuilder的云端打包配置中获取. 应用证书:必需要有苹果开发者账号,并且加入了"iOS Developer Program".加入 ...

  2. Java程序执行过程及内存机制

    本讲将介绍Java代码是如何一步步运行起来的,其中涉及的编译器,类加载器,字节码校验器,解释器和JIT编译器在整个过程中是发挥着怎样的作用.此外还会介绍Java程序所占用的内存是被如何管理的:堆.栈和 ...

  3. 未雨绸缪,数据保护之NBU介质备份

    摘要:华为GaussDB目前已支持NBU介质的备份与恢复,本文主要描述了华为GaussDB数据库和NetBackup对接进行备份.恢复的配置方法与性能调优方法. 一.简介 企业要想避开自然灾难和站点中 ...

  4. Shell----监控CPU/内存/负载高时的进程

    Shell----监控CPU/内存/负载高时的进程 1.编写脚本 vim cpu-warning.sh #!/bin/bash #监控系统cpu的情况脚本程序 #取当前空闲cpu百份比值(只取整数部分 ...

  5. 史上最全Xshell and Xftp 工具的使用

    文章目录 什么是xshell 解决: 安装Xshell Xshell怎么建立连接 Xshell如果修改已有连接信息? 修改,背景色,字体,编码 Xshell导出已有的登录信息 Xftp的使用 XFP建 ...

  6. 恶补了 Python 装饰器的六种写法,你随便问~

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 今天给大家分享一下关于装饰器的知识点,内容非常干,全程高能,认真吸收看完,一定会对装饰器有更深的理解 ...

  7. Python之复制列表

    将一个列表的数据复制到另外一个列表中. 1 a = [1,2,3] #定义列表a 2 3 b = a[:] #将列表a的切片赋值给b,也可以理解为将b的值设置为a[:] 4 5 print(a) #打 ...

  8. windows服务器安装exe文件出错

    今天在安装exe文件的时候出现了错误,记录如下: 服务器环境为windows 2016数据中心标准英文版.要安装的软件为***.exe.按照在本地电脑上安装的方法,直接双击打开,然后错误出现如下图: ...

  9. 漫画 | 老泪纵横,约会也不敢耽误改bug

    啥都不说了, 直接看图, 就说你中了几枪. 小莱已经抹了好几把辛酸泪-- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 关于作者 作者:大家好,我是莱乌,BAT搬砖工一枚.从 ...

  10. 2021韩顺平图解Linux课程(全面升级)基础篇

    第1章 Linux 开山篇-内容介绍 本套 Linux 课程内容 Linux 主要应用领域:服务器 第2章 Linux 基础篇-Linux 入门 Linux 之父 Linus Torvalds Git ...