WPF學習(11)2D繪圖

本篇咱們來學習WPF的繪圖,在2D繪圖中主要有這麼幾個重要的類:Drawing、Visual和Shape,順便講下Brush和BitmapEffect。ios

1 2D繪圖

1.1Drawing類

Drawing類表示形狀和路徑的二維圖,它繼承自Animatable類,因此支持數據綁定、動畫和資源引用等。它有這麼幾個子類:瀏覽器

  • GeometryDrawing:包含Geometry、用於填充的Brush以及繪畫輪廓的Pen(都是屬性)
  • ImageDrawing:包含ImageSource以及定義邊界的Rect
  • VideoDrawing:包含MediaPlayer以及定義邊界的Rect
  • GlyphRunDrawing:包含GlyphRun類、低級別的文本類以及用於繪製前景色的Brush
  • DrawingGroup:包含一組Drawing對象的集合類

Drawing並非UIElement,因此自己沒有繪畫的能力,若是將其設置爲窗體或者內容控件的內容,將只是顯示其ToString的結果,要想在呈現Drawing,能夠將DrawingImage、DrawingBrush和DrawingVisual這三者對象做爲宿主容器。安全

1.1.1GeometryDrawing

GeometryDrawing,顧名思義就是畫幾何圖形的,這個功能是由Geometry類型屬性提供的。Geometry類大的方向可分爲Basic Geometry和Aggregate Geometry:ide

Basic Geometry基本幾何體又可分爲:佈局

  • RectangleGeometry:繪製矩形,包括定義尺寸的Rect屬性以及分別定義圓角X軸和Y軸的RadiusX和RadiusY
  • EllipseGeometry:繪製橢圓,包括定義圓心的Center屬性以及分別定義圓角X軸和Y軸的RadiusX和RadiusY
  • LineGeometry:繪製直線,包括起點StartPoint屬性和終點EndPoint屬性
  • PathGeometry:繪製任何幾何圖形,包括描述輪廓的Figures(默認內容屬性)和填充效果FillRule,該屬性是一組PathFigure集合,而PathFigure又包含表示PathSegment(路徑片斷)集合的Segments。

  PathSegment是一個抽象類,它有如下幾個實現類:LineSegment(線段)、PolyLineSegment(線段集合)、ArcSegment(曲線段)、BezierSegment(三次貝塞爾)、PolyBezierSegment(三次貝塞爾集合)、  QuadraticBezierSegment(二次貝塞爾)和PolyQuadraticBezierSegment(二次貝塞爾集合)。性能

Aggregate Geometry聚合集合體又可分爲:學習

GeometryGroup:顧名思義,有一個或者多個Geometry組成,自己是Geometry類型,經過Transform屬性來表現複雜圖形測試

CombinedGeometry:他不是一個通用的Geometry集合,它經過GeometryCombineModel枚舉器來合併有且僅有的兩個Geometry。字體

// 摘要:
    //     指定可用於合併兩個幾何圖形的不一樣方法。
    public enum GeometryCombineMode
    {
        // 摘要:
        //     經過採用兩個區域的並集合並兩個區域。 所生成的幾何圖形爲幾何圖形 A + 幾何圖形 B。
        Union = 0,
        //
        // 摘要:
        //     經過採用兩個區域的交集合並兩個區域。 新的區域由兩個幾何圖形之間的重疊區域組成。
        Intersect = 1,
        //
        // 摘要:
        //     將在第一個區域中但不在第二個區域中的區域與在第二個區域中但不在第一個區域中的區域進行合併。 新的區域由 (A-B) + (B-A) 組成,其中 A
        //     和 B 爲幾何圖形。
        Xor = 2,
        //
        // 摘要:
        //     從第一個區域中除去第二個區域。 若是給出兩個幾何圖形 A 和 B,則從幾何圖形 A 的區域中除去幾何圖形 B 的區域,所產生的區域爲 A-B。
        Exclude = 3,
    }
View Code

 當咱們在用PathGeometry來繪製複雜的圖形時,須要寫不少的代碼,這時候MS總會想些辦法來爲咱們減小工做量,這就是路徑標記語法。動畫

1.1.2ImageDrawing

很明顯,ImageDrawing是用來繪製圖像的,它經過ImageSource屬性指定要繪製的圖像,經過Rect屬性來制定每一個圖像的位置和大小。

