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

时间:2014-07-15 22:54:27

标签: c# out-of-memory draw

我在C#中使用Visual Studio 2013 Express从头开始制作游戏。

在运行此代码时遇到内存不足错误,一切顺利进行:

 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参数有多个字符时,我得到了错误。

这是我使用here的精灵。

2 个答案:

答案 0 :(得分:2)

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

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

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

注意/编辑:注释中提到的一个重要项目是,您可能希望尝试加载一次图像 - 可能是在加载表单时或初始化期间。这有两个积极的影响。第一个是您不必担心内存泄漏,第二个是您将显着加快渲染循环(从磁盘加载图像很慢)。

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

答案 1 :(得分:0)

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

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

还有一种方法可以在您不再需要字典时或在您想要加载不同的字体图像文件时正确处理字典。

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); }
}