Three methods for drawing text with custom line spacing by using GDI +.

Source: Internet
Author: User
Tags drawtext

In. net, the graphics and text are drawn using GDI +.

In practice, it is common to draw multiple lines of text, and sometimes it is required to specify the line spacing of text when drawing multiple lines of text. For example:

Note: Because the graph is too large, only the graph on the left is truncated, and a small part on the right is not.

Above. There are 18 lines of text, 52 lines of text, and the line spacing is 1.5 characters.

The knowledge about GDI + is not described in detail here. The following describes how to implement the effect of the above figure. Three implementation methods are provided. And compare their implementation efficiency.

Because GDI + does not have the text line spacing concept, the three methods in this article are self-implemented line spacing.

Preparation: Customize a class clsdraw

There are several methods:

New (P as control) constructor, which constructs a text rendering environment based on P

Clear () Clear the canvas with the background color

Drawtext (text as string, P as point) draws text at the specified position P, which is generally a single line of text

Drawtext1 (text as string, P as point) draws text at the specified position P, which is a multiline text with a line break

Draw1 (text as string) draws text in the default position using method 1.

Draw2 (text as string) draws text in the default position using method 2.

Draw3 (text as string) draws the text in the default position using method 3.

Refresh (G as graphics) refreshes the content of the canvas to the specified control.

Have these attributes

The graphics object of the Mg graphics canvas.

Font object of the mfont font canvas

Foreground color of the canvas

Mbackcolor color the background color of the canvas

The bitmap object corresponding to the mbmp bitmap canvas.

The current drawing point of the MCP point canvas.

Text height corresponding to mtextheight integer font

The value of mlineheight is 1.5 times the text height in this article.

 

The following describes the implementation of three methods:

Method 1: Cut the text into multiple texts, call the drawtext method in sequence, and draw the text to the canvas. Simulate the effect of custom line spacing.

Public subDraw1 (ByvalTextAs string)

Clear ()

DimIAs integer, JAs integer, TS ()As string

J = int (text. Length/52)

RedimTS (J-1)

ForI = 0ToJ-1

TS (I) = text. substring (I * 52, 52)

Next

IfText. Length-J * 52 <> 0Then

Redim preserveTS (J + 1)

TS (J + 1) = text. substring (J * 52)

End if

ForI = 0ToTS. getupperbound (0)

Drawtext (TS (I ),NewPoint (3, 3 + I * mlineheight ))

Next

End sub

Public subDrawtext (ByvalTextAs string,ByvalPAs point)

MCP = P

Rendertext (text)

End sub

Private subRendertext (ByvalTextAs string)

Textrenderer. drawtext (Mg, text, mfont, MCP, mforecolor, textformatflags. nopadding)

End sub

Notes:

1. The text is drawn using the textrender class, which encapsulates GDI. The efficiency of drawing text is higher than that of graphics drawstring.

2. This method can also be thought of by everyone. However, efficiency is not flattering. There are two reasons: first, a large number of temporary strings are generated to split the text before drawing the text. Second, each call to drawtext, CLR actually does a lot of pinvoke work, and many of these work is repetitive. Check out its reflector.CodeIn each call, the MG, mfont, and mforecolor objects must be converted to GDI, drawn text, and then destroyed. In fact, the three objects are unchanged after multiple plotting, and multiple generation and destruction naturally affect the efficiency.

  

Method 2: Since the bottleneck of method 1 occurs when drawtext is called multiple times. If this method is called only once, can it improve efficiency? The answer is yes. This method splits the text into multiple strings and concatenates them with the line break (vbnewline. In this way, multiple lines of text can be drawn by calling drawtext once. However, this multi-line text does not have line spacing effect, and the next line of text is next to the last line of text. The method is to move the drawn text row by row to the specified position to produce the row spacing effect. The process of this method is divided into two steps. First, all texts are drawn on the secondary canvas, and the texts on the secondary canvas are painted to the specified position on the canvas. Generate the row spacing effect.

Public subDraw2 (ByvalTextAs string)

Clear ()

DimIAs integer, JAs integer, TS ()As string, Ts1As string

J = int (text. Length/52)

RedimTS (J-1)

ForI = 0ToJ-1

TS (I) = text. substring (I * 52, 52)

Next

IfText. Length-J * 52 <> 0Then

Redim preserveTS (J + 1)

TS (J + 1) = text. substring (J * 52)

End if

Ts1 = join (TS, vbnewline)

Drawtext1 (ts1,NewPoint (3, 3 ))

End sub

 Public subDrawtext1 (ByvalTextAs string,ByvalPAs point)

MCP = P

Rendertext1 (text)

End sub
Private subRendertext1 (ByvalTextAs string)

DimIAs integer, TrAsRectangle, tr1AsRectangle

Textrenderer. drawtext (Mg1, text, mfont, MCP, mforecolor, textformatflags. nopadding)

Tr. x = 3

Tr. Height = mtextheight

Tr. width = mbmp1.width

Tr1.x = 3

Tr1.height = mtextheight

Tr1.width = mbmp1.width

ForI = 0ToMaxlines-1

Tr. Y = 3 + I * mlineheight

Tr1.y = 3 + I * mtextheight

MG. drawimage (mbmp1, TR, tr1, graphicsunit. pixel)

Next

End sub

Public readonly PropertyMaxlines ()As integer

Get

ReturnINT (mbmp. height + mlineheight-mtextheight)/mlineheight)

End get

End Property

Notes:

1. The efficiency of this method is improved by about 20% compared with method 1.

2. There are still two problems. First, to split strings, a large number of temporary strings are generated. The second is to change the original method of calling drawtext multiple times to the method of calling drawimage multiple times, which improves the efficiency. However, pinvoke is used multiple times to generate and destroy a large number of objects, there is still a problem with efficiency.

 

