XNA предполагает вывод текста только заранее подготовленными растровыми шрифтами. И это правильно. Быстро, не зависимо от ОС, предсказуемые размеры текста.
В моём случае требовалось совершенно противоположное. Произвольный выбор гарнитуры и размера шрифта, и низкие требования к производительности. Задача оказалась довольно трудной. Информации в интернете оказалось мало и она была крайне разрознена.
Условия: 2D приложение, пользователь в любое время должен иметь возможность изменить гарнитуру, стиль и размер шрифта.
Способ решения довольно прост. Создаём битмап с нужным текстом, создаём из него текстуру и отображаем её.
Битмап создаём средствами GDI.
Преобразуем битмап в текстуру
И отображаем текстуру
В результате получаем текст с рваными краями, полное отсутствие сглаживания.

Конвертация битмапа в текстуру оказалась нетривиальной процедурой.
Метод найден здесь
Для корректной передачи альфа-канала требуется произвести дополнительные действия в методе TextureFromBitmap.
Сначала отрисовываем в текстуру данные о цветах битмапа, потом информацию об альфа-канале.
Теперь получаем удовлетворительный результат.

Исходный код
Проблемы:
1. Медленно. Ни в коем случае не надо использовать этот метод в играх и тем более на мобильных устройствах.
2. Не надежно. Нужных шрифтов может не оказаться в системе.
3. Желательно отслеживать какая часть текста будет отображаться на экране и обрезать невидимый текст. Максимальный размер текстуры 2048x2048. Если размер битмапа будет больше, то текстура будет создана максимального размера и потом растянута средствами видеокарты до нужного размера. Текст будет размытым.
Можно избавиться от переконвертирования в PNG в TextureFromBitmap используя неуправляемый код. Пример можно посмотреть здесь.
В моём случае требовалось совершенно противоположное. Произвольный выбор гарнитуры и размера шрифта, и низкие требования к производительности. Задача оказалась довольно трудной. Информации в интернете оказалось мало и она была крайне разрознена.
Условия: 2D приложение, пользователь в любое время должен иметь возможность изменить гарнитуру, стиль и размер шрифта.
Способ решения довольно прост. Создаём битмап с нужным текстом, создаём из него текстуру и отображаем её.
Битмап создаём средствами GDI.
/// <summary> Draw text on bitmap </summary> /// <returns>Bitmap with text</returns> private System.Drawing.Bitmap Layout() { // Get font var font = new System.Drawing.Font( fontName, fontSize, fontStyle); // Get text size var bitmap = new System.Drawing.Bitmap( 1, 1 ); var graphics = System.Drawing.Graphics.FromImage( bitmap ); var textSize = graphics.MeasureString( text, font ); // Draw text on bitmap bitmap = new System.Drawing.Bitmap( (int) textSize.Width, (int) textSize.Height ); graphics = System.Drawing.Graphics.FromImage( bitmap ); graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; graphics.DrawString( text, font, new System.Drawing.SolidBrush( this.color ), 0f, 0f ); return bitmap; }
Преобразуем битмап в текстуру
/// <summary> Create texture from bitmap </summary> /// <param name="gdev">Graphic device</param> /// <param name="bmp">Bitmap</param> /// <returns>Texture</returns> private static Texture2D TextureFromBitmap(GraphicsDevice gdev, System.Drawing.Bitmap bmp) { Stream fs = new MemoryStream(); bmp.Save(fs, System.Drawing.Imaging.ImageFormat.Png); var tex = Texture2D.FromStream(gdev, fs); fs.Close(); fs.Dispose(); return tex; }
И отображаем текстуру
public void Draw() { var spriteBatch = new SpriteBatch( graphicsDevice ); spriteBatch.Begin(); spriteBatch.Draw( texture, coordinate, new Rectangle(0, 0, texture.Width, texture.Height), Color.White, 0f, new Vector2(0, 0), 1.0f / textureDownsizeRatio, SpriteEffects.None, 0); spriteBatch.End(); }
В результате получаем текст с рваными краями, полное отсутствие сглаживания.

Конвертация битмапа в текстуру оказалась нетривиальной процедурой.
Метод найден здесь
Для корректной передачи альфа-канала требуется произвести дополнительные действия в методе TextureFromBitmap.
Сначала отрисовываем в текстуру данные о цветах битмапа, потом информацию об альфа-канале.
/// <summary> Create texture from bitmap </summary> /// <param name="gdev">Graphic device</param> /// <param name="bmp">Bitmap</param> /// <returns>Texture</returns> private static Texture2D TextureFromBitmap(GraphicsDevice gdev, System.Drawing.Bitmap bmp) { Stream fs = new MemoryStream(); bmp.Save(fs, System.Drawing.Imaging.ImageFormat.Png); var tex = Texture2D.FromStream(gdev, fs); fs.Close(); fs.Dispose(); // Setup a render target to hold our final texture which will have premulitplied alpha values var res = new RenderTarget2D(gdev, tex.Width, tex.Height); gdev.SetRenderTarget(res); gdev.Clear(Color.Black); // Multiply each color by the source alpha, and write in just the color values into the final texture var blendColor = new BlendState { ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue, AlphaDestinationBlend = Blend.Zero, ColorDestinationBlend = Blend.Zero, AlphaSourceBlend = Blend.SourceAlpha, ColorSourceBlend = Blend.SourceAlpha }; var spriteBatch = new SpriteBatch(gdev); spriteBatch.Begin(SpriteSortMode.Immediate, blendColor); spriteBatch.Draw(tex, tex.Bounds, Color.White); spriteBatch.End(); // Now copy over the alpha values from the PNG source texture to the final one, without multiplying them var blendAlpha = new BlendState { ColorWriteChannels = ColorWriteChannels.Alpha, AlphaDestinationBlend = Blend.Zero, ColorDestinationBlend = Blend.Zero, AlphaSourceBlend = Blend.One, ColorSourceBlend = Blend.One }; spriteBatch.Begin(SpriteSortMode.Immediate, blendAlpha); spriteBatch.Draw(tex, tex.Bounds, Color.White); spriteBatch.End(); // Release the GPU back to drawing to the screen gdev.SetRenderTarget(null); return res; }
Теперь получаем удовлетворительный результат.

Исходный код
Проблемы:
1. Медленно. Ни в коем случае не надо использовать этот метод в играх и тем более на мобильных устройствах.
2. Не надежно. Нужных шрифтов может не оказаться в системе.
3. Желательно отслеживать какая часть текста будет отображаться на экране и обрезать невидимый текст. Максимальный размер текстуры 2048x2048. Если размер битмапа будет больше, то текстура будет создана максимального размера и потом растянута средствами видеокарты до нужного размера. Текст будет размытым.
Можно избавиться от переконвертирования в PNG в TextureFromBitmap используя неуправляемый код. Пример можно посмотреть здесь.
