WPF 繪制對齊像素的清晰顯示的線條

WPF 繪制對齊像素的清晰顯示的線條
原文:WPF 繪制對齊像素的清晰顯示的線條

版權聲明:本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名呂毅(包含鏈接:http://blog.csdn.net/wpwalter/),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系(walter.lv@qq.com)。 https://blog.csdn.net/WPwalter/article/details/78858762

此前有小伙伴詢問我為何他 1 像素的線條顯示發虛,然后我告訴他是“像素對齊”的問題,然而他設置了各種對齊像素的屬性依舊沒有作用。于是我對此進行了一系列試驗,對 WPF 像素對齊的各種方法進行了一次總結。此后在 StackOverflow 中,我回答了 graphics - WPF DrawingContext seems ignore SnapToDevicePixels - Stack Overflow 問題。

閱讀本文,我們將了解解決 WPF 像素對齊的四種方法以及其各自的適用范圍和副作用。


像素對齊

為什么要做像素對齊

我們在解決什么問題

看線條!這是 3 像素的線條:

線條發虛

然而論其原因,就是因為我們屏幕太渣~哦~不,是因為繪制的線條沒有與屏幕像素對齊,具體來說是視覺對象(Visual)的位置不在整數像素上或尺寸不是整數像素。而與此同時屏幕的點距又太大以至于我們看出來繪制的線條和屏幕像素之間的差異。

然而為什么 WPF 不默認為我們對齊像素呢?這是因為要對齊像素必定帶來尺寸上的偏差;這是繪制尺寸精度和最終呈現效果之間的平衡。在 MacBook、Surface Pro 這些高檔顯示屏上,根本不用管這樣的平衡問題;但在渣渣顯示器上,微軟把這種平衡的控制交給了應用的開發者。

處理像素對齊的四種方法

方法一:布局取整 UseLayoutRounding

UseLayoutRounding

實際效果是:

UseLayoutRounding 的效果

逗我

根本就不起作用

事實上我們從 .NET Framework 源碼可以得知,UseLayoutRounding 實際只處理 UI 元素對自己子級控件的布局取整。一旦整棵布局樹種有任何一個不是整數(或者 DPI 相乘后不是整數),那么就依然沒有解決問題。

方法二:對齊設備像素 SnapsToDevicePixels

這是一個會沿著邏輯樹繼承的屬性,只要最頂層設置了這個屬性,里面的元素都會具備此特性。不過,他只處理矩形的渲染,也就是說,只對 Border Rectangle 這些類型的元素生效,其他的包括自己寫的元素基本都是不管用的。

它有一個好處,是像素對齊的情況下同時能夠保證顯示不足或超過 1 像素時,也能帶一點兒透明或者超過一點像素。

方法三:使用 DrwingContext 繪制并配合 GuidelineSet

如果自己處理繪制,則可以在 OnRender 方法中使用 DrawingContext 來繪制各種各樣的形狀。DrawingContext 有方法 PushGuidelineSet,而 PushGuidelineSet 就是用來處理對齊的。

以下是四種不同方式的對齊效果對比,其中上面一半是直接對齊(即繪制過程是緊貼著的),下面一半則是多個部分帶上一點偏移(即并不是緊貼):

四種方式對比
▲ 看不清的可以考慮方法看

于是要想像素對齊,必須:

  • 布局或繪制時,UI 元素之間一點偏移或空隙都不能有,一點都不行
  • SnapsToDevicePixelsGuidelineSet 在實際對齊中有效,而 UseLayoutRounding 就是在逗你

GuidelineSet 的使用可以參考我在 StackOverflow 上的回答:graphics - WPF DrawingContext seems ignore SnapToDevicePixels - Stack Overflow。

以下是我編寫的用于輔助繪制對齊線條的擴展方法:

public static class SnapDrawingExtensions
{public static void DrawSnappedLinesBetweenPoints(this DrawingContext dc,Pen pen, double lineThickness, params Point[] points){var guidelineSet = new GuidelineSet();foreach (var point in points){guidelineSet.GuidelinesX.Add(point.X);guidelineSet.GuidelinesY.Add(point.Y);}var half = lineThickness / 2;points = points.Select(p => new Point(p.X + half, p.Y + half)).ToArray();dc.PushGuidelineSet(guidelineSet);for (var i = 0; i < points.Length - 1; i = i + 2){dc.DrawLine(pen, points[i], points[i + 1]);}dc.Pop();}
}

注意添加到 GuidelineSet 的尺寸不需要是整數,也不需要計算對齊屏幕的位置,只需要隨便指定一個值即可,但相鄰的繪制元素的值需要在 double 級別完全相同,多一點少一點都不行

OnRender 中調用它繪制:

protected override void OnRender(DrawingContext dc)
{// Draw four horizontal lines and one vertical line.// Notice that even the point X or Y is not an integer, the line is still snapped to device.dc.DrawSnappedLinesBetweenPoints(_pen, LineThickness,new Point(0, 0), new Point(320, 0),new Point(0, 40), new Point(320, 40),new Point(0, 80.5), new Point(320, 80.5),new Point(0, 119.7777), new Point(320, 119.7777),new Point(0, 0), new Point(0, 120));
}

繪制的四條線

方法四:RenderOptions.EdgeMode

這是純渲染級別的附加屬性,對所有 UI 元素有效。這個屬性很神奇,一旦設置,元素就再也不會出現模糊的邊緣了,一定是硬像素邊緣。不足半像素的全部刪掉,超過半像素的變為 1 個像素。

以為它可以解決問題?——Too young, too simple.

你希望能夠繪制 1 像素的線條,實際上它會讓你有時看得見 1 像素線條,有時看的是 2 像素線條,有時居然完全看不見!!!

如果你都作用對象上還有其它視覺對象,它們也會一并變成了“硬邊緣”,是可以看得見一個個像素的邊緣。

硬邊緣

各種方法適用范圍總結

適用范圍總結

  1. 如果畫粗線條粗邊框,那么 RenderOptions.EdgeMode 最適合了,因為設置起來最方便,可以設置到所有的 UI 元素上。由于邊框很粗,所以多一個少一個像素用戶也注意不到。
  2. 如果是畫細邊框,那么使用 Border 配合 SnapsToDevicePixels 可以解決,無論是 0.8 像素還是 1.0 像素,1.2 像素,都能在準確地顯示其粗細的基礎之上還保證像素對齊。
  3. 如果圖形比較復雜,比如繪制表格或者其它各種交叉了線條的圖形,那么使用 DrawingContext 繪制,并設置 GuidelineSet 對齊。
  4. 如果窗口非常簡單,既沒有縮放,UI 元素也不多,可以考慮使用 UseLayoutRounding 碰碰運氣,萬一界面簡單到只需要整數對齊就夠了呢?
  5. 特別說明,上面四種方法不足與應對所有的像素對齊情況,如果還是沒辦法對齊……節哀把……我們一起找偏方……
posted on 2018-09-21 22:03 NET未來之路 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/lonelyxmas/p/9688697.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279298.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279298.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279298.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

中輸入learn_Scikit-learn新版本發布,一行代碼秒升級

十三 發自 凹非寺 量子位 報道 | 公眾號 QbitAIScikit-learn&#xff0c;這個強大的Python包&#xff0c;一直深受機器學習玩家青睞。而近日&#xff0c;scikit-learn 官方發布了 0.22 最終版本。此次的更新修復了許多舊版本的bug&#xff0c;同時發布了一些新功能。安裝最新版…

禁用刪除鍵退回歷史記錄_如何在Windows 8中刪除或禁用搜索超級按鈕歷史記錄

禁用刪除鍵退回歷史記錄When you use the Search Charm in Windows 8 it remembers everything you search for, which is very useful, but if you share your PC with someone you may want to delete your history or even disable it. Here’s how to do it. 在Windows 8中…

Java8基礎之super關鍵字

相信學過Java語言的小伙伴都熟悉super這個關鍵字&#xff0c;接下來&#xff0c;我們來研究他的一些基礎用法吧。 定義名字為Father的類 package superkeyworld;public class Father {public String name;public int age;public Father() {}public Father(String name, int age…

canpro腳本_AE/PR腳本-創建編輯導入導出專業字幕腳本 Subtitle Pro 2.8.0 + 使用教程...

Subtitle Pro是一個專業的插件&#xff0c;可讓您直接在After Effects和Premiere Pro中為視頻創建字幕。可將字幕快速的導入或導出。您可以導入.srt文件或任何字幕格式&#xff0c;也可以編寫文本。一鍵翻譯單詞&#xff0c;一鍵同步時間。不僅是簡單的字幕文字&#xff0c;還可…

【概率論】1-2:計數方法(Counting Methods)

title: 【概率論】1-2:計數方法(Counting Methods) categories: MathematicProbability keywords:Counting Methods技術方法Combinatorial Methods組合方法Multiplication乘法法則Permutations排列Stirling’s Formula斯特林公式 toc: true date: 2018-01-25 10:35:46Abstract:…

Python字符編碼詳解

Python字符編碼詳解 http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html本文簡單介紹了各種常用的字符編碼的特點&#xff0c;并介紹了在python2.x中如何與編碼問題作戰 &#xff1a;&#xff09; 請注意本文關于Python的內容僅適用于2.x&#xff0c;3.x中str和…

使用sql服務器發送賀卡_創建和發送免費電子賀卡的最佳網站

使用sql服務器發送賀卡With the holiday season upon us, it’s time to pull out the holiday card list and get writing. However, how would you like to save some money this year and also help save the environment? 隨著假期的到來&#xff0c;是時候抽出節日賀卡清…

職稱申報評審管理系統_《四川省職稱評審管理暫行辦法》出臺

我省將探索實行職稱評審電子證書&#xff0c;電子證書與紙質證書具有同等效力。12月29日&#xff0c;記者從省人社廳了解到&#xff0c;我省近日出臺《四川省職稱評審管理暫行辦法》&#xff0c;從職稱評審總體要求、評審主體、申報程序、組織實施、優化服務、強化監管等方面提…

WordCount--統計輸入文件的字符數、行數、單詞數(java)--初級功能

碼云地址&#xff1a; https://gitee.com/YuRenDaZ/WordCount 個人PSP表格&#xff1a; PSP2.1 PSP階段 預估耗時 &#xff08;分鐘&#xff09; 實際耗時 &#xff08;分鐘&#xff09; Planning 計劃 180 120 Estimate 估計這個任務需要多少時間 180 120 D…

網頁的驗證碼

1.首先可以寫一個產生隨機驗證碼的aspx文件&#xff0c;如下產生四位數字&#xff1a; private void Page_Load(object sender, System.EventArgs e) { this.CreateCheckCodeImage(GenerateCheckCode()); } private string GenerateCheckCode() { …

榮耀9igoogle模式_iGoogle個性化主頁的6種替代方法

榮耀9igoogle模式iGoogle has less than a year to go before it’s shut down for good on November 1, 2013. While Google seems to think that iGoogle isn’t necessary anymore, there are other services waiting to take its place. iGoogle距離其2013年11月1日永久關閉…

華為堡壘機_安恒信息成為“華為云優秀嚴選合作伙伴”,攜手保障“云上”資產安全訪問...

加快5G持續創新能力&#xff0c;為云計算行業注入新動能。近日&#xff0c;以“智者?同行?共贏”為主題的2020華為云ISV(嚴選)合作伙伴大會在杭州隆重舉行。上百位華為云合作伙伴、行業大咖等專業人士齊聚一堂&#xff0c;探討云計算產業熱門話題。作為華為云重要的生態合作伙…

zip4j實現多線程壓縮

使用的jar包&#xff1a;zip4j_1.3.2.jar 基本功能&#xff1a; 針對ZIP壓縮文件創建、添加、分卷、更新和移除文件 (讀寫有密碼保護的Zip文件) (支持AES 128/256算法加密) (支持標準Zip算法加密) (支持zip64格式) (支持Store(僅打包&#xff0c;默認不壓縮&#xff0c;…

非三星手機無法登錄三星賬號_如何解決所有三星手機的煩惱

非三星手機無法登錄三星賬號Samsung is the biggest manufacturer of Android phones in the world, but that doesn’t mean these handsets are perfect out of the box. In fact, most of these phones have several annoyances initially—here’s how to fix many of thes…

設置單元格填充方式_單元格的選擇及設置單元格格式

數據輸入完畢&#xff0c;接下來可以設置字體、對齊方式、添加邊框和底紋等方式設置單元格格式&#xff0c;從而美化工作表。要對單元格進行設置&#xff0c;首先要選中單元格。選擇單元格選擇單元格是指在工作表中確定活動單元格以便在單元格中進行輸入、修改、設置和刪除等操…

Recover Binary Search Tree

Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing its structure. Note: A solution using O(n) space is pretty straight forward. Could you devise a constant space solution? 要求找到BST中放錯位置的兩個節點. …

springboot三種過濾功能的使用與比較

若要實現對請求的過濾&#xff0c;有三種方式可供選擇&#xff1a;filter、interceptort和aop。本文主要討論三種攔截器的使用場景與使用方式。 下文中的舉例功能是計算每個請求的從開始到結束的時間&#xff0c;例子來源是慕課網。 一、filter 特點&#xff1a;可以獲取原始的…

后綴的形容詞_構詞法(18)構成形容詞的常見后綴 3

即時練習一、按要求改寫下列單詞。1. Japan →___________ adj. 日本(人)的2. Canton →_________ adj. 廣東(人)的3. Vietnam →__________ adj. 越南(人)的4. Europe →__________ adj. 歐洲(人)的5. India → ________ adj. 印度(人)的6. Africa →_______ adj. 非洲(人)的7…

CentOS 桌面啟動無登錄界面

最近VMWare下搞了2個CentOS 32bit虛擬機, 裝了些軟件之后&#xff0c;都遇到開機無法顯示登錄界面&#xff0c; 僅能看見桌面背景圖的情況。 以下是我搜索很久匯總的方法。 嘗試按 ctrl alt F3(快捷鍵可能有所不同), 由桌面模式進入命令行模式。 直接 startx 報錯&#xf…

批量刪除推文_如何搜索(和刪除)您的舊推文

批量刪除推文“The internet never forgets” is an aphorism that isn’t entirely true, but it’s worth thinking about whenever you post to social media. If you think your Twitter profile needs a bit of a scrub, here’s how to search and delete those old twee…