Groups | Blog | Home
all groups > dotnet drawing api > may 2006 >

dotnet drawing api : Drawing text to a Bitmap has horrible looking output


Kellie Fitton
5/27/2006 12:36:09 PM
Hi,

You can use the following GDI+ methods to improve the output:

Graphics::SetInterpolationMode
Graphics::SetSmoothingMode
Graphics::SetPixelOffsetMode

You can use the GDI graphic drawing to create a bitmap image,
then use the following APIs to produce an antialias effect:

SetStretchBltMode() using HALFTONE
StretchBlt()

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDIPlus/GDIPlusReference/Classes/GraphicsClass/GraphicsMethods/SetInterpolationMode.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDIPlus/GDIPlusreference/classes/graphicsclass/graphicsmethods/setsmoothingmode.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp/GDIPlus/GDIPlusReference/Classes/GraphicsClass/GraphicsMethods/SetPixelOffsetMode.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6cth.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_9cok.asp

Hope these suggestions helps,

Kellie.
Kellie Fitton
5/27/2006 10:02:28 PM
Hi,

The GDI and the Win32 APIs such as ExtTextOut() does not provide
any antialias/text smoothing, only GDI+ support font smoothing,
you might want to use the following API to enable smoothing mode:

SystemParametersInfo() using SPI_SETFONTSMOOTHING,
SPI_SETFONTSMOOTHINGCONTRAST,
SPI_SETFONTSMOOTHINGTYPE

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/systemparametersinfo.asp

Kellie.
Lloyd Dupont
5/28/2006 12:00:00 AM
Thanks ever Vigilent Kellie ;-)

[quoted text, click to view]

Lloyd Dupont
5/28/2006 12:00:00 AM
Thanks for all your links!

Unfortunatly none them help me fix the quality of the output of DrawString()
on a Bitmap.

I did some more test and discovered that if I paint the Background of my
Bitmap first, or use a 24bpp bitmap g.DrawString() yield much better result.

However my problem lies mainly with GDI drawing (ExtTextOut) as it is the
API I should use.

After doing some more test I come to realize that, perhaps, a Bitmap's HDC
doesn't support the same quality hint as the screen HDC.
it's why CreateFont(... CLEARTYPE_QUALITY ...) doesn't work for the Bitmap
HDC?
(while CreateFont(... ANTIALIASED_QUALITY ...) does)

Do you have any tip on how to test, wether or not, the current device
support the given font quality hint?
So that I could gracefully drop one level?

Lloyd Dupont
5/28/2006 12:00:00 AM
I solved it!

In my MC++ wrapper around HDC, when it's created from a Bitmap, I maximize
the Font quality to ANTIALIASED_QUALITY and it all works nicely :-)

Lloyd Dupont
5/28/2006 12:00:00 AM
disregard the previous post ..
grmrmbll......

still don't work..
sigh....

Lloyd Dupont
5/28/2006 12:13:32 AM
I tryed to do a bitmap export in my app by creating a bitmap, drawing to it
and saving the file.
Something like that

