最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

WPF基于物理像素繪制圖形

 更新時(shí)間:2022年06月27日 14:04:53   作者:Aaron?Lu  
這篇文章介紹了WPF基于物理像素繪制圖形的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

WPF中有一個(gè)DrawingContext類,該類提供了很多畫法方法,例如DrawLine,DrawText,DrawRectangle等。開發(fā)者使用它們可以方便地進(jìn)行圖形繪制。不過,在使用DrawingContext過程中,我發(fā)現(xiàn)使用DawLine方法畫出的線條在某些部分有些模糊。這個(gè)問題的解決,需要一些計(jì)算機(jī)圖形學(xué)方面的知識(shí),使用的方法并不是很復(fù)雜。不過,這個(gè)方法所涉及到的一些過程有些令人費(fèi)解(好吧,我沒有專門學(xué)過計(jì)算機(jī)圖形學(xué)),本文是我在實(shí)踐中的一些嘗試和經(jīng)驗(yàn)的總結(jié)。

還是從一個(gè)例子開始吧:
從FrameworkElement繼承一個(gè)MyRectangleElement,然后重寫OnRender方法如下:

protected override void OnRender(DrawingContext drawingContext)
{
    Pen pen = new Pen(Brushes.Black, 1);
    Rect rect = new Rect(20,20, 50, 60); 
    drawingContext.DrawRectangle(null, pen, rect);
}

然后在Window上呈現(xiàn)MyRectangleElement,效果(右下角有放大鏡)如下:

通過放大圖形,能夠很清晰地看到線條是不均勻的,在宏觀視覺效果上感覺模糊,看上去很不舒服。于是,兩個(gè)問題就產(chǎn)生了:

  • 為什么會(huì)出現(xiàn)這樣的問題?
  • 如何解決?

怎么辦?MSDN,百度,Google一起上!皇天不負(fù)苦心人,現(xiàn)在,謎底來到了我們的面前:

1、為什么會(huì)出現(xiàn)這樣的效果呢?

WPF呈現(xiàn)引擎的反鋸齒系統(tǒng)將那些沒有在物理像素系統(tǒng)上的線擴(kuò)展到了多個(gè)像素上。這里涉及到以下三個(gè)概念:

  • 物理像素系統(tǒng):與物理圖形設(shè)備相關(guān)??梢院?jiǎn)單地理解為一個(gè)像素點(diǎn)的二維矩陣;
  • 邏輯像素系統(tǒng):我們?cè)诋嫹ǚ椒ㄖ兴褂玫亩ㄎ幌到y(tǒng);
  • 反鋸齒效果:一種計(jì)算機(jī)圖形學(xué)上的算法,使得圖形邊緣更光滑;

聲明:以上三個(gè)概念的解釋是根據(jù)我個(gè)人的理解,不一定準(zhǔn)確、嚴(yán)謹(jǐn)。如果您發(fā)現(xiàn)什么不對(duì)的地方,還望不吝指正。
為了更好地幫助您理解這個(gè)過程,這里給出一個(gè)示意圖來解釋一下:

上圖中的淡藍(lán)色網(wǎng)格可以視為“物理像素系統(tǒng)”,深色的圖案是您畫出的線條??梢院苊黠@地看出,這條線的四條邊都不在“物理像素系統(tǒng)”上,因此,“反鋸齒系統(tǒng)”會(huì)將此線條的四條邊擴(kuò)展到相應(yīng)的“物理像素系統(tǒng)”上。于是,本文最開始的情景便出現(xiàn)了。

2、如何解決這個(gè)問題?

針對(duì)不同的問題上下文,WPF給出了與之相應(yīng)的解決方案。據(jù)我所知,有如下幾個(gè):

  • 1> 對(duì)于UIElement及其子類,使用SnapsToDevicePixels屬性

