绘制位图后形成“滞后”

时间:2012-09-22 17:24:59

标签: c# winforms bitmap

我有一个相对简单的Windows应用程序。它使用了大约15个控件。

我的构造函数方法如下所示:

public Form1()
{
    InitializeComponent();
    this.Paint += this.Teken;
}

我的Teken方法(简化)如下所示:

private void Teken(object o, PaintEventArgs pea)
{
    if (start)
    {
        if (comboBox1.Text == "Basic")
            Bitmaps.DrawBitmap1(pea.Graphics, bitmapsize, max);
    }
}

我的DrawBitmap1方法将每个像素设置为某种颜色,然后使用Graphics.DrawImage方法绘制位图。

绘制位图后,控件变得非常滞后,在文本框中选择文本需要一秒多的时间。主要形式变得非常缓慢。

我可以理解缓慢绘制的位图,但我无法理解。

我的问题有一个简单的解决方案吗?

编辑:

这是Drawbitmap1代码:

public static void DrawBitmap1(Graphics gr, int bitmapsize, int max)
    {
        Bitmap bitmap1 = new Bitmap(bitmapsize, bitmapsize);

        for (int x = 1; x < bitmapsize; x++)
        {
            for (int y = 1; y < bitmapsize; y++)
            {
                int mandelnumber = CalculateMandel(x,y)

                if (Form1.mandelnumber == max) 
                    bitmap1.SetPixel(x, y, Color.Black);
                else if (Form1.mandelnumber %2 == 0) 
                    bitmap1.SetPixel(x, y, Color.White);
                else 
                    bitmap1.SetPixel(x, y, Color.Black);
            }
        }
        gr.DrawImage(bitmap1, 50, 100);
    }

mandelnumber是在程序的另一部分计算的变量。

EDIT2:

我运行了一个分析器,似乎方法CalculateMandel()在通过位图的每个像素后都不会停止运行。这怎么可能?

EDIT3:

当将鼠标悬停在文本框或按钮上时,再次调用DrawBitmap1函数。这怎么可能,我没有任何悬停事件,甚至没有TextChanged事件..

3 个答案:

答案 0 :(得分:1)

您的DrawBitmap1方法可能会从源的任何位图转换到绘图表面支持的任何位图。这可能是不同的颜色密度,不同的比特密度等(可能缩放?)。这通常需要一些额外的时间。

我建议改用PictureBox控件。

此外,如果您需要重新绘制想要显示的图像,那么我建议仅在图像更改时进行,而不是在绘制/绘制屏幕时 - 这将导致性能降低。 e.g。

pictureBox.Image = DrawBitmap1(gr, bitmapSize, max);

如果您要将图像每秒更改一次,我建议您不要使用SetPixel,因为您永远无法获得刷新屏幕所接受的性能。

关于在绘画上做的问题是,当发生疼痛并且位图需要改变时并不总是同一时间。您可以在需要更改位图时刷新表单,但是会降低表单的所有其他绘制的性能。

答案 1 :(得分:1)

你的表单有时间重新编译你运行的O(n2)算法非常昂贵,并且它的优化程度不是很好。

在我看来,你正在做很多只需要执行一次的冗余工作。您的DrawBitmap1函数会生成三个图像中的一个(对于您从未显示过的位图,是吗?)。

你也可以将if语句从内循环中删除,然后创建三个版本。没有必要多次检查相同的条件,因为它不会在迭代之间发生变化。

您可以创建一次图像并对其进行缓存。如果再次需要该图像,只需直接从函数返回。

最重要的是,GDI +中的GetPixel()SetPixel()函数非常慢。我的意思是,真的,真的慢。 .NET中的任何实际图像处理都可能使用直接指向内存的指针在不安全的上下文中完成。调用Bitmap.LockBits,它返回一个BitmapData对象,并使用Scan0 proeprty来检索指向内存缓冲区的指针。

答案 2 :(得分:1)

this.Paint += this.Teken;

这是错误的,每当您的应用需要重新绘制时,它会调用您的Teken例程;当您打开窗口时,当您调整窗口大小时,更改焦点时,移动另一个窗口,获取工具提示,更改分辨率或主题,几乎所有时间。实际上,大约Teken未被调用的唯一时间是CalculateMandel完成后您需要显示结果。您永远不会致电Invalidate,因此当您需要绘制结果时,您永远不会收到提醒。

... CalculateMandel()在通过位图的每个像素后不会停止运行。这怎么可能?

因为您每次在U.I中发生任何变化时,都会特别告诉它重新计算位图中的所有内容。一而再,再而三。除非只有你的位图改变,否则你不要告诉它重绘。完全落后了。这也将锁定你的U.I.因为你无用地重新计算你的位图,即使它不需要重绘。

将鼠标悬停在文本框或按钮上时,会再次调用
... DrawBitmap1函数。这怎么可能,我没有任何悬停事件

但其他事情确实如此;也许是一个工具提示。并且您已经告诉您的应用每次在您的应用中使用任何内容时都会拨打CalculateMandel()。 (位图除外)更改。

问题是你有两个完全不同的事情;你的应用需要重新绘制U.I.当某些内容发生变化时,您有一个计算(CalculateMandel()),您想要更新U.I.当它完成。这些是单独的事件,您必须单独处理它们。

首先,摆脱this.Paint += this.Teken;;这会导致你的问题而不会做你想做的事。

其次,创建一个BackgroundWorker,在CalculateMandel()事件中调用DoWork,在ProgressChanged事件中返回已完成的位图,然后调用yourWindowOrPanelOrWhatever.Invalidate()重绘新数据。