void SaveToFile(string pngfile)
{
using(Bitmap bmp = new Bitmap(200, 200);
{
using(Graphics g = Graphics.FromImage(bmp))
g.DrawString("Hello!", this.Font, Brushes.Black, 0, 50);
bmp.Save(pngfile, ImageFormat.Png);
}
}

But while this work, the resulting Bitmap, I mean the text quality on the
Bitmap is appaling.
Whatever the SmoothingMode or TextRenderingHit I use.

Any tips to improve that?

Also, in fact, I'm doing a lot of GDI drawing, and the text output with HDC
/ ExtTextOut suffer from the same problem, whatever quality hint I used in
CreatFont(), any other GDI hint?

James Brown [MVP]
5/28/2006 12:06:37 PM
Standard Win32 CreateFont allows you to specify font-smoothing options with
no problem - certainly, I've never had any issue with creating a 'cleartype'
font, and then drawing with this font to an off-screen DC - font smoothing
works as expected in this case. Not tried doing this from GDI+ -> normal GDI
though.

James

--
Microsoft MVP - Windows SDK
www.catch22.net
Free Win32 Source and Tutorials


[quoted text, click to view]

Mick Doherty
5/28/2006 1:34:34 PM
When drawing to an offscreen bitmap using GDI you need to use a
CompatibleDC.

Bitmap bm = new Bitmap(width, height, ImageFormat.24bpp);
Graphics g = Graphics.FromImage(bm);
g.FillRect(width, height, Brushes.Color)
HDC hdc = g.GetHdc();

HDC cDC = CreateCompatibleDC(hdc);
HBitmap bmOriginal = SelectObject(cDC, bm.GetHBitmap());
DrawText(cDC, ....);
BitBlt(hdc, 0, 0, width, height, cDC, 0, 0, SRCCOPY);
SelectObject(cDC, bmOriginal);
DeleteDC(cDC);
....

--
Mick Doherty
http://dotnetrix.co.uk/nothing.html


[quoted text, click to view]

Mick Doherty
5/28/2006 7:06:44 PM

[quoted text, click to view]

Sounds like you either missed out the BitBlt, or the SelectObject
afterwards.

I am currently doing something very similar (in VB.Net though) and it works
exactly as expected.

[quoted text, click to view]

Personally I always like to know why something didn't work, even if I have
an acceptable workaround.
So in case it helps here are the exact methods that I am using along with an
example of their usage:
(Interop declarations not included)

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
'You sometimes need to use GDI since the control whos surface
'you're drawing to uses GDI to measure the rectangle and the GDI+
'DrawString method results in a string that's too long to fit.
Private Sub DrawGDIText(ByVal hdc As IntPtr, ByVal text As String, _
ByVal font As Font, ByVal bounds As Rectangle, _
ByVal backColor As Color, _
ByVal foreColor As Color, _
ByVal alignment As ContentAlignment)

SetTextColor(hdc, ColorTranslator.ToWin32(foreColor))

If backColor.Equals(Color.Transparent) Then
SetBkMode(hdc, TRANSPARENT)
Else
SetBkMode(hdc, OPAQUE)
SetBkColor(hdc, ColorTranslator.ToWin32(backColor))
End If

Dim hFont As IntPtr = font.ToHfont
Dim hOldFont As IntPtr = SelectObject(hdc, hFont)

Dim flags As Int32 = DT_SINGLELINE
Select Case alignment
Case ContentAlignment.TopLeft
flags += DT_TOP Or DT_LEFT
Case ContentAlignment.TopCenter
flags += DT_TOP Or DT_CENTER
Case ContentAlignment.TopRight
flags += DT_TOP Or DT_RIGHT
Case ContentAlignment.MiddleLeft
flags += DT_VCENTER Or DT_LEFT
Case ContentAlignment.MiddleCenter
flags += DT_VCENTER Or DT_CENTER
Case ContentAlignment.MiddleRight
flags += DT_VCENTER Or DT_RIGHT
Case ContentAlignment.BottomLeft
flags += DT_BOTTOM Or DT_LEFT
Case ContentAlignment.BottomCenter
flags += DT_BOTTOM Or DT_CENTER
Case ContentAlignment.BottomRight
flags += DT_BOTTOM Or DT_RIGHT
End Select

DrawText(hdc, text, -1, New RECT(bounds), flags)

SelectObject(hdc, hOldFont)
DeleteObject(hFont)

End Sub

'A bitmap needs to pass a CompatibleDC to the DrawText method.
Friend Sub DrawGDIText(ByVal bm As Bitmap, ByVal text As String, _
ByVal font As Font, ByVal bounds As Rectangle, _
ByVal backColor As Color, _
ByVal foreColor As Color, _
ByVal alignment As ContentAlignment)

Dim g As Graphics = Graphics.FromImage(bm)

If Not backColor.Equals(Color.Transparent) Then
Dim backBrush As New SolidBrush(backColor)
g.FillRectangle(backBrush, bounds)
backBrush.Dispose()
End If

Dim hDC As IntPtr = g.GetHdc
Dim cDC As IntPtr = CreateCompatibleDC(hDC)
Dim hBitmap As IntPtr = SelectObject(cDC, bm.GetHbitmap)

DrawGDIText(cDC, text, font, bounds, Color.Transparent, _
foreColor, alignment)

BitBlt(hDC, bounds.Left, bounds.Top, bounds.Width, bounds.Height, _
cDC, bounds.Left, bounds.Top, SRCCOPY)

SelectObject(cDC, hBitmap)
DeleteDC(cDC)

g.ReleaseHdc(hDC)
g.Dispose()

End Sub

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click

Dim bm As New Bitmap(someImage)

Dim testFont As New Font("Times New Roman", 36, _
FontStyle.Italic, GraphicsUnit.Point)

DrawGDIText(bm, "Sample", testFont, _
New Rectangle(Point.Empty, bm.Size), _
Color.FromArgb(200, 255, 128, 0), Color.White, _
ContentAlignment.MiddleCenter)

testFont.Dispose()
bm.Save("c:\Testing.png", Imaging.ImageFormat.Png)
bm.Dispose()

End Sub
/////////////////////////////////////////////////////////////////////////////

[quoted text, click to view]

:-) I'll add that to my feedback

