4

I am using GDI+ to draw some white text outlined with black.

I tried using a graphics path but I got poor results (especially using small text sizes) so I thought about rendering black text at the 8 pixel positions around the text and then drawing white over the top.

The result is the sort of thing I want, but the code doesn't seem all that efficient. Is there a better way of achieving the same result?

My Code:

Private _whiteFont As New Font("Segoe UI", 8)
Private _blackFont As New Font("Segoe UI", 8)

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    DrawTextWithOutline("12", New Point(18, 9))
End Sub

Private Sub DrawTextWithOutline(ByVal text As String, ByVal pt As Point)
    Using g As Graphics = Me.CreateGraphics
        g.DrawString(text, _blackFont, Brushes.Black, pt.X - 1, pt.Y) 'left
        g.DrawString(text, _blackFont, Brushes.Black, pt.X, pt.Y + 1) 'top
        g.DrawString(text, _blackFont, Brushes.Black, pt.X + 1, pt.Y) 'right
        g.DrawString(text, _blackFont, Brushes.Black, pt.X, pt.Y + 1) 'bottom
        g.DrawString(text, _blackFont, Brushes.Black, pt.X - 1, pt.Y - 1) 'top left
        g.DrawString(text, _blackFont, Brushes.Black, pt.X - 1, pt.Y + 1) 'bottom left
        g.DrawString(text, _blackFont, Brushes.Black, pt.X + 1, pt.Y - 1) 'top right
        g.DrawString(text, _blackFont, Brushes.Black, pt.X + 1, pt.Y + 1) 'bottom right
        g.DrawString(text, _whiteFont, Brushes.White, pt)
    End Using
End Sub

Sample Result: enter image description here

Matt Wilko
  • 26,994
  • 10
  • 93
  • 143
  • 1
    I don't think there's a better way in GDI+, but if you were to use the newer System.Windows.Media namespace, you could follow these instructions from MSDN entitled "How to: Create Outlined Text": http://msdn.microsoft.com/en-us/library/ms745816.aspx Their method appears to be less efficient than yours though. – andrewpm Oct 17 '13 at 14:30
  • try drawing the font a slightly bigger font in background, then draw smaller font in front so you'd just be making two g.DrawString calls instead of 8? – Dai Bok Oct 17 '13 at 14:32
  • @DaiBok - I tried that initially (and also drawing bold text first) but the difference between the two can be more or less than one pixel making it impossible to 'centre' the white text – Matt Wilko Oct 17 '13 at 14:33
  • Yes, and I am also sure that it may vary from font to font. If it needs to be dynamic, it may be a bit more work, but if you can stick to a single font, maybe you can find an optimum - possible combination of font size and weight (bold size 10)?. For example Private _whiteFont As New Font("Segoe UI", 8) Private _blackFont As New Font("Segoe UI", 10) – Dai Bok Oct 17 '13 at 14:38
  • 1
    Check out [this solution](http://stackoverflow.com/questions/14077523/how-to-drawing-text-with-outline-onto-images), which first [adds the string](http://msdn.microsoft.com/en-us/library/ms142533.aspx) to a [`GraphicsPath`](http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.graphicspath.aspx) and then draws and fills that path separately. – O. R. Mapper Oct 17 '13 at 14:47
  • Re DaiBok's idea...it might work if you created the fonts specifying GraphicsUnit.Pixel vs .Point. That might then require 2 calls to `MeasureString` to position each. Looks good though! – Ňɏssa Pøngjǣrdenlarp Oct 17 '13 at 14:51
  • @MattWilko: Sorry, somehow I missed that being mentioned ... I suppose you have already tried filling after drawing and using a less-than-one-pixel pen thickness for drawing? – O. R. Mapper Oct 17 '13 at 15:04

1 Answers1

4

You didn't post your GraphicsPath attempt, but this version seems to draw ok. I used a bold font to try to get more white space inside and a 2-pixel black pen to get the outline:

e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
Using gp As New GraphicsPath, _
      f As New Font("Segoe UI", 8, FontStyle.Bold), _
      p As New Pen(Brushes.Black, 2)
  gp.AddString(text, f.FontFamily, f.Style, f.Size + 3, New Point(100, 40), _
               StringFormat.GenericTypographic)
  e.Graphics.DrawPath(p, gp)
  e.Graphics.FillPath(Brushes.White, gp)
End Using

Beauty is in the eye of the beholder:

enter image description here

LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • +1 Thanks for taking the time Lars; your solution seems better than my graphicspath attempt, but I think I prefer my version looking at them side by side. I'll accept this if no better solution is posted. – Matt Wilko Oct 17 '13 at 16:04
  • I like this very much - I was trying to do outlines a while back and temporarily gave up. One odd thing is that darkdark outline colors like Black and BlueViolet seem to result in a smaller outline than DarkGray. Its an optical illusion, but looks odd. (I am drawing on a bitmap and not a form). – Ňɏssa Pøngjǣrdenlarp Oct 19 '13 at 01:23