Groups | Blog | Home
all groups > dotnet drawing api > february 2004 >

dotnet drawing api : ExtTextOutW and VB.Net


Ken Tucker [MVP]
2/3/2004 7:04:09 AM
Hi,


The graphics class has a gethdc method. You could create a new
bitmap and create a graphics object to the bitmap and get its hdc that way.
Wouldn't it just be easier to use the drawstring method of the graphics
class.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawinggraphicsclassgethdctopic.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawinggraphicsclassdrawstringtopic4.asp

Dim bm As New Bitmap(200, 200)

Dim g As Graphics = Graphics.FromImage(bm)

g.FillRectangle(SystemBrushes.Control, 0, 0, 200, 200)

g.DrawString("Hello, world", Me.Font, Brushes.Black, 10, 10)

picGraphics.Image = bm

g.Dispose()




Ken
----------------------------

"Benjamin Lukner" <Benjamin.Lukner_at_trinomix.de@mp3mounter.de> wrote in
message news:uzyBBPk6DHA.3860@tk2msftngp13.phx.gbl...
[quoted text, click to view]

Benjamin Lukner
2/3/2004 11:54:09 AM
Hi again!

I've spent a whole day on tryining to get ExtTextOutW to work.

I've found out a lot of things, and it works in VB6 with ExtTextOutA,
but I still have problems on VB.Net.

It's not possible to pass the font to the function.
In VB6, the function uses the font and colors of the PictureBox control.
In VB.Net, it always writes Sans Serif with white background.
I can change the text color with SetTextColor.

VB.Net does not provide the hDC like VB6, so I used GetDC with the
handle of my control.
Nevertheless I'd like to use a double buffered control and print on a
bitmap, not on the control.

Does anyone have an idea how to do this?

My code trials so far:

Inherits Control

Private Structure RECT
Public Left As Int32
Public Top As Int32
Public Right As Int32
Public Bottom As Int32
End Structure

Const ETO_GRAYED = 1
Const ETO_OPAQUE = 2
Const ETO_CLIPPED = 4

Private Declare Function ExtTextOut Lib "gdi32" Alias "ExtTextOutW" ( _
ByVal hdc As IntPtr, _
ByVal x As Int32, _
ByVal y As Int32, _
ByVal wOptions As Int32, _
ByRef lpRect As RECT, _
ByVal lpString() As Byte, _
ByVal nCount As Int32, _
ByRef lpDx As Int32 _
) As Int32

Private Declare Function SetTextColor Lib "gdi32" Alias "SetTextColor" ( _
ByVal hdc As IntPtr, _
ByVal crColor As Int32 _
) As Int32

Private Declare Function GetDC Lib "user32" Alias "GetDC" ( _
ByVal hwnd As IntPtr _
) As IntPtr

Private Declare Function ReleaseDC Lib "user32" Alias "ReleaseDC" ( _
ByVal hwnd As IntPtr, _
ByVal hdc As IntPtr _
) As Int32



Public Sub WriteText(ByVal sText As String, _
ByVal iRow As Integer, _
ByVal iCol As Integer)

Dim uRect As RECT
Dim iWidth As Int32
Dim iHeight As Int32
Dim iStruct() As Int32
Dim iIndex As Int32
Dim iLength As Int32
Dim hDC As IntPtr

Dim oFont As System.Drawing.Font = _
New System.Drawing.Font( _
"Courier New", _
nFontSize, _
FontStyle.Regular)

Dim oSize As System.Drawing.SizeF

oSize = _graphics.MeasureString("W", oFont, oOrigin, oFormat)

iLength = Len(sText)
iWidth = oSize.Width
iHeight = oSize.Height

uRect.Left = (iCol - 1) * iWidth
uRect.Top = (iRow - 1) * iHeight
uRect.Right = uRect.Left + iLength * iWidth
uRect.Bottom = uRect.Top + iHeight

ReDim iStruct(iLength)

For iIndex = 0 To iLength
iStruct(iIndex) = iWidth
Next

hDC = GetDC(Me.Handle)
SetTextColor(hDC, RGB(222, 0, 0))
ExtTextOut(hDC, uRect.Left, uRect.Top, ETO_CLIPPED, uRect, _
System.Text.Encoding.Unicode.GetBytes(sText), iLength, iStruct(0))
ReleaseDC(Me.Handle, hDC)

End Sub



Kind regards,

Benjamin Lukner
2/3/2004 3:19:09 PM
[quoted text, click to view]

LAST (so I hope) QUESTIONS:
- How do I select the font?? I still found no API call [IMPORTANT!].

hDC = _graphics.GetHdc
SetTextColor(hDC, ToRgb(System.Drawing.Color.DarkRed))
SetBkColor(hDC, ToRgb(System.Drawing.Color.SteelBlue))
ExtTextOut(hDC, uRect.Left, uRect.Top, ETO_CLIPPED, uRect, _
System.Text.Encoding.Unicode.GetBytes(sText), iLength, iStruct(0))
_graphics.ReleaseHdc(hDC)