對(duì)于從UIElement繼承的類型,比如Control,通過將SnapsToDevicePixels設(shè)置為true可以得到清晰的圖像,該屬性的默認(rèn)值為false。FrameworkElement從UIElement繼承的時(shí)候,給這個(gè)屬性賦予了一個(gè)Inherited元數(shù)據(jù)。如此一來,只要您在FrameworkElement Tree的根結(jié)點(diǎn)上將此屬性設(shè)置為true,那么整個(gè)FrameworkElement Tree的繪制都將變得清晰起來。

  • 2> 對(duì)于自定義畫法,使用GuidelineSet

SnapsToDevicePixels屬性對(duì)于WPF Control來說是有用的,但是對(duì)本文的問題無能為力,于是,嗯,GuidelineSet橫空出世!對(duì)于這個(gè)類,MSDN只是給出了一個(gè)用法的示例,從這個(gè)例子中我只能看到GuidelineSet可以這么用,但為什么是這樣用就沒有答案了。而且,有點(diǎn)離譜、神奇的是:MSDN上關(guān)于這點(diǎn)上一個(gè)示意圖內(nèi)容上有錯(cuò)。如下圖所示:

圖下部的左黑框是用了GuidelineSet后出現(xiàn)的結(jié)果,右邊才是沒有使用的結(jié)果。這兩個(gè)圖的位置應(yīng)該互換一下。

我們必須回答這個(gè)問題:如果在(x1,y1) (x2,y2)處畫一條線該如何在DrawingContext上使用GuidelineSet,以保證畫法是清晰的呢?

這個(gè)問題讓我著實(shí)納悶了許久(原因本文第一段已經(jīng)交代),不過,經(jīng)過不斷地嘗試和思考,最終我找到了答案:
Guideline其實(shí)是圖形設(shè)備在呈現(xiàn)時(shí)用來把邏輯像素點(diǎn)對(duì)齊到物理像素點(diǎn)的參考量。 使用它告訴圖形設(shè)備你希望哪些邏輯像素點(diǎn)被對(duì)齊到物理像素點(diǎn)上。

聲明:以上概念的解釋是根據(jù)我個(gè)人的理解,不一定準(zhǔn)確、嚴(yán)謹(jǐn)。如果您發(fā)現(xiàn)什么不對(duì)的地方,還望不吝指正。

下面,我將使用一個(gè)簡(jiǎn)單的示例來演示如何使用GuidelineSet,以及它所帶來的效果。在這個(gè)示例中,我們使用DrawingContext的DrawLine方法繪制一個(gè)10×10的網(wǎng)格,相關(guān)代碼如下:
首先,定義畫法所用到的常量

    internal static class DrawingConstants
    {
        public static readonly int Rows = 10;
        public static readonly int Columms = 10;
        public static readonly double PenThickness = 1.0;
        public static readonly double HalfOfPenThickness = PenThickness/2;
    }

然后,定義NormalDrawingElement(使用一般畫法):

    class NormalDrawingElement : FrameworkElement
    {
        protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            double xOffset = Math.Floor(this.RenderSize.Width / DrawingConstants.Columms);
            double yOffset = Math.Floor(this.RenderSize.Height / DrawingConstants.Rows);
            double xLineWidth = Math.Floor(this.RenderSize.Width);
            double yLineHeight = Math.Floor(this.RenderSize.Height);

            DrawingContext dct = drawingContext;
            Pen blackPen = new Pen(Brushes.Black, DrawingConstants.PenThickness);
            blackPen.Freeze();

            //Draw the horizontal lines  
            Point x = new Point(0, 0);
            Point y = new Point(xLineWidth, 0);
            for (int i = 0; i <= DrawingConstants.Rows; i++)
            {
                dct.DrawLine(blackPen, x, y);
                x.Offset(0, yOffset);
                y.Offset(0, yOffset);
            }

            //Draw the vertical lines  
            x = new Point(0, 0);
            y = new Point(0, yLineHeight);
            for (int i = 0; i <= DrawingConstants.Columms; i++)
            {
                dct.DrawLine(blackPen, x, y);
                x.Offset(xOffset, 0);
                y.Offset(xOffset, 0);
            }
        }
    }

