Problem: How to draw text into bitmap?

Hi,

I need to draw text into the bitmap. Resulting text should be "smooth" (antialiased) and international scripts should be handled properly. In addition, it should be fast. You might think, "That is simple, isn't it " Well, it is not!

There are at least two ways to do it:

1. Graphics.DrawString and

2. TextRenderer.DrawText.

Problem is that I cannot reach my targets with these two guys (who would say, huh ).

Graphics.DrawString is too SLOW, and it is not handling complex scripts properly they say. On the other hand, rendering quality is ok, and it is not depending on system-wide text smoothing setting.

TextRenderer.DrawText is the recommended text rendering engine in .NET2 and it is doing its job when rendering the text on the controls and system-wide text smoothing is active. Problem is, that it is rendering text into bitmaps always without text smoothing (independently of the system-wide text smoothing state) - at least I never managed to get smooth text out of it.

I need help desperately - I have spent already many days trying to figure out the solution without any success. I just cannot believe that in 2006 with all new technologies around it is such an issue to get smooth text into the bitmap. :(

Thanks for all your ideas beforehand.

Best regards,

Roman



Answer this question

Problem: How to draw text into bitmap?

  • sanmarcos

    Mike, thank you for your answers!
    Mike Danes wrote:

    Tried the example and got the same result, that is GDI is slower than GDI+. Surprise... Well, not completly surprised. Try drawing to the screen (create the Graphics context using Form.CreateGraphics method) and you'll have another suprise... GDI is faster than GDI+ . Why I'm not sure. One big difference between GDI and GDI+ is that GDI sits in kernel mode and talks directly to the video driver taking advantage of hardware acceleration (if there is such a thing for text, I don't know how text rendering can be hardware accelerated) and GDI+ works in user mode and ... umm... how does actually GDI+ writes to the screen I don't know but all it can do is talk to either GDI or DirectX and as far as I know it does not use hardware acceleration.

    You are probably right about hardware acceleration in case of on-screen GDI rendering - at least it makes sense. And yes, text rendering could be accelerated by video hardware.

    Mike Danes wrote:

    I'm don't know what you mean by different results in the case of Arabic. It's a bit slower with both and the look similar to me Did they look different to you Anyway it's known that GDI++ doesn't do quite well. See this blog for a comment about this: http://blogs.msdn.com/michkap/archive/2005/10/11/479438.aspx.

    Yes, they look different. As I said - GDI+ renders Arabic properly (RTL), and GDI - reversed (LTR). That's very strange.

    Mike Danes wrote:

    For your case maybe you should somehow cache the rendered text (bitmaps). Speaking of this, are you sure the way you render the text in Direct X is OK I mean 15 lines of text is not much for neither GDI or GDI++ but I suppose you render to a bitmap and create a texture from the bitmap (or update an existing one)... thing like this. If done wrong this operations can be quite expensive.

    15 lines is not a problem per se. The problem begins when I try to scroll that list as fast as possible - I am getting 100% CPU utilization (while using GDI+). That is not a very good thing, as my application has to react to certain things (like digital TV processing) in real time. With DirectX I do not hit this performance bottleneck, but I am not happy about DirectX way of handling text rendering - if you think GDI vs GDI+ is tough, you would be surprised to see how ugly is DirectX.

    I was thinking about some kind of compromise between those two - using DirectX for main graphics tasks while using GDI or GDI+ for text rendering part: render text into black and white bitmap, convert it to texture and then use pixel shader to render actual text in DirectX using that texture as alpha source (I hope this explanation is clear).

    Mike Danes wrote:

    And one last note... it needs the background color so it can do antialiasing correctly. You cannot do antialias if you don't know what's behind the text... that could be a good reason why GDI+ is slower on display... it may need to read pixels from the screen to do antialiasing and that's usually slow... reading from a memory bitmap is much faster.

    Could be... On the other hand it would be quite stupid not to use some kind of buffering (in-memory bitmap) for this purpose... I think, that the difference comes mostly from the fact that GDI text rendering is hardware accelerated, while GDI+ is not - they had to implement completely new text rendering engine to handle all those cool features like GenericTypographic and alpha blending.

    Anyway, now we have "best" of both worlds - slowness of GDI+ with its ability to render typographically correct text with alpha blending and inability to handle Uniscribe on one side, and speed of GDI (speed which speed ) with its ability to handle complex scripts (well, if you can figure out how to do itl; so far - no success), but nasty bugs in alpha handling on the other. If you ask my opinion, these two technologies are kind of orthogonal, i.e. one cannot get simple job (render complex scripts properly) done with either of them.

    Oh, well, nevermind... It seems Microsoft is sooooooooo busy marketing and developing future versions of the "new and improved" products (they could try to release "old and fixed" version for a change at least once), that such annoying things like FAST and CORRECT text rendering (done by bloody Macs for ages) are simply not fitting into their strategy. It sounds so funny when MS guys on DirectX-related forums propose people with text rendering problems to write their own text rendering engines :) - IMHO it would make sense if MS would fix errors in GDI, GDI+ and DirectX just once, and for all... Dreams.... dreams.....


  • zeeshan hirani

    Hello again!

    So, it seems that text rendering IS indeed quite tough subject...

    I just wonder, if anyone had any success in rendering for example Arabic with TextRenderer.DrawText I just downloaded little demo program from MSDN Magazine (don't have the link to it anymore, but article was about DrawString vs DrawText) and it has same behavior: DrawString - properly handles "mixed" content (string like "abc <then some Arabic> def"), DrawText - "reverses" Arabic sub-parts (so it renders "abc <cibarA emos neht> def"). Weird...

    (NOTE: I am not an "expert" in Arabic - I make my judgment based on what I see when I render it and assuming that Internet Explorer renders Arabic properly).

    Roman


  • fastdev

    Thanks once more!
    Regarding "reversed" Arabic - I would wonder what's wrong with it in my case... Have you tried to copy a bit of text from some Arabic web-page and then paste it into the string Does it still look like the source text in IE
    That's cool thing you figured about native bitmaps! In my previous snippet the speed was not changing with different pixel formats. Cannot test your code now (I am not in the office). Is GDI faster than GDI+ or kind of same
    By "black and white" I meant a true-color bitmap with black background and white text rendered with antialiasing, so that there are gradations of gray. I am not sure I would like to use ClearType for my UI - I am using quite big fonts and honestly saying AntiAlias looks better than ClearTypeGridFit.

  • jmckown

    I've tried with some text copied from BBC Arabic page. It looks the same, including some numbers in it. I'm using IE 7 Beta 2. Could be that other version/browser has problems copying the text

    GDI is 4-5 times faster than GDI+ with both Latin and Arabic text. And there's a good chance that il will be even faster if you PInvoke TextOut/DrawText instead of using TextRenderer. TextRenderer needs to configure the HDC every time you call it that is something like SetTextColor, SetBkColor, SelectObject(font). If you use PInvoke to GDI functions you can set these things just once and draw multiple strings.

    Indeed, with a large font size normal font antialiasing is good enough or even better than ClearType. ClearType was probably designed for small sizes like 8-12 pt where taking advantage of 3 RGB subpixels that compose a pixel means a lot. It's like you are renderint text on a device that has 288 dpi instead of 96 (of course, only horizontally).

     


  • Bob M

    I tried various combinations but I could not get Arabic text to render reversed. I wrote random text in Word using Arabic keyboard and copy/pasted in Visual Studio, I wrote directly in Visual Studio using Arabic keyboard (and in both cases I added some other text using English keyboard and some numbers). One time I got something strange: a number that in Word was displayed after some arabic text was displayed in front of it using TextRenderer AND DrawString, but the order of characters/digits was normal.

    DrawText has a flag: TextFormatFlags.RightToLeft, try it and see what happens... but for it reversed some words (words, not the characters in the words).

    And about GDI performance when rendering on a bitmap. Try this modified code:

    Public Class Form1
    Declare Auto Function CreateBitmap Lib "gdi32.dll" Alias "CreateBitmap" (ByVal width As Int32, ByVal Height As Int32, ByVal planes As UInt32, ByVal bpp As UInt32, ByVal bits As IntPtr) As IntPtr
    Declare Auto Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hdc As IntPtr) As IntPtr
    Declare Auto Function SelectObject Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal hbmp As IntPtr) As IntPtr

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim F As New Font("Arial", 48, FontStyle.Regular, GraphicsUnit.Pixel, 0)

    Dim hdc As IntPtr = CreateCompatibleDC(IntPtr.Zero)
    Dim hbmp As IntPtr = CreateBitmap(500, 500, 1, 32, IntPtr.Zero)
    SelectObject(hdc, hbmp)

    Dim GG As Graphics = Graphics.FromHdc(hdc)

    GG.Clear(Color.White)

    Dim DC As IDeviceContext = GG
    Dim ST As System.DateTime
    Dim ET As System.DateTime

    ST = System.DateTime.Now

    For i As Integer = 0 To 10000
    GG.DrawString("This is only a test", F, Brushes.Black, 10, 10, System.Drawing.StringFormat.GenericTypographic)
    'TextRenderer.DrawText(DC, "This is only a test", F, New Point(10, 100), Color.Black, Color.Transparent)
    Next

    ET = System.DateTime.Now
    Debug.Print("Time " & (ET - ST).ToString())

    GG.Dispose()

    Dim b As Bitmap = Bitmap.FromHbitmap(hbmp)
    Dim G As Graphics = CreateGraphics()
    G.DrawImageUnscaled(b, 0, 0, 500, 500)
    End Sub
    End Class

    You'll have a surprise... GDI isn't slower anymore... The reason Obviously GDI cannot draw onto a "native" GDI+ Graphics object nor onto a "native" GDI+ Bitmap so someone somewhere must do some heavy lifting to make them work together, probably something like CreateDC, CreateBitmap and maybe even some bitmap copying and pixel format conversion (for instance your example uses PARGB format, I wonder if GDI can handle this ) . Once you create the Graphics/Bitmap objects from GDI objects this problems go away.

    "render text into black and white bitmap, convert it to texture and then use pixel shader to render actual text in DirectX using that texture as alpha source (I hope this explanation is clear)."

    Now it depends what you mean by black and white bitmap. If it's 1 bpp bitmap then it will blow up any form of antialiasing done by DrawString/TextRenderer. If it's something like 8bpp grayscale this will probably blow up ClearType antialiasing because it needs the color components of the pixel. Take a screenshot of some ClearType black text on white background and you'll see that pixels at the border between text and background have various colors, they're not gray. In fact ClearType will be blown up by other things like rendering text horizontally and then displaying it vertically.


  • Travis Baldree

    Tried the example and got the same result, that is GDI is slower than GDI+. Surprise... Well, not completly surprised. Try drawing to the screen (create the Graphics context using Form.CreateGraphics method) and you'll have another suprise... GDI is faster than GDI+ . Why I'm not sure. One big difference between GDI and GDI+ is that GDI sits in kernel mode and talks directly to the video driver taking advantage of hardware acceleration (if there is such a thing for text, I don't know how text rendering can be hardware accelerated) and GDI+ works in user mode and ... umm... how does actually GDI+ writes to the screen I don't know but all it can do is talk to either GDI or DirectX and as far as I know it does not use hardware acceleration.

    I'm don't know what you mean by different results in the case of Arabic. It's a bit slower with both and the look similar to me Did they look different to you Anyway it's known that GDI++ doesn't do quite well. See this blog for a comment about this: http://blogs.msdn.com/michkap/archive/2005/10/11/479438.aspx.

    And look here at another blog post about GDI vs. GDI+ performance:

    http://blogs.msdn.com/cjacks/archive/2006/05/19/602021.aspx

    Unfortunately text rendering is not an easy task and when it comes to supporting different languages is even harder.

    Look here for another interesting post about differences between GDI and GDI+, it made my head spin a bit:

    http://windowsforms.net/articles/gdiptext.aspx

    For your case maybe you should somehow cache the rendered text (bitmaps). Speaking of this, are you sure the way you render the text in Direct X is OK I mean 15 lines of text is not much for neither GDI or GDI++ but I suppose you render to a bitmap and create a texture from the bitmap (or update an existing one)... thing like this. If done wrong this operations can be quite expensive.

    And one last note... it needs the background color so it can do antialiasing correctly. You cannot do antialias if you don't know what's behind the text... that could be a good reason why GDI+ is slower on display... it may need to read pixels from the screen to do antialiasing and that's usually slow... reading from a memory bitmap is much faster.


  • Sajitha Jose

    > Note that I have specified the background color of the text. Without it clear type won't work as expected (unless you are drawing on a black bakground).

    Great! Now the text IS smooth! (and yes - black background helps too). Very strange behaviour I would say, but nevermind...

    The only problem is that now I am even more confused.

    1. GDI+ is supposed to be SLOWER than GDI, yeah Well, the fact is, that in my test run Graphics.DrawString is about 5 times FASTER than TextRenderer.DrawText! 8-O

    2. I am getting totally different result from those guys if I am putting some Arabic text there. I am not an expert in Arabic to say which one is correct, but that is funny behaviour.

    Here is the snippet (don't kick me - it is in VB.NET, but it is not my fault :) - can be put in button_click or wherever...

    -------------------------------------

    Dim F As New Font("Arial", 48, FontStyle.Regular, GraphicsUnit.Pixel, 0)

    Dim B As New Bitmap(500, 500, Imaging.PixelFormat.Format32bppPArgb)

    Dim GG As Graphics = Graphics.FromImage(B)

    GG.Clear(Color.White)

    Dim DC As IDeviceContext = GG

    Dim ST As System.DateTime

    Dim ET As System.DateTime

    ST = System.DateTime.Now

    For i As Integer = 0 To 10000

    'GG.DrawString("This is only a test", F, Brushes.Black, 10, 10, System.Drawing.StringFormat.GenericTypographic)

    TextRenderer.DrawText(DC, "This is only a test", F, New Point(10, 100), Color.Black, Color.White)

    Next

    ET = System.DateTime.Now

    Debug.Print("Start " & ST & " " & ST.Millisecond)

    Debug.Print("End " & ET & " " & ET.Millisecond)

    GG.Dispose()

    Dim G As Graphics = Me.CreateGraphics()

    G.DrawImageUnscaled(B, 0, 0, 500, 500)

    -------------------------------------

    Tell me, that I am just plain stupid and have some obvious error in the code above... I cannot believe that text rendering is really SO BIG DEAL in Windows environment with video accelerators capable of rendering real-time movie effects.

    Best regards,
    Roman


  • Vince2006

    Roman H wrote:

    2. I am getting totally different result from those guys if I am putting some Arabic text there. I am not an expert in Arabic to say which one is correct, but that is funny behaviour.

    Just a quick update. Graphics.DrawString renders Arabic strings correctly (at least they look like the text I grabbed from the Internet Explorer to test it). TextRenderer.DrawText, on the other hand, is reversing Arabic text (not RTL, but LTR), which is wrong.

  • Carl Perkins

    There are two cases, to display Arabic text using DrawText:

    Unmirrored HDC (I mean normal , without mirroring styles). You need to set the flag + adjust the y-coordinates. As seen below:

    'We have to adjust the left of the drawing rectangle to draw the text in the correct location
    'NewLeft = FormWidth – (Left + Width)
    TextRenderer.DrawText(e.Graphics, " .", Me.Font, New Rectangle(Me.Width - (10 + 100), 10, 100, 100),
    SystemColors.ControlText, TextFormatFlags.RightToLeft)


    If your DC is mirrored (for example use RightToLeftLayout=true) then you don't need to add a flag or adjust the y-coordinates:

    TextRenderer.DrawText(e.Graphics, " .", Me.Font, New Rectangle(10, 10, 100, 100), SystemColors.ControlText)

    However, I'm not sure that you can displays indic numerals.


  • zooto68

    cgraus wrote:

    How much text are you drawing that DrawString is too slow

    User interface of my application is made with DirectX. Unfortunately, I do not like the way DirectX is handling the text (far various reasons) - that's why I started to look at alternatives.

    Now back to the amount of the text... Sometimes it could be quite much - for example, on some forms there could be few multicolumn lists with 15 quite long lines each. List scrolling takes 100% of CPU (it is really a problem - there are other important things happening on the machine at the same time) and is not too fast. To have some UI animations like focus change, or list scrolling, I will end up inventing complex frameworks to cache rendered text, update only parts of the form, etc. and even those are not bringing acceptable performace (already tried it).

    Just for reference: In DirectX implementation same scrolling takes only few percents of the CPU (but as I said I am not satisfied with the way DirectX handles the text).

    On top of that, speed is not the only concern - as I said I need complex scripts, and GDI+ is not good for those.


  • OldSam

    I've tried using TextRenderer to draw onto a Bitmap and it worked just fine.

    protected override void OnPaint(PaintEventArgs e)
    {
    base.OnPaint(e);

    using (Bitmap bmp = new Bitmap(100, 100))
    {
    using (Graphics g = Graphics.FromImage(bmp))
    TextRenderer.DrawText(g, "hello world", Font, Point.Empty, Color.Black, SystemColors.Control);

    e.Graphics.DrawImage(bmp, Point.Empty);
    }
    }

    Note that I have specified the background color of the text. Without it clear type won't work as expected (unless you are drawing on a black bakground).

    I'm curious about the speed. GDI+ is known to much slower at text rendering than GDI but still... 15 lines of text does not sound like much. You said that using Direct X text rendering CPU usage is very low and when using GDI++ the usage is 100%. But how does it look with GDI, even without clear type


  • CyrilDex

    How much text are you drawing that DrawString is too slow



  • Problem: How to draw text into bitmap?