<Grid Grid.Row="1" Grid.Column="2">
            <Grid.Background>
                <DrawingBrush>
                    <DrawingBrush.Drawing>
                        <ImageDrawing ImageSource="/Images/2.png" Rect="0,0,50,50"/>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </Grid.Background>
        </Grid>

1.1.3VideoDrawing

播放媒體文件。 若是媒體爲視頻文件,則 VideoDrawing 會將其繪製到指定的矩形中。它包含一個獲取或設置與繪製關聯的媒體播放器的MediaPlayer類型的Player屬性,包含一個獲取或設置可在其中繪製視頻的矩形區域的Rect屬性。雖然能夠在 XAML 中聲明此類的實例,可是因爲MediaPlayer類的依賴項,特別是 Open 和 Play 方法的緣故,若是不使用代碼,則沒法加載和播放其媒體。 要只在 XAML 中播放媒體,請使用 MediaElement。可使用 VideoDrawingMediaPlayer 來播放音頻或視頻文件。 加載並播放媒體的方法有兩種。 第一種方法是使用 MediaPlayerVideoDrawing 自身,第二種方法是建立您本身的 MediaTimeline,並將其與 MediaPlayerVideoDrawing 一塊兒使用,這種方法能夠控制Video的播放,好比快進等。

// Create a MediaTimeline.
MediaTimeline mTimeline = 
    new MediaTimeline(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative)); 

// Set the timeline to repeat.
mTimeline.RepeatBehavior = RepeatBehavior.Forever;

// Create a clock from the MediaTimeline.
MediaClock mClock = mTimeline.CreateClock();

MediaPlayer repeatingVideoDrawingPlayer = new MediaPlayer();
repeatingVideoDrawingPlayer.Clock = mClock;

VideoDrawing repeatingVideoDrawing = new VideoDrawing();
repeatingVideoDrawing.Rect = new Rect(150, 0, 100, 100);
repeatingVideoDrawing.Player = repeatingVideoDrawingPlayer; 
View Code

1.1.4GlyphRunDrawing

表示一個呈現GlyphRunDrawing對象,而GlyphRun表示一序列標誌符號,這些標誌符號來自具備一種字號和一種呈現樣式的一種字體。

<Grid Grid.Row="2" x:Name="grid">
            <Grid.Background>
                <DrawingBrush>
                    <DrawingBrush.Drawing>
                        <GlyphRunDrawing ForegroundBrush="Black">
                            <GlyphRunDrawing.GlyphRun>
                                <GlyphRun 
        CaretStops="{x:Null}" 
      ClusterMap="{x:Null}" 
      IsSideways="False" 
      GlyphOffsets="{x:Null}" 
      GlyphIndices="43 72 79 79 82 3 58 82 85 79 71" 
      BaselineOrigin="0,12.29"  
      FontRenderingEmSize="13.333333333333334" 
      DeviceFontName="{x:Null}" 
      AdvanceWidths="9.62666666666667 7.41333333333333 2.96 2.96 7.41333333333333 3.70666666666667 12.5866666666667 7.41333333333333 4.44 2.96 7.41333333333333" 
      BidiLevel="0">
                                    <GlyphRun.GlyphTypeface>
                                        <GlyphTypeface FontUri="C:\WINDOWS\Fonts\TIMES.TTF" />
                                    </GlyphRun.GlyphTypeface>
                                </GlyphRun>
                            </GlyphRunDrawing.GlyphRun>
                        </GlyphRunDrawing>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </Grid.Background>
        </Grid>
View Code

GlyphRun是一種比Label等更低級別的文本展現方式,用於固定格式的文檔表示和打印方案

1.1.5DrawingGroup

DrawingGroup用於將一個或者多個Drawing組合起來做爲總體繪圖,好比你能夠將多個Drawing組合起來,使用Transform屬性。

