当PictureBox包含图像时,FPS会大幅下降-有没有办法改善此WinForm代码示例的FPS? [英] Huge FPS drop when PictureBox have an Image - Is there a way to improve the FPS of this WinForm code sample?

查看:83
本文介绍了当PictureBox包含图像时,FPS会大幅下降-有没有办法改善此WinForm代码示例的FPS?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找在Winform上刷新图像(并绘制某些形状)的最快方法。我现在使用的是PictureBox,但我愿意征求建议。



我从这里获取最大FPS的方式是这样的: 我在渲染循环上的最后一篇文章(希望如此)



这是一个游戏循环模式,但也可以用于其他目的,例如显示从相机获取的实时图像。



我在不同位置绘制了一个红色圆圈。



如您所见,当在PictureBox上加载位图时,FPS下降很多。



我的FPS PC值:

 表单大小(起始大小)
位图开启:140(缩放); 1000(普通); 135(拉伸); 880(自动); 1000(中心)
位图OFF:3400 !!

表单大小(缩放100%单击后)
位图开启:40(缩放); 150(普通); 65(拉伸); 150(自动); 150(中心)
位图关闭:540 !!

编辑1:
这个结果是针对1024x1024图像的。但是我意识到表格的高度没有得到正确的值,我发现这是因为表格的大小受到屏幕分辨率的限制。因此,我已编辑代码以加载800x600位图。现在,缩放100%按钮将起作用。如您所见,在100%缩放时,FPS为350,但是如果您将PictureBox的大小再增加一个像素,FPS会降至35,慢¬!¬

最大的问题是:如何改善FPS,特别是在缩放模式下?



PS:我知道WinForm不是最好的方法,我也知道WPF。但是现在,我正在寻找使用WinForm的解决方案。好?原因是我有一个使用它的大型项目。我打算改用WPF,但仍在学习中。 :)



这里是完整代码,因此您可以自己对其进行测试。
Form1.cs:

  //。NET 4.0客户端配置文件
,使用系统;
使用System.Diagnostics;
使用System.Drawing;
使用System.Drawing.Imaging;
使用System.Runtime.InteropServices;
使用System.Windows.Forms;
//您需要使用System.Activities.Presentation.Hosting添加System.Activities.Presentation.dll