Less important:
- How do I print text with transparent background? (SetBkColor to the
current backcolor didn't help. I did not state ETO_OPAQUE.)
- Which API call do I use to get the size of a single letter of a
specific font, rendition and size?

Anyway (questions above because I was afraid that they could be overseen
*g*) :

Thanks! :-)

I relied on the popup menu of .NET, but it doesn't show GetHdc and
ReleaseHdc...

And of course I'd like to use DrawString (what works fine on Pocket PC),
but it renders text with spaces of varying width.

http://support.microsoft.com/default.aspx?scid=kb;en-us;307208 mentions
in paragraph "How to display adjacent text" to use _GenericTypographic_
and _TextRenderingHintAntiAlias_ for editor-like applications, but I
still got spaces of 1 AND 2 pixels width in a printed text. I posted it
with subject 'DrawString and "real" fixed width' last week. The default
answer for this problem is to use GDI / ExtTextOut instead of GDI+ /
DrawString. So I did it.


Kind regards,

Benjamin Lukner
2/3/2004 3:55:20 PM
I just got an eMail explaining how to select the font via CreateFont and
SelectObject. Oh happy day! If someone needs it, tell me. At the moment
I'm still converting the example code from VB6 to VB.Net.

So there are only the less important questions left:

- How do I print text with transparent background? (SetBkColor to the
current backcolor didn't help. I did not state ETO_OPAQUE.)
- Which API call do I use to get the size of a single letter of a
specific font, rendition and size?


Kind regards,

Mattias Sjögren
2/3/2004 7:51:30 PM

[quoted text, click to view]

SetBkMode(hdc, TRANSPARENT)


[quoted text, click to view]

Not sure, but perhaps GetGlyphOutline does what you want.



Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
tejaz
2/4/2004 4:46:07 PM
Actually your use of ExTextOut will get more interesting if you try to double buffer using a Graphics object created from a Bitmap. This is a problem a I am still working to resolve, but first let me explain. If you do the following:

' Create Memory Bitmap (I think it makes a DIB or DIB Section) as the basis for the Graphics object
Dim buffer As Bitmap = New Bitmap(Panel1.Width, Panel1.Height, Drawing.Imaging.PixelFormat.Format32bppPArgb)

' Create the graphics object from the bitmap.
Dim g As Graphics = Graphics.FromImage(buffer)

' Get the device context to the graphics object, to use with Win32 Calls
Dim hDC As IntPtr = g.GetHdc()

' GetHFont is a function I wrote which returns a handle to a font (win32 style) from a .NET font object
' the font is created to be PROOF_QUALITY
Dim hFont As IntPtr = TextDrawControl1.GetHFont(New Font("Times New Roman", 16, FontStyle.Bold), hDC)

'Win32Wrapper just declares the Win32 calls (using the names from gdi32.dll and user32.dll)
Win32Wrapper.SelectObject(hDC, hFont)

' This is part of the problem as best as I can tell
Win32Wrapper.SetBkMode(hDC, Win32Wrapper.TRANSPARENT)

Win32Wrapper.SetBkColor(hDC, New Win32Wrapper.RGB(0, 0, 255))
Win32Wrapper.SetTextColor(hDC, New Win32Wrapper.RGB(255, 0, 0))

' Text is drawn to the buffer, but you'll notice that it looks jagged.
' closer inspection reveals that the antialiasing was not performed on a background
' consisting of the same color as whatever G had- even if you call g.FillRect....
' In summary, GDI seems to draw the text using a black background and then performs
' the antialiasing, which looks horrible on any other color of background.
Win32Wrapper.ExtTextOut(hDC, 0, 0, 0, Nothing, "Hello World", "Hello World".Length, Nothing)

g.ReleaseHdc(hDC)

' As would be necessary for a double buffer, blit the buffer into a visible control
Dim destG As Graphics = Panel1.CreateGraphics()
destG.DrawImageUnscaled(buffer, 0, 0)

destG.Dispose()
g.Dispose()
buffer.Dispose()

If you read through the comments in the above source you should understand the problem, GDI doesn't know what to use to antilias with when the BKMODE is transparent and the Bitmap was not created by GDI directly. My workaround to get nice, sharp text from the buffer is to NOT create the buffer in .NET but to use straight GDI. Instead do this to get the buffer:

Dim compHDC As IntPtr = Win32Wrapper.GetDC(Nothing) ' Uses the screen as a basis
Dim memDC As IntPtr = Win32Wrapper.CreateCompatibleDC(compHDC) 'Create Memory Device Context
Dim hBitmap As IntPtr = Win32Wrapper.CreateCompatibleBitmap(compHDC, Panel1.Width, Panel1.Height) 'Make BMP

Win32Wrapper.SelectObject(memDC, hBitmap) ' associate the bitmap with the memDC

then simly do the following to get your bitmap backed Graphics object:

Dim g As Graphics = Graphics.FromHdc(memDC)

Your text drawing will appear smooth, because somehow it is very capable of getting the correct background color information and thus performing antialiasing correctly.

AddThis Social Bookmark Button