<Image Grid.Row="2" Grid.Column="1">
            <Image.Source>
                <DrawingImage>
                    <DrawingImage.Drawing>
                        <DrawingGroup>
                            <DrawingGroup.Transform>
                                <RotateTransform Angle="30" />
                            </DrawingGroup.Transform>
                            <GeometryDrawing>
                                <GeometryDrawing.Pen>
                                    <Pen Brush="Black" Thickness="10"/>
                                </GeometryDrawing.Pen>
                                <GeometryDrawing.Brush>
                                    <SolidColorBrush Color="Silver" />
                                </GeometryDrawing.Brush>
                                <GeometryDrawing.Geometry>
                                    <PathGeometry FillRule="Nonzero">
                                        <PathGeometry.Figures>
                                            <PathFigure IsClosed="True">
                                                <PathFigure.Segments>
                                                    <!--<QuadraticBezierSegment Point1="0,0" Point2="1,1" />-->
                                                    <LineSegment Point="0,100" />
                                                    <LineSegment Point="80,100" IsSmoothJoin="True"/>
                                                </PathFigure.Segments>
                                            </PathFigure>
                                            <PathFigure IsClosed="True" StartPoint="50,0">
                                                <PathFigure.Segments>
                                                    <!--<QuadraticBezierSegment Point1="0,0" Point2="1,1" />-->
                                                    <LineSegment Point="0,100" />
                                                    <LineSegment Point="80,100" IsSmoothJoin="True"/>
                                                </PathFigure.Segments>
                                            </PathFigure>
                                        </PathGeometry.Figures>
                                    </PathGeometry>
                                </GeometryDrawing.Geometry>
                            </GeometryDrawing>
                            <GeometryDrawing>
                                <GeometryDrawing.Pen>
                                    <Pen Brush="Green" Thickness="1" />
                                </GeometryDrawing.Pen>
                                <GeometryDrawing.Geometry>
                                    <RectangleGeometry Rect="0,0,100,100" />
                                </GeometryDrawing.Geometry>
                            </GeometryDrawing>
                        </DrawingGroup>
                    </DrawingImage.Drawing>
                </DrawingImage>
            </Image.Source>
        </Image>
View Code

1.2Visual

Visual類是UIElement類的抽象基類,它是任何東西繪畫到屏幕上的基本實現。DrawingVisual也是Visual的一個間接子類(直接繼承自ContainerVisual),它主要是呈現Drawing,例如Image、Video等,並且它還支持經過Hit Testing來進行與輸入設備的交互,可是它不能接收鍵盤、鼠標或筆針事件。

1.2.1DrawingVisual在屏幕上的顯示

咱們知道UIElement都能很好的在屏幕上顯示,然而,DrawingVisual在做爲ContentControl的內容時,只是顯示其ToString的結果。爲了讓DrawingVisual顯示在屏幕上,須要將其添加到UIElement的Visual樹結構上,將該UIElment做爲其宿主容器。爲此,咱們須要作這樣的兩個工做:一是自定義一個繼承自UIElement的類;而是Override其VisualChildrenCount屬性和GetVisualChild方法。

class WndVisual:UIElement
    {
        private DrawingVisual drawingVisual = null;
        public WndVisual()
        {
            drawingVisual = new DrawingVisual();
        }
        protected override int VisualChildrenCount
        {
            get
            {
                return 1;
            }
        }
        protected override Visual GetVisualChild(int index)
        {
            if (index != 0)
                throw new ArgumentOutOfRangeException("index");
            return drawingVisual;
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            //Drawing drawing = FindResource("glyphRunStyle") as GlyphRunDrawing;
            //drawingContext.DrawDrawing(drawing);
            base.OnRender(drawingContext);
        }
        protected override void OnRenderSizeChanged(SizeChangedInfo info)
        {
            GlyphRunDrawing glyphRun = Application.Current.MainWindow.FindResource("glyphRenStyle") as GlyphRunDrawing;
            if (glyphRun != null)
            {
                using (var drawingContext = drawingVisual.RenderOpen())
                {
                    drawingContext.DrawDrawing(glyphRun);
                }
                this.AddVisualChild(drawingVisual);
            }
            base.OnRenderSizeChanged(info);
        }
    }
View Code

1.2.2 Visual的Hit Testing

Hit Testing譯爲命中測試,它指的是判斷一個點或者一組點是否與一個給定的對象相交。應用於鼠標時,一般是指鼠標指針的位置。

在WPF中有兩種命中測試:Visual Hit Testing和Input Hit Testing。前者被全部的Visual對象支持,然後者僅被UIElement對象支持。這個很好理解,輸入事件被定義在UIElement類中。這裏主要講Visual Hit Testing。經過VisualTreeHelper的HitTest方法來實現,來看下方法定義:

//
        // 摘要:
        //     經過指定 System.Windows.Point 返回命中測試的最頂層 System.Windows.Media.Visual 對象。
        //
        // 參數:
        //   reference:
        //     要進行命中測試的 System.Windows.Media.Visual。
        //
        //   point:
        //     要進行命中測試的點值。
        //
        // 返回結果:
        //     System.Windows.Media.Visual 的命中測試結果,做爲 System.Windows.Media.HitTestResult
        //     類型返回。
        public static HitTestResult HitTest(Visual reference, Point point);
        //
        // 摘要:
        //     使用調用方定義的 System.Windows.Media.HitTestFilterCallback 和 System.Windows.Media.HitTestResultCallback
        //     方法對指定的 System.Windows.Media.Visual 啓動命中測試。
        //
        // 參數:
        //   reference:
        //     要進行命中測試的 System.Windows.Media.Visual。
        //
        //   filterCallback:
        //     表示命中測試篩選回調值的方法。
        //
        //   resultCallback:
        //     表示命中測試結果回調值的方法。
        //
        //   hitTestParameters:
        //     要進行命中測試的參數值。
        public static void HitTest(Visual reference, HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback, HitTestParameters hitTestParameters);
        //
        // 摘要:
        //     使用調用方定義的 System.Windows.Media.HitTestFilterCallback 和 System.Windows.Media.HitTestResultCallback
        //     方法對指定的 System.Windows.Media.Media3D.Visual3D 啓動命中測試。
        //
        // 參數:
        //   reference:
        //     要進行命中測試的 System.Windows.Media.Media3D.Visual3D。
        //
        //   filterCallback:
        //     表示命中測試篩選回調值的方法。
        //
        //   resultCallback:
        //     表示命中測試結果回調值的方法。
        //
        //   hitTestParameters:
        //     要進行命中測試的三維參數值。
        public static void HitTest(Visual3D reference, HitTestFilterCallback filterCallback, HitTestResultCallback resultCallback, HitTestParameters3D hitTestParameters);
View Code

第一個版本是一個簡單的版本,它僅用於返回命中測試的最頂層的對象;第二個版本可用於重疊的Visual的命中測試的狀況;第三個版本可用於重疊的Visual3D的命中測試的狀況。

