WPF 繪製對齊畫素的清晰顯示的線條

2021-09-19 22:08:57 字數 3672 閱讀 4959

原文:

wpf 繪製對齊畫素的清晰顯示的線條

此前有小夥伴詢問我為何他 1 畫素的線條顯示發虛,然後我告訴他是「畫素對齊」的問題,然而他設定了各種對齊畫素的屬性依舊沒有作用。於是我對此進行了一系列試驗,對 wpf 畫素對齊的各種方法進行了一次總結。此後在 stackoverflow 中,我回答了 graphics - wpf drawingcontext seems ignore snaptodevicepixels - stack overflow 問題。

閱讀本文,我們將了解解決 wpf 畫素對齊的四種方法以及其各自的適用範圍和***。

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

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

然而為什麼 wpf 不預設為我們對齊畫素呢?這是因為要對齊畫素必定帶來尺寸上的偏差;這是繪製尺寸精度和最終呈現效果之間的平衡。在 macbook、su***ce pro 這些高檔顯示屏上,根本不用管這樣的平衡問題;但在渣渣顯示器上,微軟把這種平衡的控制交給了應用的開發者。

方法一:布局取整 uselayoutrounding

實際效果是:

根本就不起作用

事實上我們從 .net framework 原始碼可以得知,uselayoutrounding實際只處理 ui 元素對自己子級控制項的布局取整。一旦整棵布局樹種有任何乙個不是整數(或者 dpi 相乘後不是整數),那麼就依然沒有解決問題。

方法二:對齊裝置畫素 snapstodevicepixels

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

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

方法三:使用 drwingcontext 繪製並配合 guidelineset

如果自己處理繪製,則可以在onrender方法中使用drawingcontext來繪製各種各樣的形狀。drawingcontext有方法pushguidelineset,而pushguidelineset就是用來處理對齊的。

▲ 看不清的可以考慮方法看

於是要想畫素對齊,必須:

guidelineset的使用可以參考我在 stackoverflow 上的回答:graphics - wpf drawingcontext seems ignore snaptodevicepixels - stack overflow。

以下是我編寫的用於輔助繪製對齊線條的擴充套件方法:

public

static

class snapdrawingextensions

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.pop();}}

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

onrender中呼叫它繪製:

方法四:renderoptions.edgemode

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

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

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

如果你都作用物件上還有其它視覺物件,它們也會一併變成了「硬邊緣」,是可以看得見乙個個畫素的邊緣。

如果畫粗線條粗邊框,那麼renderoptions.edgemode最適合了,因為設定起來最方便,可以設定到所有的 ui 元素上。由於邊框很粗,所以多乙個少乙個畫素使用者也注意不到。

如果是畫細邊框,那麼使用border配合snapstodevicepixels可以解決,無論是 0.8 畫素還是 1.0 畫素,1.2 畫素,都能在準確地顯示其粗細的基礎之上還保證畫素對齊。

如果圖形比較複雜,比如繪製**或者其它各種交叉了線條的圖形,那麼使用drawingcontext繪製,並設定guidelineset對齊。

如果視窗非常簡單,既沒有縮放,ui 元素也不多,可以考慮使用uselayoutrounding碰碰運氣,萬一介面簡單到只需要整數對齊就夠了呢?

特別說明,上面四種方法不足與應對所有的畫素對齊情況,如果還是沒辦法對齊……節哀把……我們一起找偏方……

iOS繪製1畫素的線

有時候頁面上需要繪製1畫素的分隔線,可以通過新增 view 的方式,也可以通過 uigraphicsgetcurrentcontext 的方式實現。但是通過uigraphicsgetcurrentcontext實現的時候需要注意畫素的問題。在繪製1畫素線之前,我們先來看一下繪製高度為50的線是什麼樣...

WPF 如何畫出1畫素的線

如何有人告訴你,請你畫出1畫素的線,是不是覺得很簡單,實際上在 wpf 上還是比較難的。畫出線的第乙個方法,建立乙個 canvas 新增乙個線 介面 x name canvas canvas 在後台新增一條線 line myline new line myline.stroke system.win...

WPF 如何畫出1畫素的線

原文 wpf 如何畫出1畫素的線 如何有人告訴你,請你畫出1畫素的線,是不是覺得很簡單,實際上在 wpf 上還是比較難的。畫出線的第乙個方法,建立乙個 canvas 新增乙個線 介面 x name canvas canvas 在後台新增一條線 line myline new line myline....