The code featured in this gist is the result of article posted on my blog, here. It's used to demonstrate a way to handle real-time graphics rendering using GDI+, which is usually used in single-threaded message-based GUI applications.
Last active
March 29, 2020 06:12
-
-
Save softwareantics/736e7cab18ee05157d954c44eb689b2b to your computer and use it in GitHub Desktop.
Real-Time Graphics Rendering with GDI+ in C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace RealTimeGDIExample | |
{ | |
using System; | |
using System.ComponentModel; | |
using System.Drawing; | |
using System.Windows.Forms; | |
public class Display : Form | |
{ | |
public Display(int width, int height, string title) | |
{ | |
this.Width = width; | |
this.Height = height; | |
this.Text = title; | |
this.StartPosition = FormStartPosition.CenterScreen; | |
} | |
public bool IsClosing { get; private set; } | |
protected override void OnClosing(CancelEventArgs e) | |
{ | |
// Close if we haven't cancelled the operation. | |
this.IsClosing = !e.Cancel; | |
base.OnClosing(e); | |
} | |
protected override void OnPaint(PaintEventArgs e) | |
{ | |
/* | |
e.Graphics.Clear(Color.CornflowerBlue); | |
for (int i = 0; i < 100; i++) | |
{ | |
for (int j = 0; j < 100; j++) | |
{ | |
e.Graphics.DrawRectangle(Pens.White, new Rectangle((i * 32), j * 32, 32, 32)); | |
} | |
} | |
*/ | |
base.OnPaint(e); | |
} | |
} | |
public sealed class GraphicsDevice : IDisposable | |
{ | |
private BufferedGraphics bufferedGraphics; | |
private BufferedGraphicsContext context; | |
private bool isDisposed; | |
public GraphicsDevice(IntPtr handle, Size bufferSize) | |
{ | |
this.context = new BufferedGraphicsContext() | |
{ | |
MaximumBuffer = bufferSize, | |
}; | |
// Create the buffered graphics object. | |
this.bufferedGraphics = this.context.Allocate(Graphics.FromHwnd(handle), new Rectangle(Point.Empty, bufferSize)); | |
} | |
~GraphicsDevice() | |
{ | |
this.Dispose(false); | |
} | |
public Graphics Graphics | |
{ | |
get { return this.bufferedGraphics.Graphics; } | |
} | |
public void Dispose() | |
{ | |
this.Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
public void SwapBuffers() | |
{ | |
// Writes the contents of the back buffer, to the front buffer (the default device, in our case, the display). | |
this.bufferedGraphics.Render(); | |
} | |
private void Dispose(bool disposing) | |
{ | |
if (this.isDisposed) | |
{ | |
return; | |
} | |
if (disposing) | |
{ | |
// Dispose is reverse order. | |
if (this.bufferedGraphics != null) | |
{ | |
this.bufferedGraphics.Dispose(); | |
this.bufferedGraphics = null; | |
} | |
if (this.context != null) | |
{ | |
this.context.Dispose(); | |
this.context = null; | |
} | |
} | |
this.isDisposed = true; | |
} | |
} | |
internal static class Program | |
{ | |
private static void Main() | |
{ | |
var display = new Display(800, 600, "Real-Time GDI Rendering") | |
{ | |
Visible = true | |
}; | |
var graphicsDevice = new GraphicsDevice(display.Handle, display.ClientSize); | |
Graphics graphics = graphicsDevice.Graphics; | |
display.Resize += (s, e) => | |
{ | |
if (graphicsDevice != null) | |
{ | |
graphicsDevice.Dispose(); | |
graphicsDevice = null; | |
} | |
graphicsDevice = new GraphicsDevice(display.Handle, display.ClientSize); | |
graphics = graphicsDevice.Graphics; | |
}; | |
while (!display.IsClosing) | |
{ | |
graphics.Clear(Color.CornflowerBlue); | |
for (int i = 0; i < 100; i++) | |
{ | |
for (int j = 0; j < 100; j++) | |
{ | |
graphics.DrawRectangle(Pens.White, new Rectangle(i * 32, j * 32, 32, 32)); | |
} | |
} | |
graphicsDevice.SwapBuffers(); | |
Application.DoEvents(); | |
} | |
graphicsDevice.Dispose(); | |
display.Dispose(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment