这段代码是如何工作的? (GDI,C#)

Posted

技术标签:

【中文标题】这段代码是如何工作的? (GDI,C#)【英文标题】:How does this code work? (GDI, C#) 【发布时间】:2017-04-09 09:52:10 【问题描述】:

我只是想知道是否有人可以为我阐明这一点。我多年来一直在编写 c# 代码,但除了位图类之外,我什至从未接触过 System.Drawing 命名空间中的任何内容,并且我一直在关注一些教程并提出了一些有效的代码。我正在开发一个 2D 游戏引擎,下面的代码用于使用 GDI 的图形引擎。但是,我只是不明白这段代码是如何工作的。这里是:

    private Graphics frontBuffer;
    private Graphics backBuffer;
    private Bitmap backBufferBitmap;

    public void Initialize()
    
      backBufferBitmap = new Bitmap(game.Form.Width, game.Form.Height);
      frontBuffer = game.Form.CreateGraphics();
      backBuffer = Graphics.FromImage(backBufferBitmap);          
    

    public void Update()
    
      try
      
        frontBuffer.DrawImageUnscaled(backBufferBitmap,0,0);
        backBuffer.Clear(Color.Black);
      
      catch(Exception e)
      
        throw e;
      
    

所以,让我感到困惑的主要部分是这个;

后台缓冲区位图如何更新?为什么清除的是后台缓冲区而不是前台缓冲区?

另外,initialize 方法被调用一次,update 方法在 while 循环中每帧调用一次。

【问题讨论】:

仅作记录:我对您找到或编写为 game.Form.CreateGraphics(); 的代码有一些疑问,因为缓冲 Graphics 对象通常是个坏主意。 但是为什么呢?我所做的只是引用表单图形对象? 没有这样的东西。您正在(原文如此!)创建一个。但你不应该。由Paint 事件来做到这一点! (除非您在其他地方需要一个,因为您想要测量事物或想要像橡皮筋线一样绘制 非持久性 图形..) - 另外:Frontbuffer 被隐式​​清除,因为当您创建它的位图。 我已经禁用了绘制事件,因为使用这种方法更快。我真正要做的就是在需要时调用绘制事件,而不是每一帧。如果没有这种方法,引擎就不会高效。 你做了DoubleBuffer这个表格吗?您的表单是否存在最小化-最大化循环? 【参考方案1】:

在您从位图初始化backBuffer 图形对象后,每次您说,例如backBuffer.DrawLine(...) GDI+ 将直接在此位图上进行像素操作。它们以某种方式联系在一起。将backBufferBitmap 视为画布,将backBuffer 视为画笔。

frontBuffer 是从表单初始化的。所以表单就是它的画布,无论你对frontBuffer 做什么都会被绘制到表单上——在这种情况下,它会绘制backBufferBitmap

它基本上是一个双缓冲方案,与直接在表单上绘制线条和圆相比有很多优势,例如少闪烁。每当您在表单上绘制某些东西时,请记住它经常被移除(例如,当您将表单移到屏幕区域之外时)。您需要使用表单的 Paint 事件来刷新它。

在调用Update() 之后,您需要在再次调用Update 之前将场景重绘到backBuffer,因为位图在绘制到屏幕后会被Clear() 涂黑。

【讨论】:

非常感谢!那是非常有用的。我意识到我感到困惑的主要原因是我不知道在初始化后缓冲区后我不知道它会专门绘制到位图。谢谢你澄清这个。我有一种感觉,这就是正在发生的事情,但我不确定。正如你所知,我没有使用绘画事件,而是通过在每一帧调用 Application.DoEvents 来使用我自己的方法。非常感谢! 您似乎对@MathewO'Dwyer 这个答案很满意?如果是这样,请将其标记为已接受,以便其他人在同一领域寻求帮助时可以快速找到正确答案,并感谢 Jens。 @FSDaniel 好的,第一次这样做很抱歉 :)。 @FSDaniel 我应该如何去赞扬 jens? 没问题。看你想通了:)只要接受他的回答,他就会得到分数。【参考方案2】:

frontBuffer 正在更新,因为每次您在 update() 中调用 frontBuffer.DrawImageUnscaled(backBufferBitmap,0,0);

backBuffer 正在清除,因为您正在调用 backBuffer.Clear(Color.Black);

另外,initialize() 应该只被调用一次。在创建对象时。而且我相信这是一个更大的程序的一部分,其中父母正在调用孩子的update()

【讨论】:

你确定这是你的疑问吗? 嘿,谢谢你的回答。但是,Initialize 和 Update 是我自己创建的方法。我不明白的是位图是如何更新的? 我的意思是为什么我要调用 backBuffer.clear 而不是 frontBuffer.clear ?为什么我必须这样做? 如果backBufferBitmap 正在更新,那么您将多次调用initialize()。 那么当前没有更新它,你应该只是得到一个黑屏。

以上是关于这段代码是如何工作的? (GDI,C#)的主要内容,如果未能解决你的问题,请参考以下文章

使用带有内存流的 c# 在控制台应用程序中保存图像时,GDI + 中发生一般错误

c#中利用system.timers多线程做图像处理,图像保存时提示“GDI+ 中发生一般性错误”,如何解决?

C# wpf 使用GDI+实现截屏

如何在 C# 中捕获 Windows 应用商店应用程序的窗口内容

C# 获取指定 “QQ头像“ 绘制 “圆形头像框“GDI(Graphics)

C# 获取指定 “QQ头像“ 绘制 “圆形头像框“GDI(Graphics)