class WndDrawingVisual3:UIElement
    {
        DrawingVisual bodyVisual = null;
        DrawingVisual eyesVisual = null;
        DrawingVisual mouthVisual = null;
        public WndDrawingVisual3()
        {
            bodyVisual = new DrawingVisual();
            eyesVisual = new DrawingVisual();
            mouthVisual = new DrawingVisual();

            using (DrawingContext dc = bodyVisual.RenderOpen())
            {
                // The body
                dc.DrawGeometry(Brushes.Blue, null, Geometry.Parse(
                @"M 240,250
              C 200,375 200,250 175,200
              C 100,400 100,250 100,200
              C 0,350 0,250 30,130
              C 75,0 100,0 150,0
              C 200,0 250,0 250,150 Z"));
            }
            using (DrawingContext dc = eyesVisual.RenderOpen())
            {
                // Left eye
                dc.DrawEllipse(Brushes.Black, new Pen(Brushes.White, 10),
                  new Point(95, 95), 15, 15);
                // Right eye
                dc.DrawEllipse(Brushes.Black, new Pen(Brushes.White, 10),
                 new Point(170, 105), 15, 15);
            }
            using (DrawingContext dc = mouthVisual.RenderOpen())
            {
                // The mouth
                Pen p = new Pen(Brushes.Black, 10);
                p.StartLineCap = PenLineCap.Round;
                p.EndLineCap = PenLineCap.Round;
                dc.DrawLine(p, new Point(75, 160), new Point(175, 150));
            }
            bodyVisual.Children.Add(eyesVisual);
            bodyVisual.Children.Add(mouthVisual);
            this.AddVisualChild(bodyVisual);
        }
        protected override Visual GetVisualChild(int index)
        {
            if (index != 0)
                throw new ArgumentOutOfRangeException("index");
            return bodyVisual;
        }
        protected override int VisualChildrenCount
        {
            get
            {
                return 1;
            }
        }
        protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            /*
            // Retrieve the mouse pointer location relative to the Window
            Point location = e.GetPosition(this);
            // Perform visual hit testing
            HitTestResult result = VisualTreeHelper.HitTest(this, location);
            // If we hit any DrawingVisual, rotate it
            if (result.VisualHit.GetType() == typeof(DrawingVisual))
            {
                DrawingVisual dv = result.VisualHit as DrawingVisual;
                if (dv.Transform == null)
                    dv.Transform = new RotateTransform();
                (dv.Transform as RotateTransform).Angle++;
            }
             * */
            Point location = e.GetPosition(this);
            VisualTreeHelper.HitTest(this, new HitTestFilterCallback(hitTestFilterCallback), new HitTestResultCallback(HitTestCallback), new PointHitTestParameters(location));
        }
        private HitTestResultBehavior HitTestCallback(HitTestResult result)
        {
            if (result.VisualHit.GetType() == typeof(DrawingVisual))
            {
                DrawingVisual dv = result.VisualHit as DrawingVisual;
                if (dv.Transform == null)
                    dv.Transform = new RotateTransform();
                (dv.Transform as RotateTransform).Angle++;
            }
            return HitTestResultBehavior.Continue;
        }
        private HitTestFilterBehavior hitTestFilterCallback(DependencyObject potentialHitTestTarget)
        {
            if (potentialHitTestTarget == bodyVisual)
                return HitTestFilterBehavior.ContinueSkipSelf;
            return HitTestFilterBehavior.Continue;
        }
        //重寫該方法,可實現自定義命中方式
        protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
        {
            return base.HitTestCore(hitTestParameters);
        }
    }
View Code

1.3Shape

Shape和GeometryDrawing同樣,也是基本的二位畫圖,它結合了Geometry、Brush和Pen。不一樣的是,Shape繼承者FrameworkElement,能夠直接在屏幕顯示。它包括Brush類型的Fill屬性和Stroke屬性。

它有這麼幾個子類:

  • Line:包括(X1,Y2)、(X2,Y2)這四個Double值,該座標是相對座標
  • Rectangle:包括圓角的RadiusX和RadiusY,當RadiusX=RadiusY>=Width/2=Height/2時,表示一個圓,RadiusX>=Width/2 && RadiusY>=Height/2表示一個橢圓
  • Ellipse:用最大的橢圓去填充由Width和Height決定的矩形區域,沒有指定圓角的RadiusX和RadiusY和Center屬性,當Width=Height時,表示一個圓
  • Polyline:表示一組有序的線段,經過Points屬性指定多個點,格式:Points="x1,y1 x2,y2"或者Points="x1 y1 x2 y2"
  • Polygon:它比Polyline惟一多作的事情就是會自動封閉
  • Path:它僅僅比Shape多了一個Geometry類型的Data屬性,所以能夠將任意的Geometry顯示在界面,這裏可使用路徑標記文法給Data賦值
<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Rectangle Fill="Orange" Stroke="Black" StrokeThickness="5" Width="100" Height="100" RadiusX="30" RadiusY="20" />
        <Ellipse Grid.Column="1" Fill="SkyBlue" Stroke="BurlyWood" Width="100" Height="80" StrokeThickness="5"/>
        <Line Grid.Column="2" Fill="Red" Stroke="DarkSalmon" X1="20" Y1="20" X2="100" Y2="100" StrokeThickness="5"/>
        <Line Grid.Column="2" Fill="Red" Stroke="DarkSalmon" X1="100" Y1="20" X2="20" Y2="100" StrokeThickness="5" />
        <Polyline Grid.Row="1" Points="20,20 20,100 100,100 100,20 20,20" FillRule="EvenOdd" Fill="Bisque" Stroke="Azure" StrokeDashCap="Round" StrokeThickness="5" StrokeEndLineCap="Triangle"/>
        <Path Grid.Row="1" Grid.Column="1" Fill="MintCream" Stroke="Purple" StrokeDashCap="Round" StrokeDashArray="1,1,10,15">
            <Path.Data>
                <LineGeometry StartPoint="30,30" EndPoint="80,80" />
            </Path.Data>
        </Path>
        <Path Grid.Row="1" Grid.Column="1" Fill="MintCream" Stroke="Purple" StrokeDashCap="Round" StrokeDashArray="1,1,10,15" Data="M 80,30 L 30,80 Z"/>
    </Grid>
View Code

1.4Brush

在使用XAML進行頁面佈局時,咱們發現幾乎不多直接用Color來交互,而是用Color的封裝對象Brush來直接交互。它包括三種Color Brush(SolidColorBrush、LinearGradientBrush和RadialGradientBrush)和三種Tile Brush(DrawingBrush、ImageBrush和VisualBrush)

  • SolidColorBrush:單色,它包含一個Color屬性,該屬性支持兩種色彩空間:sRGB和scRGB,另外還可使用ContextColor uri來使用ICC Profile
  • LinearGradientBrush:線性漸變色,它包含一個GradientStops屬性,該屬性是一個GradientStop集合,GradientStop經過Offset和Color屬性來控制
  • RadialGradientBrush:徑向漸變色,它的每一個GradientStop都以相同的起始點呈橢圓狀向外散發,經過GrandientOrigin屬性指定起始點,默認值(.5,.5),當GrandientOrigin與Center不在一點時,會呈現很驚          人的效果
  • DrawingBrush:包含Drawing屬性,能夠繪製形狀、文本、視頻、圖像或其餘繪圖,能夠經過Stretch、ViewBox、ViewPort、AlignmentX/AlignmentY來控制
  • ImageBrush:包含ImageSource屬性(矢量圖),而ImageDrawing的ImageSource屬性(位圖)
  • VisualBrush:包含Visual屬性,若是直接將一個新的UIElement做爲Visual屬性值,它只是一個外觀,不具備該UIElement的行爲,關聯一個屏幕上的UIElement則可具備其行爲
<Rectangle Grid.Row="1" Grid.Column="2" Width="100" Height="100" Stroke="Black">
            <Rectangle.Fill>
                <LinearGradientBrush StartPoint=".0,.0" EndPoint=".8,.8" SpreadMethod="Repeat">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0" Color="Red" />
                        <GradientStop Offset="0.5" Color="Green" />
                        <GradientStop Offset=".5" Color="Yellow" />
                        <GradientStop Offset=".7" Color="Yellow" />
                        <GradientStop Offset=".7" Color="Orange" />
                        <GradientStop Offset="1" Color="Blue" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>
        <Ellipse Grid.Row="2" Width="100" Height="80" Stroke="Red">
            <Ellipse.Fill>
                <SolidColorBrush Color="ContextColor file://C:/Windows/System32/spool/drivers/color/sRGB%20Color%20Space%20Profile.icm 1.0,0.0,0.0,0.0"/>
            </Ellipse.Fill>
        </Ellipse>
        <Ellipse Grid.Row="2" Grid.Column="1" Stroke="Black" Width="100" Height="80">
            <Ellipse.Fill>
                <RadialGradientBrush Center="0.3,0.3" RadiusX="0.7" RadiusY="0.7" GradientOrigin="0,0" SpreadMethod="Repeat">
                    <GradientStop Offset=".0" Color="Red" />
                    <GradientStop Offset=".5" Color="Yellow" />
                    <GradientStop Offset="1" Color="Green" />
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <ResizeGrip Grid.Row="2" Grid.Column="2" Width="100" Height="100" >
            <ResizeGrip.Background>
                <DrawingBrush Stretch="Fill" Viewbox="0 0 .5 1" AlignmentX="Left" AlignmentY="Top" Viewport="0 0 .5 .5" TileMode="FlipXY">
                    <DrawingBrush.Drawing>
                        <GeometryDrawing Brush="Yellow">
                            <GeometryDrawing.Pen>
                                <Pen Brush="Red" Thickness="5" />
                            </GeometryDrawing.Pen>
                            <GeometryDrawing.Geometry>
                                <RectangleGeometry RadiusX="5" RadiusY="3" Rect="0 0 10 10" />
                            </GeometryDrawing.Geometry>
                        </GeometryDrawing>
                    </DrawingBrush.Drawing>
                </DrawingBrush>
            </ResizeGrip.Background>
        </ResizeGrip>
        
        <Rectangle Grid.Column="3" Stroke="Black">
            <Rectangle.Fill>
                <ImageBrush ImageSource="/DrawingDemo;component/Images/1.png" />
            </Rectangle.Fill>
        </Rectangle>
        <Grid Grid.Row="1" Grid.Column="3">
            <StackPanel>
                <TextBox x:Name="tb" FontSize="30" Text="Hello"/>
                <Rectangle Width="{Binding ActualWidth,ElementName=tb}" Height="{Binding ActualHeight,ElementName=tb}">
                    <Rectangle.Fill>
                        <VisualBrush Visual="{Binding ElementName=tb}" />
                    </Rectangle.Fill>
                    <Rectangle.LayoutTransform>
                        <ScaleTransform ScaleY="-0.75" />
                    </Rectangle.LayoutTransform>
                    <Rectangle.OpacityMask>
                        <LinearGradientBrush EndPoint="0,1">
                            <GradientStop Offset="0" Color="Transparent" />
                            <GradientStop Offset="1" Color="#77000000" />
                        </LinearGradientBrush>
                    </Rectangle.OpacityMask>
                </Rectangle>
            </StackPanel>
        </Grid>
View Code

1.5BitmapEffect

BitmapEffect位圖效果指的是System.Windows.Media.Effects命名空間下的五種Effect,可被應用於UIElement、DrawingGroup和Viewport3DVisual等。這五種Effect分別是:

  • BevelBitmapEffect:建立一個凹凸效果,該效果根據指定的曲線來擡高圖像表面(已過期,用Effect代替)
  • BlurBitmapEffect:模擬經過離焦透鏡查看對象的情形(已過期,用BlurEffect代替)
  • DropShadowBitmapEffect:以微小偏移量在可視對象以後應用陰影,經過模擬虛構光源的投影來肯定偏移量(已過期,用DropShadowEffect代替)
  • EmbossBitmapEffect:建立可視對象的凹凸貼圖,從而製造人工光源下的深度和紋理效果(已過期,用Effect代替)
  • OuterGlowBitmapEffect:圍繞對象或顏色區域建立顏色光環(已過期,用BlurEffect代替)

從WPF4開始,這些類都已通過時了,用Effect及其子類來代替,這意味着你在WPF4環境中使用上面的類是沒有效果的。咱們來看看新的Effect子類:

注意:這三個類是派生自Effect類,並非派生自BitmapEffect類,BitmapEffect和Effect是同級別的類。

WPF4使用Effect代替BitmapEffect的緣由有這麼幾點:

  • 使用的是UI線程渲染,而不是渲染線程,因此會致使性能降低
  • 不支持像素着色器
  • 使用的非託管代碼實現,一來安全性要求高,在瀏覽器中沒法使用;二來自定義不方便,須要使用mileffects.h等一些非託管API來實現

能夠經過繼承ShaderEffect抽象類來實現自定義的像素着色器,須要使用HLSL來編寫着色器,而後編譯成.ps文件供WPF使用,在Codeplex上已經有了這樣的第三方的自定義效果類。

這裏隨便講一下WritableBitmap類:Image沒有提供建立編輯位圖的方法,這是WritableBitmap類的由來。先以一張圖來講明繼承關係:

首先須要說明的是,並非全部的像素格式都是支持寫入的,常見的支持寫入的像素格式有:Bgra3二、Pbgra32和Bgr32等。

WriteableBitmap bitmap = new WriteableBitmap(80, 80, 96, 96, PixelFormats.Bgra32, null);
            int stride = 80 * bitmap.Format.BitsPerPixel / 8;//計算跨距,每行像素數據須要的字節數量
            /*
            //1.逐像素法
            byte[] colors = { 0, 255, 0, 255 }; //按照B,G,R,G的順序,此處表示綠色
            for (int i = 0; i < 80; i++)
            {
                for (int j = 0; j < 80; j++)
                {
                    Int32Rect rect = new Int32Rect(i, j, 1, 1);
                    bitmap.WritePixels(rect, colors, stride, 0);
                }
            }
             * */
            //2.一次寫入法
            Int32Rect rect1 = new Int32Rect(0, 0, 80, 60);
            byte[] colors1 = new byte[80 * 60 * bitmap.Format.BitsPerPixel / 8];
            for (int i = 0; i < 60; i++)//行遍歷
            {
                for (int j = 0; j < 80; j++)//列遍歷
                {
                    int offset = (j + 80 * i) * bitmap.Format.BitsPerPixel / 8;//計算像素的偏移量
                    colors1[offset] = 0;
                    colors1[offset + 1] = 255;
                    colors1[offset + 2] = 0;
                    colors1[offset + 3] = 255;
                    bitmap.WritePixels(rect1, colors1, stride, 0);
                }
            }
            Image img = new Image();
            img.Source = bitmap;
            img.ToolTip = "Image";
            grid.Children.Add(img);
            img.SetValue(Grid.RowProperty, 3);
            img.SetValue(Grid.ColumnProperty, 1);
View Code

對於大片像素的寫入,一次寫入法的效率顯然要好不少。

相關文章
相關標籤/搜索