Skip to content

Instantly share code, notes, and snippets.

@softwareantics
Last active March 29, 2020 06:12
Show Gist options
  • Save softwareantics/736e7cab18ee05157d954c44eb689b2b to your computer and use it in GitHub Desktop.
Save softwareantics/736e7cab18ee05157d954c44eb689b2b to your computer and use it in GitHub Desktop.
Real-Time Graphics Rendering with GDI+ in C#

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.

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