WPF uses Direct2D1 drawing to draw basic graphics

This article tells you how to draw basic graphics in Direct2D1, including line segments, rectangles, and ellipses

This article is a series

The organization of this article refers to Direct2D , and I would like to express my gratitude to the great god.

Before we start, I will tell you why you need to use Direct2D. Although WPF is also based on DX for rendering, WPF has done a lot of compatibility processing, so there is no higher performance than using Direct2D directly. After testing, using all the code below, the CPU usage is almost 0%, because there is no layout, transparency and event processing, so the speed is very fast.

point

The point used in Direct2D is Point2F, and two floats are passed in, which is similar to Point.

Point2F is also a structure, so it is similar to the Point type

line segment

The line segment needs to use DrawLine, the signature of the method

    public void DrawLine(Point2F firstPoint 起始点 , Point2F secondPoint 终点, Brush brush 笔刷, float strokeWidth 线段宽度)

 public unsafe void DrawLine(Point2F firstPoint, Point2F secondPoint, Brush brush, float strokeWidth, StrokeStyle strokeStyle 线段样式)

So use the following method to draw a red line with a width of 2 at (10,10) (100,10)

            _renderTarget.DrawLine(new D2D.Point2F(10, 10), new D2D.Point2F(100, 10),
                _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 2);

The above code runs in the OnRendering method of the WPF Using Direct2D1 Drawing Getting Started article. In order to let everyone try the following code, it is recommended that you read this blog first.

About brushes will be said later

StrokeStyle

You can see that the last parameter of the line segment above is StrokeStyle, so how is this parameter created? There are many classes in Direct2D that cannot be created directly and need to be created using D2DFactory or RenderTarget. StrokeStyle needs to be created using D2DFactory.

Creating a StrokeStyle requires the parameter StrokeStyleProperties. There are two overloads for the construction of this class, one requires no parameters, and the other requires many parameters. See the code below.

public StrokeStyleProperties(CapStyle startCap, CapStyle endCap, CapStyle dashCap, LineJoin lineJoin, float miterLimit, DashStyle dashStyle, float dashOffset)

From the naming of the code, you can probably know the meaning of the StrokeStyleProperties parameter. Let's create a StrokeStyle without a constructor first. Please see the following code

            var strokeStyleProperties = new D2D.StrokeStyleProperties();

            var strokeStyle = d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10,10),new D2D.Point2F(100,10), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)),2, strokeStyle);

            _renderTarget.EndDraw();

It should be noted that the factory for creating strokeStyle needs to be the same as creating RenderTarget. If you use a different factory, the following exception will occur.

Microsoft.WindowsAPICodePack.DirectX.Direct2D1.Direct2DException:“EndDraw has failed with error: 一起使用的对象必须创建自相同的工厂实例。 (异常来自 HRESULT:0x88990012) Tags=(0,0).”

So you need to modify the code of WPF using Direct2D1 to draw introductory article, and write D2DFactory as a field

   public MainWindow()
        {
            InitializeComponent();

            CompositionTarget.Rendering += OnRendering;

            Loaded += (s, e) =>
            {
                var d2DFactory = D2D.D2DFactory.CreateFactory(D2D.D2DFactoryType.Multithreaded);

                var windowHandle = new WindowInteropHelper(this).Handle;
                var renderTarget = d2DFactory.CreateHwndRenderTarget(new D2D.RenderTargetProperties(),
                    new D2D.HwndRenderTargetProperties(windowHandle,
                        new D2D.SizeU((uint) ActualWidth, (uint) ActualHeight),
                        D2D.PresentOptions.RetainContents));

                _redBrush = renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1));

                _greenBrush = renderTarget.CreateSolidColorBrush(new D2D.ColorF(0, 1, 0, 1));

                _blueBrush = renderTarget.CreateSolidColorBrush(new D2D.ColorF(0, 0, 1, 1));

                _renderTarget = renderTarget;

                _d2DFactory = d2DFactory;
            };
        }

StrokeStyleProperties

What needs to be said about StrokeStyleProperties is each parameter.

From the name, you can see that StartCap and EndCap are the graphics at both ends of the line segment, optional parameters

  • Flat
  • Square
  • Round
  • Triangle

What is the specific representation, I will use the following example

Flat

flat

            var strokeStyleProperties = new D2D.StrokeStyleProperties();

            strokeStyleProperties.StartCap = D2D.CapStyle.Flat;
            strokeStyleProperties.EndCap = D2D.CapStyle.Flat;

            var strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10,10),new D2D.Point2F(100,10), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)),2, strokeStyle);

            _renderTarget.EndDraw();

Round

round

       float h = 10;

            strokeStyleProperties.StartCap = D2D.CapStyle.Round;
            strokeStyleProperties.EndCap = D2D.CapStyle.Round;
            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            h += 20;

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