命名空间WindowsFormsApplication2
{
公用枚举DrawSource
{
无= 0,
PictureBox = 1,
Grapghics = 2 ,
}

公共局部类Form1:表单
{
private readonly Timer _updateFPSTimer;
private bool _isIdle;
私人Point _location;
private double _updateCount;
private只读Bitmap _backImage;

公共Form1()
{
InitializeComponent();

this.DoubleBuffered = this.ckbDoubleBufferForm.Checked;
this.pictureBox1.DoubleBuffered = this.ckbDoubleBufferPictureBox.Checked;

_backImage = new Bitmap(800,600,PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(_backImage);
int penWidth = 10;
Pen pen =新的Pen(Brushes.Blue,penWidth);

g.DrawRectangle(笔,新Rectangle(新Point(penWidth,penWidth),新Size(_backImage.Size.Width-2 * penWidth,_backImage.Size.Height-2 * penWidth))) ;

this.cbxSizeMode.DataSource = Enum.GetValues(typeof(PictureBoxSizeMode));
this.cbxSizeMode.SelectedItem = PictureBoxSizeMode.Zoom;

this.cbxBitmapDrawSource.DataSource = Enum.GetValues(typeof(DrawSource));
this.cbxBitmapDrawSource.SelectedItem = DrawSource.PictureBox;

this.pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
Application.Idle + = Application_Idle;

_updateFPSTimer = new Timer();
_updateFPSTimer.Tick + = UpdateFPSTimer_Tick;
_updateFPSTimer.Interval = Convert.ToInt32(1000.0 / 5); //周期为s = 1 s / 5 Hz
_updateFPSTimer.Start();

}

private void Application_Idle(对象发送方,EventArgs e)
{
而(this.ckbRun.Checked&& IsAppStillIdle() )
this.pictureBox1.Refresh();
}

void UpdateFPSTimer_Tick(object sender,EventArgs e)
{
GetFPS();
}

private void GetFPS()
{
double fps = _updateCount /(Convert.ToDouble(_updateFPSTimer.Interval)/ 1000.0);
_updateCount = 0;
lblFps.Text = fps.ToString( 0.00);
}

private void PictureBox1_Paint(object sender,PaintEventArgs e)
{
DrawCircle(e.Graphics);
}

private void DrawCircle(Graphics g)
{
if(this.pictureBox1.Image == null&&((DrawSource)this.cbxBitmapDrawSource .SelectedItem == DrawSource.Grapghics))
g.DrawImage(_backImage,0,0,g.ClipBounds.Width,g.ClipBounds.Height);

var rect = new Rectangle(_location,new Size(30,30));

g.DrawEllipse(Pens.Red,rect);

_location.X + = 1;
_location.Y + = 1;

如果(_location.X> g.ClipBounds.Width)
_location.X = 0;

如果(_location.Y> g.ClipBounds.Height)
_location.Y = 0;

_updateCount ++;
}

///< summary>
///获取应用是否仍然空闲。
/// http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
///< / summary>
///< returns>< / returns>
私人布尔IsAppStillIdle()
{
消息msg;
return!PeekMessage(out msg,IntPtr.Zero,0,0,0);
}

#region非托管Get PeekMessage
// http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
[System.Security.SuppressUnmanagedCodeSecurity] //我们不会恶意使用
[DllImport( User32.dll,CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out消息msg,IntPtr hWnd,uint messageFilterMin,uint messageFilterMax,uint标志);

#endregion

私人无效btnZoomReset_Click(对象发送方,EventArgs e)
{
if(_backImage!= null)
{
//是否有更聪明的方法?
///请注意,指定的屏幕分辨率限制了最大窗体尺寸。
//矩形屏幕Rectangle = RectangleToScreen(this.ClientRectangle);
// int titleHeight = screenRectangle.Top-this.Top;
//边界
尺寸边框= new Size();
border.Width = this.Width-this.pictureBox1.Width;
border.Height = this.Height-this.pictureBox1.Height;
this.Width = border.Width + _backImage.Width;
this.Height = border.Height + _backImage.Height;
Console.WriteLine( PictureBox size: + this.pictureBox1.Size.ToString());
}

}

private void SizeMode_SelectedIndexChanged(object sender,EventArgs e)
{
PictureBoxSizeMode模式;
Enum.TryParse< PictureBoxSizeMode>(cbxSizeMode.SelectedValue.ToString(),输出模式);

this.pictureBox1.SizeMode =模式;
}

private void DoubleBufferForm_CheckedChanged(object sender,EventArgs e)
{
this.DoubleBuffered = this.ckbDoubleBufferForm.Checked;
}

private void DoubleBufferPictureBox_CheckedChanged(object sender,EventArgs e)
{
this.pictureBox1.DoubleBuffered = this.ckbDoubleBufferPictureBox.Checked;
}

私有无效BitmapDrawSource_SelectedIndexChanged(对象发送方,EventArgs e)
{
if((DrawSource)this.cbxBitmapDrawSource.SelectedItem == DrawSource.PictureBox)
this.pictureBox1.Image = _backImage;
else
this.pictureBox1.Image = null;
}
}
}

Designer.CS:

 命名空间WindowsFormsApplication2 
{
部分类Form1
{
///<摘要>
///必需的设计器变量。
///< / summary>
私人System.ComponentModel.IContainer组件= null;

///< summary>
///清理所有正在使用的资源。
///< / summary>
///< param name = disposed>如果应该处置托管资源,则为true; < / param>否则为false。
保护的覆盖无效void Dispose(布尔处理)
{
if(dispose&&(components!= null))
{
components.Dispose();
}
base.Dispose(处置);
}

#region Windows窗体设计器生成的代码

///< summary>
/// Designer支持的必需方法-请勿使用代码编辑器修改
///的此方法的内容。
///< / summary>
private void InitializeComponent()
{
this.pictureBox1 = new WindowsFormsApplication2.PictureBoxDoubleBuffer();
this.label1 = new System.Windows.Forms.Label();
this.lblFps = new System.Windows.Forms.Label();
this.ckbRun = new System.Windows.Forms.CheckBox();
this.btnZoomReset = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.cbxSizeMode = new System.Windows.Forms.ComboBox();
this.gpbDoubleBuffer = new System.Windows.Forms.GroupBox();
this.ckbDoubleBufferPictureBox = new System.Windows.Forms.CheckBox();
this.ckbDoubleBufferForm = new System.Windows.Forms.CheckBox();
this.cbxBitmapDrawSource = new System.Windows.Forms.ComboBox();
this.label3 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1))。BeginInit();
this.gpbDoubleBuffer.SuspendLayout();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Anchor =(((System.Windows.Forms.AnchorStyles)(((((System.Windows.Forms.AnchorStyles .Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))));
this.pictureBox1.BackColor = System.Drawing.SystemColors.ControlDarkDark;
this.pictureBox1.DoubleBuffered = true;
this.pictureBox1.Location = new System.Drawing.Point(8,84);
this.pictureBox1.Name = pictureBox1;
this.pictureBox1.Size = new System.Drawing.Size(347,215);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.pictureBox1.Paint + =新的System.Windows.Forms.PaintEventHandler(this.PictureBox1_Paint);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(134,13);
this.label1.Name = label1;
this.label1.Size = new System.Drawing.Size(30,13);
this.label1.TabIndex = 3;
this.label1.Text = FPS:;
//
// lblFps
//
this.lblFps.AutoSize = true;
this.lblFps.Location = new System.Drawing.Point(160,13);
this.lblFps.Name = lblFps;
this.lblFps.Size = new System.Drawing.Size(10,13);
this.lblFps.TabIndex = 4;
this.lblFps.Text =-;
//
// ckbRun
//
this.ckbRun.Appearance = System.Windows.Forms.Appearance.Button;
this.ckbRun.AutoSize = true;
this.ckbRun.Checked = true;
this.ckbRun.CheckState = System.Windows.Forms.CheckState.Checked;
this.ckbRun.Location = new System.Drawing.Point(8,8);
this.ckbRun.Name = ckbRun;
this.ckbRun.Size = new System.Drawing.Size(37,23);
this.ckbRun.TabIndex = 5;
this.ckbRun.Text =运行;
this.ckbRun.UseVisualStyleBackColor = true;
//
// btnZoomReset
//
this.btnZoomReset.Location = new System.Drawing.Point(51,8);
this.btnZoomReset.Name = btnZoomReset;
this.btnZoomReset.Size = new System.Drawing.Size(74,23);
this.btnZoomReset.TabIndex = 7;
this.btnZoomReset.Text =缩放100%;
this.btnZoomReset.UseVisualStyleBackColor = true;
this.btnZoomReset.Click + = new System.EventHandler(this.btnZoomReset_Click);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(5,37);
this.label2.Name = label2;
this.label2.Size = new System.Drawing.Size(57,13);
this.label2.TabIndex = 8;
this.label2.Text = SizeMode:;
//
// cbxSizeMode
//
this.cbxSizeMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbxSizeMode.FormattingEnabled = true;
this.cbxSizeMode.Location = new System.Drawing.Point(73,34);
this.cbxSizeMode.Name = cbxSizeMode;
this.cbxSizeMode.Size = new System.Drawing.Size(84,21);
this.cbxSizeMode.TabIndex = 9;
this.cbxSizeMode.SelectedIndexChanged + =新的System.EventHandler(this.SizeMode_SelectedIndexChanged);
//
// gpbDoubleBuffer
//
this.gpbDoubleBuffer.Controls.Add(this.ckbDoubleBufferPictureBox);
this.gpbDoubleBuffer.Controls.Add(this.ckbDoubleBufferForm);
this.gpbDoubleBuffer.Location =新的System.Drawing.Point(221,8);
this.gpbDoubleBuffer.Name = gpbDoubleBuffer;
this.gpbDoubleBuffer.Size = new System.Drawing.Size(99,65);
this.gpbDoubleBuffer.TabIndex = 10;
this.gpbDoubleBuffer.TabStop = false;
this.gpbDoubleBuffer.Text =双缓冲区;
//
// ckbDoubleBufferPictureBox
//
this.ckbDoubleBufferPictureBox.AutoSize = true;
this.ckbDoubleBufferPictureBox.Checked = true;
this.ckbDoubleBufferPictureBox.CheckState = System.Windows.Forms.CheckState.Checked;
this.ckbDoubleBufferPictureBox.Location = new System.Drawing.Point(9,38);
this.ckbDoubleBufferPictureBox.Name = ckbDoubleBufferPictureBox;
this.ckbDoubleBufferPictureBox.Size = new System.Drawing.Size(77,17);
this.ckbDoubleBufferPictureBox.TabIndex = 8;
this.ckbDoubleBufferPictureBox.Text = PictureBox;
this.ckbDoubleBufferPictureBox.UseVisualStyleBackColor = true;
this.ckbDoubleBufferPictureBox.CheckedChanged + =新的System.EventHandler(this.DoubleBufferPictureBox_CheckedChanged);
//
// ckbDoubleBufferForm
//
this.ckbDoubleBufferForm.AutoSize = true;
this.ckbDoubleBufferForm.Checked = true;
this.ckbDoubleBufferForm.CheckState = System.Windows.Forms.CheckState.Checked;
this.ckbDoubleBufferForm.Location = new System.Drawing.Point(9,15);
this.ckbDoubleBufferForm.Name = ckbDoubleBufferForm;
this.ckbDoubleBufferForm.Size = new System.Drawing.Size(49,17);
this.ckbDoubleBufferForm.TabIndex = 7;
this.ckbDoubleBufferForm.Text =表格;
this.ckbDoubleBufferForm.UseVisualStyleBackColor = true;
this.ckbDoubleBufferForm.CheckedChanged + = new System.EventHandler(this.DoubleBufferForm_CheckedChanged);
//
// cbxBitmapDrawSource
//
this.cbxBitmapDrawSource.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbxBitmapDrawSource.FormattingEnabled = true;
this.cbxBitmapDrawSource.Items.AddRange(new object [] {
PictureBox,
Graphics});
this.cbxBitmapDrawSource.Location = new System.Drawing.Point(73,57);
this.cbxBitmapDrawSource.Name = cbxBitmapDrawSource;
this.cbxBitmapDrawSource.Size = new System.Drawing.Size(84,21);
this.cbxBitmapDrawSource.TabIndex = 12;
this.cbxBitmapDrawSource.SelectedIndexChanged + =新的System.EventHandler(this.BitmapDrawSource_SelectedIndexChanged);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(5,60);
this.label3.Name = label3;
this.label3.Size = new System.Drawing.Size(68,13);
this.label3.TabIndex = 11;
this.label3.Text =位图绘制:;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F,13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(361,304);
this.Controls.Add(this.cbxBitmapDrawSource);
this.Controls.Add(this.label3);
this.Controls.Add(this.gpbDoubleBuffer);
this.Controls.Add(this.cbxSizeMode);
this.Controls.Add(this.label2);
this.Controls.Add(this.btnZoomReset);
this.Controls.Add(this.ckbRun);
this.Controls.Add(this.lblFps);
this.Controls.Add(this.label1);
this.Controls.Add(this.pictureBox1);
this.DoubleBuffered = true;
this.Name = Form1;
this.Text =最大FPS测试器;
((System.ComponentModel.ISupportInitialize)(this.pictureBox1))。EndInit();
this.gpbDoubleBuffer.ResumeLayout(false);
this.gpbDoubleBuffer.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

PictureBoxDoubleBuffer pictureBox1;
私人System.Windows.Forms.Label label1;
私人System.Windows.Forms.Label lblFps;
私人System.Windows.Forms.CheckBox ckbRun;
私人System.Windows.Forms.Button btnZoomReset;
私人System.Windows.Forms.Label label2;
私人System.Windows.Forms.ComboBox cbxSizeMode;
私人System.Windows.Forms.GroupBox gpbDoubleBuffer;
私人System.Windows.Forms.CheckBox ckbDoubleBufferPictureBox;
私人System.Windows.Forms.CheckBox ckbDoubleBufferForm;
私人System.Windows.Forms.ComboBox cbxBitmapDrawSource;
私人System.Windows.Forms.Label label3;
}
}

PictureBoxDoubleBuffer.cs:

 使用System.Windows.Forms; 

命名空间WindowsFormsApplication2
{
public class PictureBoxDoubleBuffer:PictureBox
{
public bool DoubleBuffered
{
get {返回基数。 DoubleBuffered; }
set {base.DoubleBuffered = value; }
}

public PictureBoxDoubleBuffer()
{
base.DoubleBuffered = true;
}
}
}


解决方案

最大的问题是:如何改善FPS,尤其是在缩放模式下?



在缩放(和拉伸)模式下,Image()为可能需要为每个Refresh()调整大小 ...这是一个非常昂贵的操作。您可以滚动自己的缩放模式来创建已经缩放的新图像,然后分配该图像,而将模式保留为普通。



这里是一个简单的示例在另一个PictureBox上使用作弊缩放(显然,您可以通过计算宽高比等来数学上进行缩放,但这在早上会伤害我的大脑):

  private void cbxSizeMode_SelectedIndexChanged(object sender,EventArgs e)
{
PictureBoxSizeMode模式;
Enum.TryParse< PictureBoxSizeMode>(cbxSizeMode.SelectedValue.ToString(),输出模式);
this.pictureBox1.SizeMode =模式;

if(this.pictureBox1.SizeMode == PictureBoxSizeMode.Zoom)
{
使用(PictureBox pb = new PictureBox())
{
pb .Size = pictureBox1.Size;
pb.SizeMode = PictureBoxSizeMode.Zoom;
pb.Image = _backImage;
位图bmp =新位图(pb.Size.Width,pb.Size.Height);
pb.DrawToBitmap(bmp,pb.ClientRectangle);

this.pictureBox1.SizeMode = PictureBoxSizeMode.Normal;
this.pictureBox1.Image = bmp;
}
}
else
{
pictureBox1.Image = _backImage;
}
}

您需要调用此方法当缩放为默认选择时,当表单首次运行时,否则它将没有自定义缩放图像。



现在,您的缩放模式应该可以放大了! (抱歉,无法抗拒)


I'm looking for the fastest way to refresh an image (and draw some shapes) on a Winform. I'm using a PictureBox now, but I'm open for suggestions.

The way I'm getting the max FPS possible I took from here: "My last post on render loops (hopefully)"

This is a game loop pattern, but it can also be used to other purpose, like to display live images acquired from a camera.

I'm drawing a red circle in different positions.

As you can see, the FPS drops a lot when a Bitmap is loaded at the PictureBox. Especially in the SizeMode Zoom (the one I want to use).

My PC values for FPS:

Form size (start size)
Bitmap ON: 140 (Zoom); 1000 (Normal); 135 (Stretch); 880 (Auto); 1000 (Center)
Bitmap OFF: 3400 !!

Form size (after Zoom 100% click)
Bitmap ON: 40 (Zoom); 150 (Normal); 65 (Stretch); 150 (Auto); 150 (Center)
Bitmap OFF: 540 !!

Edit 1: Well, this results was for a 1024x1024 image. But I realizy that the form Height was not getting the right value, I found out that it is because the form size is limited by the Screen resolution. So I've edited the code to load a 800x600 Bitmap. Now the Zoom 100% button works. As you can see, at zoom 100% the FPS is 350, but if you increase the size of the PictureBox one more pixel, the FPS drops to 35, about 10x slower ¬¬ !

The big question is: How can I improve the FPS, specially in zoom mode?

PS: I know WinForm is not the best way to go, I also know WPF. But for now I'm looking the solution using WinForm. Ok? The reason is that I have a big scary project using it. I'm planing to move to WPF, but I'm still learning it. :)

Here is the full code, so you can test it by yourself. Form1.cs:

//.NET 4.0 Client Profile
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//You need to add System.Activities.Presentation.dll
using System.Activities.Presentation.Hosting;

namespace WindowsFormsApplication2
{
    public enum DrawSource
    {
        None = 0,
        PictureBox = 1,
        Grapghics = 2,
    }

    public partial class Form1 : Form
    {
        private readonly Timer _updateFPSTimer;
        private bool _isIdle;
        private Point _location;
        private double _updateCount;
        private readonly Bitmap _backImage;

        public Form1()
        {
            InitializeComponent();

            this.DoubleBuffered = this.ckbDoubleBufferForm.Checked;
            this.pictureBox1.DoubleBuffered = this.ckbDoubleBufferPictureBox.Checked;

            _backImage = new Bitmap(800, 600, PixelFormat.Format24bppRgb);
            Graphics g = Graphics.FromImage(_backImage);
            int penWidth = 10;
            Pen pen = new Pen(Brushes.Blue, penWidth);

            g.DrawRectangle(pen, new Rectangle(new Point(penWidth, penWidth), new Size(_backImage.Size.Width - 2 * penWidth, _backImage.Size.Height - 2 * penWidth)));

            this.cbxSizeMode.DataSource = Enum.GetValues(typeof(PictureBoxSizeMode));
            this.cbxSizeMode.SelectedItem = PictureBoxSizeMode.Zoom;

            this.cbxBitmapDrawSource.DataSource = Enum.GetValues(typeof(DrawSource));
            this.cbxBitmapDrawSource.SelectedItem = DrawSource.PictureBox;

            this.pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
            Application.Idle += Application_Idle;

            _updateFPSTimer = new Timer();
            _updateFPSTimer.Tick += UpdateFPSTimer_Tick;
            _updateFPSTimer.Interval = Convert.ToInt32(1000.0 / 5); //Period in s = 1 s / 5 Hz
            _updateFPSTimer.Start();

        }

        private void Application_Idle(object sender, EventArgs e)
        {
            while (this.ckbRun.Checked && IsAppStillIdle())
                this.pictureBox1.Refresh();
        }

        void UpdateFPSTimer_Tick(object sender, EventArgs e)
        {
            GetFPS();
        }

        private void GetFPS()
        {
            double fps = _updateCount / (Convert.ToDouble(_updateFPSTimer.Interval) / 1000.0);
            _updateCount = 0;
            lblFps.Text = fps.ToString("0.00");
        }

        private void PictureBox1_Paint(object sender, PaintEventArgs e)
        {
            DrawCircle(e.Graphics);
        }

        private void DrawCircle(Graphics g)
        {
            if (this.pictureBox1.Image == null && ((DrawSource)this.cbxBitmapDrawSource.SelectedItem == DrawSource.Grapghics))
                g.DrawImage(_backImage, 0, 0, g.ClipBounds.Width, g.ClipBounds.Height);

            var rect = new Rectangle(_location, new Size(30, 30));

            g.DrawEllipse(Pens.Red, rect);

            _location.X += 1;
            _location.Y += 1;

            if (_location.X > g.ClipBounds.Width)
                _location.X = 0;

            if (_location.Y > g.ClipBounds.Height)
                _location.Y = 0;

            _updateCount++;
        }

        /// <summary>
        /// Gets if the app still idle.
        /// http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
        /// </summary>
        /// <returns></returns>
        private bool IsAppStillIdle()
        {
            Message msg;
            return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
        }

        #region  Unmanaged Get PeekMessage
        // http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
        [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

        #endregion

        private void btnZoomReset_Click(object sender, EventArgs e)
        {
            if (_backImage != null)
            {
                //Any smarter way to do this?
                //Note that the maximum Form size is limmited by designe by the Screen resolution.
                //Rectangle screenRectangle = RectangleToScreen(this.ClientRectangle);
                //int titleHeight = screenRectangle.Top - this.Top;
                //Borders
                Size border = new Size();
                border.Width = this.Width - this.pictureBox1.Width;
                border.Height = this.Height - this.pictureBox1.Height;
                this.Width = border.Width + _backImage.Width;
                this.Height = border.Height + _backImage.Height;
                Console.WriteLine("PictureBox size: " + this.pictureBox1.Size.ToString());
            }

        }

        private void SizeMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            PictureBoxSizeMode mode;
            Enum.TryParse<PictureBoxSizeMode>(cbxSizeMode.SelectedValue.ToString(), out mode);

            this.pictureBox1.SizeMode = mode;
        }

        private void DoubleBufferForm_CheckedChanged(object sender, EventArgs e)
        {
            this.DoubleBuffered = this.ckbDoubleBufferForm.Checked;
        }

        private void DoubleBufferPictureBox_CheckedChanged(object sender, EventArgs e)
        {
            this.pictureBox1.DoubleBuffered = this.ckbDoubleBufferPictureBox.Checked;
        }

        private void BitmapDrawSource_SelectedIndexChanged(object sender, EventArgs e)
        {
            if ((DrawSource)this.cbxBitmapDrawSource.SelectedItem == DrawSource.PictureBox)
                this.pictureBox1.Image = _backImage;
            else
                this.pictureBox1.Image = null;
        }
    }
}

Designer.CS:

namespace WindowsFormsApplication2
{
    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.pictureBox1 = new WindowsFormsApplication2.PictureBoxDoubleBuffer();
            this.label1 = new System.Windows.Forms.Label();
            this.lblFps = new System.Windows.Forms.Label();
            this.ckbRun = new System.Windows.Forms.CheckBox();
            this.btnZoomReset = new System.Windows.Forms.Button();
            this.label2 = new System.Windows.Forms.Label();
            this.cbxSizeMode = new System.Windows.Forms.ComboBox();
            this.gpbDoubleBuffer = new System.Windows.Forms.GroupBox();
            this.ckbDoubleBufferPictureBox = new System.Windows.Forms.CheckBox();
            this.ckbDoubleBufferForm = new System.Windows.Forms.CheckBox();
            this.cbxBitmapDrawSource = new System.Windows.Forms.ComboBox();
            this.label3 = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
            this.gpbDoubleBuffer.SuspendLayout();
            this.SuspendLayout();
            // 
            // pictureBox1
            // 
            this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.pictureBox1.BackColor = System.Drawing.SystemColors.ControlDarkDark;
            this.pictureBox1.DoubleBuffered = true;
            this.pictureBox1.Location = new System.Drawing.Point(8, 84);
            this.pictureBox1.Name = "pictureBox1";
            this.pictureBox1.Size = new System.Drawing.Size(347, 215);
            this.pictureBox1.TabIndex = 0;
            this.pictureBox1.TabStop = false;
            this.pictureBox1.Paint += new System.Windows.Forms.PaintEventHandler(this.PictureBox1_Paint);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(134, 13);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(30, 13);
            this.label1.TabIndex = 3;
            this.label1.Text = "FPS:";
            // 
            // lblFps
            // 
            this.lblFps.AutoSize = true;
            this.lblFps.Location = new System.Drawing.Point(160, 13);
            this.lblFps.Name = "lblFps";
            this.lblFps.Size = new System.Drawing.Size(10, 13);
            this.lblFps.TabIndex = 4;
            this.lblFps.Text = "-";
            // 
            // ckbRun
            // 
            this.ckbRun.Appearance = System.Windows.Forms.Appearance.Button;
            this.ckbRun.AutoSize = true;
            this.ckbRun.Checked = true;
            this.ckbRun.CheckState = System.Windows.Forms.CheckState.Checked;
            this.ckbRun.Location = new System.Drawing.Point(8, 8);
            this.ckbRun.Name = "ckbRun";
            this.ckbRun.Size = new System.Drawing.Size(37, 23);
            this.ckbRun.TabIndex = 5;
            this.ckbRun.Text = "Run";
            this.ckbRun.UseVisualStyleBackColor = true;
            // 
            // btnZoomReset
            // 
            this.btnZoomReset.Location = new System.Drawing.Point(51, 8);
            this.btnZoomReset.Name = "btnZoomReset";
            this.btnZoomReset.Size = new System.Drawing.Size(74, 23);
            this.btnZoomReset.TabIndex = 7;
            this.btnZoomReset.Text = "Zoom 100%";
            this.btnZoomReset.UseVisualStyleBackColor = true;
            this.btnZoomReset.Click += new System.EventHandler(this.btnZoomReset_Click);
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(5, 37);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(57, 13);
            this.label2.TabIndex = 8;
            this.label2.Text = "SizeMode:";
            // 
            // cbxSizeMode
            // 
            this.cbxSizeMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cbxSizeMode.FormattingEnabled = true;
            this.cbxSizeMode.Location = new System.Drawing.Point(73, 34);
            this.cbxSizeMode.Name = "cbxSizeMode";
            this.cbxSizeMode.Size = new System.Drawing.Size(84, 21);
            this.cbxSizeMode.TabIndex = 9;
            this.cbxSizeMode.SelectedIndexChanged += new System.EventHandler(this.SizeMode_SelectedIndexChanged);
            // 
            // gpbDoubleBuffer
            // 
            this.gpbDoubleBuffer.Controls.Add(this.ckbDoubleBufferPictureBox);
            this.gpbDoubleBuffer.Controls.Add(this.ckbDoubleBufferForm);
            this.gpbDoubleBuffer.Location = new System.Drawing.Point(221, 8);
            this.gpbDoubleBuffer.Name = "gpbDoubleBuffer";
            this.gpbDoubleBuffer.Size = new System.Drawing.Size(99, 65);
            this.gpbDoubleBuffer.TabIndex = 10;
            this.gpbDoubleBuffer.TabStop = false;
            this.gpbDoubleBuffer.Text = "Double Buffer";
            // 
            // ckbDoubleBufferPictureBox
            // 
            this.ckbDoubleBufferPictureBox.AutoSize = true;
            this.ckbDoubleBufferPictureBox.Checked = true;
            this.ckbDoubleBufferPictureBox.CheckState = System.Windows.Forms.CheckState.Checked;
            this.ckbDoubleBufferPictureBox.Location = new System.Drawing.Point(9, 38);
            this.ckbDoubleBufferPictureBox.Name = "ckbDoubleBufferPictureBox";
            this.ckbDoubleBufferPictureBox.Size = new System.Drawing.Size(77, 17);
            this.ckbDoubleBufferPictureBox.TabIndex = 8;
            this.ckbDoubleBufferPictureBox.Text = "PictureBox";
            this.ckbDoubleBufferPictureBox.UseVisualStyleBackColor = true;
            this.ckbDoubleBufferPictureBox.CheckedChanged += new System.EventHandler(this.DoubleBufferPictureBox_CheckedChanged);
            // 
            // ckbDoubleBufferForm
            // 
            this.ckbDoubleBufferForm.AutoSize = true;
            this.ckbDoubleBufferForm.Checked = true;
            this.ckbDoubleBufferForm.CheckState = System.Windows.Forms.CheckState.Checked;
            this.ckbDoubleBufferForm.Location = new System.Drawing.Point(9, 15);
            this.ckbDoubleBufferForm.Name = "ckbDoubleBufferForm";
            this.ckbDoubleBufferForm.Size = new System.Drawing.Size(49, 17);
            this.ckbDoubleBufferForm.TabIndex = 7;
            this.ckbDoubleBufferForm.Text = "Form";
            this.ckbDoubleBufferForm.UseVisualStyleBackColor = true;
            this.ckbDoubleBufferForm.CheckedChanged += new System.EventHandler(this.DoubleBufferForm_CheckedChanged);
            // 
            // cbxBitmapDrawSource
            // 
            this.cbxBitmapDrawSource.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cbxBitmapDrawSource.FormattingEnabled = true;
            this.cbxBitmapDrawSource.Items.AddRange(new object[] {
            "PictureBox",
            "Graphics"});
            this.cbxBitmapDrawSource.Location = new System.Drawing.Point(73, 57);
            this.cbxBitmapDrawSource.Name = "cbxBitmapDrawSource";
            this.cbxBitmapDrawSource.Size = new System.Drawing.Size(84, 21);
            this.cbxBitmapDrawSource.TabIndex = 12;
            this.cbxBitmapDrawSource.SelectedIndexChanged += new System.EventHandler(this.BitmapDrawSource_SelectedIndexChanged);
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(5, 60);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(68, 13);
            this.label3.TabIndex = 11;
            this.label3.Text = "Bitmap draw:";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(361, 304);
            this.Controls.Add(this.cbxBitmapDrawSource);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.gpbDoubleBuffer);
            this.Controls.Add(this.cbxSizeMode);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.btnZoomReset);
            this.Controls.Add(this.ckbRun);
            this.Controls.Add(this.lblFps);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.pictureBox1);
            this.DoubleBuffered = true;
            this.Name = "Form1";
            this.Text = "Max FPS tester";
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
            this.gpbDoubleBuffer.ResumeLayout(false);
            this.gpbDoubleBuffer.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        PictureBoxDoubleBuffer pictureBox1;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label lblFps;
        private System.Windows.Forms.CheckBox ckbRun;
        private System.Windows.Forms.Button btnZoomReset;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.ComboBox cbxSizeMode;
        private System.Windows.Forms.GroupBox gpbDoubleBuffer;
        private System.Windows.Forms.CheckBox ckbDoubleBufferPictureBox;
        private System.Windows.Forms.CheckBox ckbDoubleBufferForm;
        private System.Windows.Forms.ComboBox cbxBitmapDrawSource;
        private System.Windows.Forms.Label label3;
    }
}

