绘制自定义字体精灵时内存不足

本文关键字:内存不足 精灵 字体 自定义 绘制 | 更新日期: 2023-09-27 18:13:11

我一直在用Visual Studio 2013 Express在c#中从头开始制作游戏。

一切都很顺利,直到我运行这段代码时遇到内存不足错误:

 public static class ExtraGraphics
{
    static string[] chars = { " ", "!", "'"", "#", "$", "%", "&", "''", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "¬", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~" };
    static List<string> charList = chars.ToList<string>();
    public static void DrawFont(this Graphics g, Bitmap image, int width, int height, string str)
    {
        for (int i = 0; i < str.Length; i++)
        {
            g.DrawImage(image.Clone(new Rectangle(new Point(charList.IndexOf(str.Substring(i)) * width, 0), new Size(width, height)), System.Drawing.Imaging.PixelFormat.DontCare), new Point(i * width, 0)); //Line that errors.
        }
    }
}

我是这样称呼它的:

ExtraGraphics.DrawFont(canvas, new Bitmap(Image.FromFile("Assets''font.gif")), sliceWidth, sliceHeight, text);

这意味着要做的是从图像(自定义字体)中抓取字符条,然后将其拆分并根据输入字符串显示不同的字符,但是当str参数有多个字符时,我得到该错误。

这是我在这里使用的精灵

绘制自定义字体精灵时内存不足

这里的问题是每次调用DrawFont方法时都在使用内存。这是因为您每次都要创建一个新的Bitmap对象,这会不断地使用更多的内存。解决这个问题最简单的方法是改变你的绘制字体例程,在using语句中创建新的Bitmap对象。

using (var temp = new Bitmap(Image.FromFile("Assets''font.gif"))) {
    ExtraGraphics.DrawFont(canvas, temp, sliceWidth, sliceHeight, text);
}

您可能还必须对您的克隆采取相同的策略,以便完全修复内存泄漏。祝你好运!

注释/编辑:评论中提到的一个重要项目是,如果可能的话,你想尝试加载图像一次-可能是当你的表单加载或在你的初始化。这有两个积极的影响。首先,您不必担心内存泄漏,其次,您将大大加快渲染循环(从磁盘加载图像很慢)。

最后一件要考虑的事情是使用Matrix来绘制您正在寻找的图像的一部分,而不是试图克隆您想要绘制的像素。您还将从中看到巨大的性能优势。

下面是一段从字体图像中绘制字符字形的代码。

它首先准备一个Dictionary,该Dictionary将保留所有的字形位图以供以后重用。键是每个字形对应的字符。

当你不再需要字典或者当你想要加载一个不同的字体图像文件时,还有一个方法可以正确地处理字典。

private void Form1_Load(object sender, EventArgs e)
{
    prepareFont(fontImageFileName);
}

Dictionary<char, Bitmap> theFont = new Dictionary<char, Bitmap>();
string fontImageFileName = @"D:'yourfontimage.png";
string fontCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
int fontCharsPerLine = 7;
int fontCharsLines = 5;
int fontCharPixelWidth = 63;
int fontCharPixelHeight = 87;

void prepareFont(string fontImageFile)
{
    Rectangle destRect = new Rectangle(0, 0, fontCharPixelWidth, fontCharPixelHeight );
    using (Bitmap bmp = new Bitmap(fontImageFileName))
        for (int y = 0; y < fontCharsLines; y++)
        for (int x = 0; x < fontCharsPerLine; x++)
        {
            if (x + y * fontCharsPerLine < fontCharacters.Length)
            {
               char c = fontCharacters[x + y * fontCharsPerLine];
               Bitmap glyph = new Bitmap(fontCharPixelWidth, fontCharPixelHeight);
               using (Graphics G = Graphics.FromImage(glyph))
               {
                   G.DrawImage(bmp, destRect, fontCharPixelWidth * x, fontCharPixelHeight * y,
                        fontCharPixelWidth, fontCharPixelHeight, GraphicsUnit.Pixel);
                   theFont.Add(c, glyph);
               }
            }
        }
}

void disposeFont()
{
    foreach (char c in theFont.Keys) theFont[c].Dispose();
    theFont.Clear();
}
void drawFontString(Graphics G, string text, Point location)
{
    int offset = 0;
    foreach (char c in text)
    {
        if (theFont.Keys.Contains(c))
        {
            G.DrawImage(theFont[c], new Point(location.X + offset, location.Y));
            offset += fontCharPixelWidth;
        }
    }
}
private void button1_Click(object sender, EventArgs e)
{
    using (Graphics G = panel1.CreateGraphics())
    { drawFontString(G, textBox1.Text, Point.Empty); }
}