在我的程序中,我正在编写一个基本的图像编辑器。其中一部分允许用户绘制一个矩形区域,我弹出一个显示该区域缩放约3倍(他们可以通过鼠标轮进一步调整)的显示。如果他们右击并拖动这个图像,它将在原始图像上移动缩放区域,基本上是一个放大镜。
问题是,即使在相对较小的位图上,我也会看到一些严重的性能问题。如果显示缩放区域的位图约为400x400,它仍在以鼠标所能移动的速度进行更新,并且非常平滑,但如果我鼠标滚动,缩放到450 x 450左右,它会立即开始分块,只有大约每秒2次更新,如果是这样的话。我不明白为什么这么小的加薪会带来这么大的性能问题.就像我达到了内部记忆极限之类的。这似乎并不重要的源位图的大小,正在被放大,只是大小的放大位图。
问题是我使用的是Graphics.DrawImage和PictureBox。看看这个站点,我发现这两种方法的性能都不是很好,但我对GDI的内部工作方式不太了解,无法提高我的速度。我希望你们中的一些人知道我的瓶颈在哪里,因为我可能只是以糟糕的方式使用这些工具,或者不知道有什么更好的工具可以代替它使用。
下面是我的鼠标事件和相关函数的一些片段。
private void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
else if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
// slide the zoomed part to look at a different area of the original image
if (zoomFactor > 1)
{
isMovingZoom = true;
// try saving the graphics object?? are these settings helping at all??
zoomingGraphics = Graphics.FromImage(displayImage);
zoomingGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
zoomingGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
zoomingGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
zoomingGraphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
}
}
}
private void pictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (isMovingZoom)
{
// some computation on where they moved mouse ommitted here
zoomRegion.X = originalZoomRegion.X + delta.X;
zoomRegion.Y = originalZoomRegion.Y + delta.Y;
zoomRegionEnlarged = scaleToOriginal(zoomRegion);
// overwrite the existing displayImage to prevent more Bitmaps being allocated
createZoomedImage(image.Bitmap, zoomRegionEnlarged, zoomFactor, displayImage, zoomingGraphics);
}
}
private void createZoomedImage(Bitmap source, Rectangle srcRegion, float zoom, Bitmap output, Graphics outputGraphics)
{
Rectangle destRect = new Rectangle(0, 0, (int)(srcRegion.Width * zoom), (int)(srcRegion.Height * zoom));
outputGraphics.DrawImage(source, destRect, srcRegion, GraphicsUnit.Pixel);
if (displayImage != originalDisplayImage && displayImage != output)
displayImage.Dispose();
setImageInBox(output);
}
// sets the picture box image, as well as resizes the window to fit
void setImageInBox(Bitmap bmp)
{
pictureBox.Image = bmp;
displayImage = bmp;
this.Width = pictureBox.Width + okButton.Width + SystemInformation.FrameBorderSize.Width * 2 + 25;
this.Height = Math.Max(450, pictureBox.Height) + SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height * 2 + 20;
}
private void pictureBox_MouseUp(object sender, MouseEventArgs e)
{
else if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
if (isMovingZoom)
{
isMovingZoom = false;
zoomingGraphics.Dispose();
}
}
}正如您所看到的,我并不是每次想要绘图时都声明一个新的位图,而是重用一个旧的位图(以及位图的图形对象,尽管我不知道重复调用Graphics.FromImage是否会带来很大的成本)。我尝试添加Stopwatches来测试我的代码,但我认为DrawImage将功能传递给另一个线程,因此函数声称完成得相对较快。当我不使用位图和图形对象时,我会尝试Dispose所有的位图和图形对象,并避免在MouseMove事件期间重复调用分配/释放资源。我正在使用PictureBox,但我不认为这是问题所在。
任何帮助,以加快这段代码或教我发生了什么在DrawImage是感谢的!我已经削减了一些多余的代码,以使它更美观,但如果我不小心修剪了一些重要的东西,或不显示我是如何使用可能会造成问题的东西,请让我知道,我将修改这篇文章。
发布于 2013-01-09 23:00:42
我处理这样的问题的方式是,当接收到画图事件时,我将整个图像绘制到一个内存位图中,然后将其绘制到窗口。这样,所有的视觉闪光灯都被消除了,而且看起来很快,即使它实际上不是。
更清楚的是,我不从鼠标事件处理程序中进行任何绘制。我只是设置了主油漆处理程序所需的内容,然后执行无效操作。所以画发生在鼠标事件完成之后。
补充说:在评论中回答汤姆的问题,我是这样做的。请记住,我并不认为它是快速的,只是它看起来很快,因为_e.Graphics.DrawImage(bmToDrawOn, new Point(0,0));看起来是瞬间的。它只是从一个图像到另一个图像。用户不会看到窗口被清除,然后逐个重新绘制。它的效果和双重缓冲一样。
Graphics grToDrawOn = null;
Bitmap bmToDrawOn = null;
private void DgmWin_Paint(object sender, PaintEventArgs _e){
int w = ClientRectangle.Width;
int h = ClientRectangle.Height;
Graphics gr = _e.Graphics;
// if the bitmap needs to be made, do so
if (bmToDrawOn == null) bmToDrawOn = new Bitmap(w, h, gr);
// if the bitmap needs to be changed in size, do so
if (bmToDrawOn.Width != w || bmToDrawOn.Height != h){
bmToDrawOn = new Bitmap(w, h, gr);
}
// hook the bitmap into the graphics object
grToDrawOn = Graphics.FromImage(bmToDrawOn);
// clear the graphics object before drawing
grToDrawOn.Clear(Color.White);
// paint everything
DoPainting();
// copy the bitmap onto the real screen
_e.Graphics.DrawImage(bmToDrawOn, new Point(0,0));
}
private void DoPainting(){
grToDrawOn.blahblah....
}https://stackoverflow.com/questions/14247956
复制相似问题