PictureBoxDoubleBuffer.cs:

using System.Windows.Forms;

namespace WindowsFormsApplication2
{
   public class PictureBoxDoubleBuffer : PictureBox
   {
       public bool DoubleBuffered
       {
           get { return base.DoubleBuffered; }
           set { base.DoubleBuffered = value; }
       }

       public PictureBoxDoubleBuffer()
       {
           base.DoubleBuffered = true;
       }
    }
}

解决方案

"The big question is: How can I improve the FPS, specially in zoom mode?"

In Zoom (and Stretch) mode the Image() is probably being resized for each Refresh()...a very expensive operation. You could roll your own "zoom" mode that creates a new image that is already zoomed and assign that instead, leaving the mode at "Normal".

Here's a quick example using a cheat zoom with another PictureBox (obviously you could do the zoom mathematically by computing aspect ratio, etc...but that hurts my brain in the morning):

    private void cbxSizeMode_SelectedIndexChanged(object sender, EventArgs e)
    {
        PictureBoxSizeMode mode;
        Enum.TryParse<PictureBoxSizeMode>(cbxSizeMode.SelectedValue.ToString(), out mode);
        this.pictureBox1.SizeMode = mode;

        if (this.pictureBox1.SizeMode == PictureBoxSizeMode.Zoom)
        {
            using (PictureBox pb = new PictureBox())
            {
                pb.Size = pictureBox1.Size;
                pb.SizeMode = PictureBoxSizeMode.Zoom;
                pb.Image = _backImage;
                Bitmap bmp = new Bitmap(pb.Size.Width, pb.Size.Height);
                pb.DrawToBitmap(bmp, pb.ClientRectangle);

                this.pictureBox1.SizeMode = PictureBoxSizeMode.Normal;
                this.pictureBox1.Image = bmp;
            }
        }
        else
        {
            pictureBox1.Image = _backImage;
        }
    }

You'll need to call this method when the form first runs if Zoom is the default selection, otherwise it won't have custom zoom image.

Now your Zoom mode should zoom along! (sorry, couldn't resist doing that)

这篇关于当PictureBox包含图像时,FPS会大幅下降-有没有办法改善此WinForm代码示例的FPS?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