定義GuidelineSetDrawingElement(使用GuidelineSet):

    class GuidelineSetDrawingElement : FrameworkElement
    {
        protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            double xOffset = Math.Floor(this.RenderSize.Width / DrawingConstants.Columms);
            double yOffset = Math.Floor(this.RenderSize.Height / DrawingConstants.Rows);
            double xLineWidth = Math.Floor(this.RenderSize.Width);
            double yLineHeight = Math.Floor(this.RenderSize.Height);

            DrawingContext dct = drawingContext;
            Pen blackPen = new Pen(Brushes.Black, DrawingConstants.PenThickness);
            blackPen.Freeze();

            //Draw the horizontal lines  
            Point x = new Point(0, 0);
            Point y = new Point(xLineWidth, 0);
            for (int i = 0; i <= DrawingConstants.Rows; i++)
            {
                dct.PushGuidelineSet(new GuidelineSet(null, new double[] { y.Y - DrawingConstants.HalfOfPenThickness, y.Y + DrawingConstants.HalfOfPenThickness}));
                dct.DrawLine(blackPen, x, y);
                dct.Pop();
                x.Offset(0, yOffset);
                y.Offset(0, yOffset);
            }

            //Draw the vertical lines  
            x = new Point(0, 0);
            y = new Point(0, yLineHeight);
            for (int i = 0; i <= DrawingConstants.Columms; i++)
            {
                dct.PushGuidelineSet(new GuidelineSet(new double[] { x.X + DrawingConstants.HalfOfPenThickness, x.X - DrawingConstants.HalfOfPenThickness}, null));
                dct.DrawLine(blackPen, x, y);
                dct.Pop();
                x.Offset(xOffset, 0);
                y.Offset(xOffset, 0);
            }
        }
    }

這個(gè)畫法和上一個(gè)畫法的區(qū)別僅僅在于以下兩點(diǎn):

  • 對(duì)于水平方向的線,我期望它的兩個(gè)水平邊緣是和”物理像素系統(tǒng)“對(duì)齊的;
  • 對(duì)于垂直方向的線,我期望它的兩個(gè)垂直邊緣是和”物理像素系統(tǒng)“對(duì)齊的。

最后,我們將這兩個(gè)Element呈現(xiàn)出來:

<Window x:Class="GuidelineSetDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:GuideLineSetDemo"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Grid Grid.Column="0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock Text="Normal Drawing" Margin="3" HorizontalAlignment="Center"/>
            <loc:NormalDrawingElement Margin="10" Grid.Row="1"/>
        </Grid>

        <Grid Grid.Column="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock Text="Dawing with GuidelineSet" Margin="3" HorizontalAlignment="Center"/>
            <loc:GuidelineSetDrawingElement Margin="10" Grid.Row="1"/>
        </Grid>
    </Grid>
</Window>

運(yùn)行后的效果如下:

另外,使用GuidelineSet的時(shí)候需要注意以下幾個(gè)細(xì)節(jié):

  • 1、Push和pop一般情況下要成對(duì)(其實(shí)當(dāng)您調(diào)用DrawingContext上的PushXXX方法后,都要考慮是否有與之對(duì)應(yīng)的Pop方法調(diào)用);
  • 2、GuidelineSet只對(duì)水平或者垂直線有用;
  • 3、使用GuidelineSet后,您所繪制圖形的位置或大小可能和最初的設(shè)定有細(xì)微的差別。

您可以從這里下載本文最后一個(gè)示例的源代碼。

