I'm attempting to use the Bebas font to render text to a Bitmap in C#.
Consider the following code that loads a .png from the filesystem to render overlaying text:
public Bitmap CreateCard(AccumulatedStats player, string template, bool includeGamerpic = false)
{
Bitmap imageCache = new Bitmap(template);
using (Graphics g = Graphics.FromImage(imageCache))
{
//AddAvatar(g, player);
AddGamertag(g, player);
//AddStats(g, player);
//AddLogo(g, player);
AddPrimaryPosition(g, player);
}
return imageCache;
}
The AddGamertag
method draws text at size 38 font, and renders correctly:
The AddPrimaryPosition
method draws text at size 12 font with unacceptable quality:
private void AddPrimaryPosition(Graphics g, AccumulatedStats player)
{
Brush bgText = new SolidBrush(Color.FromArgb(red: 238, green: 243, blue: 234));
var centerAlign = new StringFormat() { Alignment = StringAlignment.Center };
Font bebas = new Font("Bebas", 12);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.DrawString($"Lorem Ipsum\nYour Name Here", bebas, bgText, new PointF(601, 221), centerAlign);
//g.DrawString($"Lorem Ipsum\nYour Name Here", bebas, Brushes.Black, new PointF(843, 221), centerAlign);
}
Note that I am using SmoothingMode
and TextRenderingHint
to specify antialiasing.
Compare that to the quality from Paint.NET:
I found a question noting similar behaviour, where the accepted answer suggested that the area was not initialized. To remedy this I tried to draw a colored rectangle first before drawing the text, in hopes that the antialiasing would be able to sample from the rectangle behind the text:
private void AddPrimaryPosition(Graphics g, AccumulatedStats player)
{
Brush bgText = new SolidBrush(Color.FromArgb(red: 238, green: 243, blue: 234));
var centerAlign = new StringFormat() { Alignment = StringAlignment.Center };
Font bebas = new Font("Bebas", 12);
// Slight red offset for testing, should be 0
var islesBlue = new SolidBrush(Color.FromArgb(red: 200, green: 83, blue: 155));
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Rectangle rect = new Rectangle(499, 219, 203, 66);
g.FillRectangle(islesBlue, rect);
g.DrawRectangle(new Pen(islesBlue), rect);
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.DrawString($"Lorem Ipsum\nYour Name Here", bebas, bgText, new PointF(601, 221), centerAlign);
g.DrawString($"Lorem Ipsum\nYour Name Here", bebas, Brushes.Black, new PointF(843, 221), centerAlign);
}
Note that I also rendered the text further to the right, where I have a background that matches the color of the background where AddGamertag
draws the larger text.
I changed this method to set SmoothingMode=None
and TextRenderingHint=SingleBitPerPixel
, which looks different (indicating some antialiasing was happening previously) but also unacceptably poor:
Why is the antialiasing quality so poor at smaller font sizes? Is there a way to resolve this without resorting to larger text or a different font face?
I tried running the code on a different computer, and the resulting quality was significantly better:
The PC I ran this on initially is an old Intel-based Macbook running Windows 10 via Bootcamp, with an Intel Core i2xxx series CPU and Intel HD Graphics 4000.
The PC I re-ran this on is a Windows 10 desktop with an Intel Core i2500k CPU and an nvidia Geforce GTX970 GPU.
My guess is that it's a Windows GDI+ issue,as all other variables (source code, source image loaded as Bitmap, etc) have remained the same.
The explanation for why it may be a GDI+ issue is presented here: Graphics.DrawString vs TextRenderer.DrawText?Which can Deliver Better Quality
Consider using TextRenderer.DrawText