📜 ⬆️ ⬇️

XNA: Display text in system fonts

XNA assumes text output only in advance prepared raster fonts. And it is right. Fast, regardless of OS, predictable text sizes.
In my case, the exact opposite was required. Arbitrary headset and font size selection, and low performance requirements. The task was quite difficult. Information on the Internet was not enough and it was extremely fragmented.


Conditions: 2D application, the user at any time should be able to change the headset, font style and size.

The solution is quite simple. Create a bitmap with the desired text, create a texture from it and display it.
')
Bitmap create using 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; } 


Convert bitmap to texture
 /// <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; } 


And display the texture
 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(); } 


As a result, we get a text with torn edges, the complete absence of anti-aliasing.


Converting bitmap to texture was not a trivial procedure.
Method found here

For correct transfer of the alpha channel, it is required to perform additional actions in the TextureFromBitmap method.
First, we draw the data about the colors of the bitmap into the texture, then the information about the alpha channel.
 /// <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; } 


Now we get a satisfactory result.


Source

Problems:
1. Slowly. In no case should not use this method in games and especially on mobile devices.
2. Not reliable. The necessary fonts may not be in the system.
3. It is advisable to track which part of the text will be displayed on the screen and cut off the invisible text. The maximum size of the texture is 2048x2048. If the size of the bitmap is larger, the texture will be created to the maximum size and then stretched by the video card to the desired size. The text will be blurred.

You can get rid of the conversion to PNG in TextureFromBitmap using unmanaged code. An example can be found here .

Source: https://habr.com/ru/post/135851/


All Articles