Method 3: Use the gdipdrawdriverstring function. All our GDI + objects are actually encapsulated functions in gdiplus. dll, but some functions are not encapsulated. Gdipdrawdriverstring is one of them. Its vb2005 statement is

<Dllimport ("Gdiplus. dll", Charset: = charset. Unicode)> _

Friend shared FunctionGdipdrawdriverstring (ByvalGraphicsAsIntptr ,_

ByvalTextAs string,_

ByvalLengthAs integer,_

ByvalFontAsIntptr ,_

ByvalBrushAsIntptr ,_

ByvalPositions ()AsPointf ,_

ByvalFlagsAs integer,_

ByvalMatrixAsIntptr)As integer
End Function

This function cannot directly call graphics, Font, solidbrush, and other objects. Therefore, you have to encapsulate the following before calling:

Private shared subDrawdriverstring (ByvalGraphicsAsGraphics ,_

ByvalTextAs string,ByvalFontAsFont ,_

ByvalBrushAsBrush,ByvalPositions ()AsPointf)

Drawdriverstring (graphics, text, Font, brush, positions, nothing)

End sub

Private shared subDrawdriverstring (ByvalGAsGraphics ,_

ByvalTAs string,ByvalFAsFont ,_

ByvalBAsBrush,ByvalP ()AsPointf,ByvalMAsMatrix)

If(GIs nothing)Then throw newArgumentnullexception ("graphics ")

If(TIs nothing)Then throw newArgumentnullexception ("text ")

If(FIs nothing)Then throw newArgumentnullexception ("font ")

If(BIs nothing)Then throw newArgumentnullexception ("brush ")

If(PIs nothing)Then throw newArgumentnullexception ("positions ")

DimFieldAsFieldinfo

Field =GetType(Graphics). getfield ("nativegraphics", bindingflags. InstanceOrBindingflags. nonpublic)

DimHgraphicsAsIntptr = field. getvalue (g)

Field =GetType(Font). getfield ("nativefont", bindingflags. InstanceOrBindingflags. nonpublic)

DimHfontAsIntptr = field. getvalue (f)

Field =GetType(Brush). getfield ("nativebrush", bindingflags. InstanceOrBindingflags. nonpublic)

DimHbrushAsIntptr = field. getvalue (B)

DimHmatrixAsIntptr = intptr. Zero

If(NotMIs nothing)Then

Field =GetType(Matrix). getfield ("nativematrix", bindingflags. InstanceOrBindingflags. nonpublic)

Hmatrix = field. getvalue (m)

End if

DimResultAs integer= Gdipdrawdriverstring (hgraphics, T, T. length, hfont, hbrush, P, driverstringoptions. cmaplookup, hmatrix)

End sub

Private EnumDriverstringoptions

Cmaplookup = 1

Vertical = 2

Advance = 4

Limitsubpixel = 8

End Enum

The above code is a piece of C # code I transplanted online. During this period, I also encountered traps. Let's take a look at the two articles: "using GDI + to draw spacious text" and "GetType (vb2005 )".ArticleI know what a trap is.

When calling this function, you must pass an array of pointf to specify the position where each character is drawn. In addition, this position refers to the position in the lower left corner of the character. So I do not need to split the string during the call, but calculate the position of each character.

Public subDraw3 (ByvalTextAs string)

Clear ()

DimIAs integer, TP ()AsPointf

RedimTP (text. Length-1)

ForI = 0ToText. Length-1

TP (I). x = (I mod 52) * 16 + 3

TP (I). Y = 3 + int (I/52) * mlineheight + 12

Next

Drawdriverstring (Mg, text, mfont, new solidbrush (mforecolor), TP)

End sub

I wrote a test code to test the efficiency of the three methods. The Code is as follows:

Private subButton#click (ByvalSenderAsSystem. object,ByvalEAsSystem. eventargs)HandlesButton1.click

DimMgdiAs newClsdraw (Panel1)

DimTs1As string= My. Computer. filesystem. readalltext ("t1.txt", system. Text. encoding. Default)

DimT1As integer, T2As integer

T1 = environment. tickcount

Mgdi. draw1 (ts1)

T2 = environment. tickcount

Debug. Print (T2-t1)

T1 = environment. tickcount

Mgdi. draw2 (ts1)

T2 = environment. tickcount

Debug. Print (T2-t1)

 

T1 = environment. tickcount

Mgdi. draw3 (ts1)

T2 = environment. tickcount

Debug. Print (T2-t1)

 

Panel1.invalidate ()

End sub

The effect of the text in the image is tested ten times. the time consumed by the three methods is as follows (unit: milliseconds ):

First time: Method 1: 125; Method 2: 94; Method 3: 15
Second: Method 1: 125; Method 2: 78; Method 3: 16
Third time: Method 1: 125; Method 2: 79; Method 3: 15
Fourth: Method 1: 110; Method 2: 93; Method 3: 16
Fifth: Method 1: 125; Method 2: 78; Method 3: 16
Sixth: Method 1: 125; Method 2: 78: method 3: 16
Seventh: Method 1: 125; Method 2: 78; Method 3: 15
Eighth time: Method 1: 125; Method 2: 78; Method 3: 16
Ninth time: Method 1: 110; Method 2: 94; Method 3: 15
Tenth time: Method 1: 109; Method 2: 94; Method 3: 15

It can be seen that method 1 and method 2 are inefficient because they need to split the string and call the GDI + method repeatedly. method 2 is slightly more efficient because the drawimage method is used. Method 3 does not split the string or only calls a single GDI + method, which is highly efficient. Leave the first two methods far behind.

If you have any good ideas, please contact us and learn from each other.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.