Square

square

  strokeStyleProperties.StartCap = D2D.CapStyle.Square;
            strokeStyleProperties.EndCap = D2D.CapStyle.Square;
            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            h += 20;

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

Triangle

triangle


            strokeStyleProperties.StartCap = D2D.CapStyle.Triangle;
            strokeStyleProperties.EndCap = D2D.CapStyle.Triangle;
            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            h += 20;

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h), _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

DashStyle

If you need to draw a dashed line, you can use DashStyle, and the dashed line display is to use CapStyle


            strokeStyleProperties.DashStyle = D2D.DashStyle.DashDot;
            strokeStyleProperties.DashCap = D2D.CapStyle.Square;
            strokeStyleProperties.DashOffset = 2;

            h += 20;

            strokeStyle = _d2DFactory.CreateStrokeStyle(strokeStyleProperties);

            _renderTarget.BeginDraw();

            _renderTarget.DrawLine(new D2D.Point2F(10, h), new D2D.Point2F(100, h),
                _renderTarget.CreateSolidColorBrush(new D2D.ColorF(1, 0, 0, 1)), 5, strokeStyle);

            _renderTarget.EndDraw();

Everyone try it and you'll know

There is also the attribute LineJoin, which can not be done by line segments, but only by polylines, indicating how two line segments are linked.

rectangle

Use DrawRectangle to draw a rectangle. The parameter needs to be passed in RectF, and the floating point number of up, down, left, and right must be passed in.

            _renderTarget.DrawRectangle(new D2D.RectF(10, 10, 100, 100), brush, 10);

Rectangle has two overloads

    public void DrawRectangle(RectF rect, Brush brush, float strokeWidth)
    public unsafe void DrawRectangle(RectF rect, Brush brush, float strokeWidth, StrokeStyle strokeStyle)

The StrokeStyle of a rectangle is the same as that of a line segment.

oval

In fact, drawing a circle and an ellipse is the same, and the function for drawing a circle has two overloads

    public void DrawEllipse(Ellipse ellipse, Brush brush, float strokeWidth)
 public unsafe void DrawEllipse(Ellipse ellipse, Brush brush, float strokeWidth, StrokeStyle strokeStyle)

The Ellipse and brushes need to be created first.

To create an Ellipse, you need to give the center and two axes. Next, create an ellipse with the center at (100,100) and both axes being 50. It's actually a circle with a radius of 50.

            var ellipse = new D2D.Ellipse(new D2D.Point2F(100, 100), 50, 50);

That's it for drawing basic graphics.

So how to fill the graph? In fact, all Draws have a corresponding Fill function, except for line segments. So filling is to call the corresponding Fill function.

Try to run the program and see that the CPU is almost motionless at this time, because all the calculations are done on the GPU. However, the code in the program includes creating graphics, which is actually created on the CPU, but because it is very fast and requires almost no calculation, the time required is very short.

Word

The last is to tell you how to draw text.

To draw text, you need to use DirectWrite, you need to create a DWriteFactory before you can draw text.

There are multiple ways to draw text, because many of the required parameters cannot be created directly and need to be created using DWriteFactory, so the following code needs to be used first

            var dWriteFactory = DWriteFactory.CreateFactory();

There are multiple ways to create text

public void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush)

public void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush, MeasuringMode measuringMode)

public void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush, DrawTextOptions options)

public unsafe void DrawText(string text, TextFormat textFormat, RectF layoutRect, Brush defaultForegroundBrush, DrawTextOptions options, MeasuringMode measuringMode)


 public unsafe void DrawTextLayout(Point2F origin, TextLayout textLayout, Brush defaultForegroundBrush)

 public unsafe void DrawTextLayout(Point2F origin, TextLayout textLayout, Brush defaultForegroundBrush, DrawTextOptions options)

Because there are many parameters, you need to try it yourself

Write simple text below

You need to create a textFormat first, you need to tell which glyph to use, and the font size

            var textFormat = dWriteFactory.CreateTextFormat("宋体", 20);

The following is to draw text. Text wrapping can be used \n. For complex line wrapping, please use the text overloading method. I will not talk about it here.

            _renderTarget.BeginDraw();

            _renderTarget.DrawText("lindexi 本文所有博客放在 lindexi.oschina.io \n欢迎大家来访问\n\n这是系列博客,告诉大家如何在 WPF 使用Direct2D1", textFormat, new D2D.RectF(10, 10, 1000, 1000), brush);

            _renderTarget.EndDraw();

It needs to be said that Windows API Code Pack 1.1 has not been updated for a long time, and there are errors, so it is recommended to use SharpDX

参见:Using Direct2D with WPF - CodeProject

https://jeremiahmorrill.wordpress.com/2011/02/14/a-critical-deep-dive-into-the-wpf-rendering-system/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324602019&siteId=291194637