到此這篇關(guān)于WPF基于物理像素繪制圖形的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#探秘系列(一)——ToDictionary,ToLookup

    C#探秘系列(一)——ToDictionary,ToLookup

    這個(gè)系列我們看看C#中有哪些我們知道,但是又不知道怎么用,又或者懶得去了解的東西,比如這篇我們要介紹的toDictionary和ToLookup。
    2014-05-05
  • C#實(shí)現(xiàn)鼠標(biāo)拖拽無邊框浮動(dòng)窗體的方法

    C#實(shí)現(xiàn)鼠標(biāo)拖拽無邊框浮動(dòng)窗體的方法

    一般情況下,在標(biāo)題欄中按住鼠標(biāo)左鍵不放即可實(shí)現(xiàn)拖動(dòng)操作,當(dāng)做浮動(dòng)窗體時(shí),如果包含窗體邊框,那么界面給使用者的感覺將很不友好,因此本文給大家介紹了C#實(shí)現(xiàn)鼠標(biāo)拖拽無邊框浮動(dòng)窗體的方法,感興趣的朋友可以參考下
    2024-04-04
  • C#中IntPtr類型的具體使用

    C#中IntPtr類型的具體使用

    本文主要介紹了C#中IntPtr類型的具體使用,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C#獲取DICOM圖像像素的像素值的代碼詳解

    C#獲取DICOM圖像像素的像素值的代碼詳解

    DICOM即醫(yī)學(xué)數(shù)字成像和通信,是醫(yī)學(xué)圖像和相關(guān)信息的國際標(biāo)準(zhǔn)(ISO 12052),它定義了質(zhì)量能滿足臨床需要的可用于數(shù)據(jù)交換的醫(yī)學(xué)圖像格式,這篇文章主要介紹了C#獲取DICOM圖像像素的像素值的方法,需要的朋友可以參考下
    2024-07-07
  • C#在窗體中設(shè)計(jì)滾動(dòng)字幕的方法

    C#在窗體中設(shè)計(jì)滾動(dòng)字幕的方法

    普通窗體中的文字位置都是固定的,但在一些窗體中需要讓文字動(dòng)起來,如一些廣告性較強(qiáng)的界面中需要做一些滾動(dòng)的字幕,所以本文給大家介紹了C#在窗體中設(shè)計(jì)滾動(dòng)字幕的方法,需要的朋友可以參考下
    2024-04-04
  • 基于一個(gè)應(yīng)用程序多線程誤用的分析詳解

    基于一個(gè)應(yīng)用程序多線程誤用的分析詳解

    本篇文章是對(duì)一個(gè)應(yīng)用程序多線程的誤用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C#設(shè)置輸入法實(shí)例分析

    C#設(shè)置輸入法實(shí)例分析

    這篇文章主要介紹了C#設(shè)置輸入法的方法,實(shí)例分析了C#獲取系統(tǒng)輸入法及設(shè)置輸入法的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • C#實(shí)現(xiàn)在PDF文檔中應(yīng)用多種不同字體

    C#實(shí)現(xiàn)在PDF文檔中應(yīng)用多種不同字體

    在PDF文檔中,可繪制不同字體樣式、不同語言的文字,可通過使用Standard字體、TrueType字體、CJK字體或者自定義(私有)等字體類型。本文將具體介紹實(shí)現(xiàn)的方法,需要的可以參考一下
    2022-01-01
  • C#中單例的實(shí)現(xiàn)方法

    C#中單例的實(shí)現(xiàn)方法

    這篇文章主要介紹了C#中單例的實(shí)現(xiàn)方法,以實(shí)例形式分析了C#中單例的原理與實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-01-01
  • C# 中的委托詳細(xì)解析與完整應(yīng)用小結(jié)

    C# 中的委托詳細(xì)解析與完整應(yīng)用小結(jié)

    C#委托是一種類型安全的函數(shù)指針,允許將方法作為參數(shù)傳遞或賦值給變量,它在事件處理、回調(diào)和異步編程中廣泛應(yīng)用,本文詳細(xì)介紹了委托的基本概念、用法和高級(jí)應(yīng)用,感興趣的朋友一起看看吧
    2025-03-03

最新評(píng)論

南岸区| 独山县| 东丽区| 林州市| 永昌县| 易门县| 南漳县| 社旗县| 青岛市| 岳阳县| 家居| 旬邑县| 阜阳市| 庆阳市| 三明市| 岳西县| 资中县| 德州市| 雷波县| 嘉禾县| 民丰县| 民和| 大安市| 句容市| 虞城县| 通辽市| 乌恰县| 乌什县| 德化县| 兴安盟| 清远市| 安溪县| 新竹县| 三亚市| 潮州市| 固安县| 漳平市| 韶关市| 台山市| 陈巴尔虎旗| 海南省|