--
Mick Doherty
http://dotnetrix.co.uk/nothing.html

Lloyd Dupont
5/28/2006 9:20:25 PM
[quoted text, click to view]
I think that's the issue.
Here is a simple weird test I have done.

I create HFONT with CreateFont(... CLEARTYPE_QUALITY ..)
I also SetMapMode(MM_TEXT) and SetWorldTransform(rotated and scaled world)

If I do
Image img = new Bitmap(width, height, ImageFormat.24bpp); <<== GDI work
better with 24bpp
HDC hdc = Graphics.FromImage(img).GetHdc();
DrawText(hdc, ....)

this produce nice text output, however if I do

Image img = new Bitmap(width, height, ImageFormat.24bpp);
Graphics g = Graphics.FromImage(img);
g.FillRect(widt, height, Brushes.Color) <<=== new
HDC hdc = g.GetHdc();
DrawText(hdc, ....)

the text is blurred ... :-(

Somehow some GDI+ operation have bad impact on my output.
And I can't removethe g.FillRect() as I have huge recursive mixed GDI/GDI+
code ....

However If I use CreateFont(.. ANTIALIAS_QUALITY ..) it works (I do get the
ANTIALIAS quality, not as nice as CLEARTYPE, but NOT blurred!)

Lloyd Dupont
5/29/2006 1:06:17 AM
I tried that, which seems reasonable though a tad acrobatic but, quite
strangely, I had no GDI output at all when doing that ?!?!

Anyway, I dropped this issue as it is quite marginal and I could make sure
to use ANTIALIAS_QUALITY in such case.

Regards,
Lloyd

PS: I upgraded to the latest version of Nothing and it really works just as
expected!
Without all these annoying spywares and adwares you find in Anything
nowadays!

"Mick Doherty"
<EXCHANGE#WITH@AND.REMOVE.SQUAREBRACKETS.[mdaudi100#ntlworld.com]> wrote in
message news:%23ZRiWMlgGHA.3900@TK2MSFTNGP05.phx.gbl...
[quoted text, click to view]

Ben Voigt
5/29/2006 9:55:25 AM
[quoted text, click to view]

I accidentally replied to your previous post before reading this one. This
statement confirms my suspicion concerning losing the alpha channel.

When your bitmap has an alpha channel (32-bit), the GDI+ text routines use
transparency to implement anti-aliasing. GDI doesn't understand the alpha
channel information... it uses only grayscale blends with the background
color.

If you draw the background first, it's opaque and you get blends with the
background with an opaque result.

[quoted text, click to view]

Lloyd Dupont
6/20/2006 12:00:00 AM
Well I solved it.
Instead this, which would have required too many bitmap copy anyway (As I
switch between GDI & GDI+ graphics all the time) I simply use the 'standart'
double buffering class (BufferedGraphics, which seems to works well in
windows forms) to draw to Bitmap too.
And it worked!

--
Regards,
Lloyd Dupont

NovaMind development team
NovaMind Software
Mind Mapping Software
<www.nova-mind.com>
"Mick Doherty"
<EXCHANGE#WITH@AND.REMOVE.SQUAREBRACKETS.[mdaudi100#ntlworld.com]> wrote in
message news:%23lEJ%23FogGHA.3916@TK2MSFTNGP04.phx.gbl...
[quoted text, click to view]

AddThis Social Bookmark Button