Managed Gdi Wrapper for Better Performance

The days of Avalon have not come yet and Gdi Plus is surely a nice API to create wonderfull UI whith transparency and gradients, but it has a plumbing caveats : It cannot benefit of graphic cards acceleration.

It is real problem since people user bigger screens with better pitch at higher resolution. Drawing all your controls using the CPU makes your UI slower. You'll tell me 'what else can I do...'

Often, only parts of you drawing actually need Gdi+. Filling the background (often the longuest part) with a solid color can be made with GDI, copying a image or icon without transparency can also be done faster using this good old API.

The problem then is to make all this boring API calls. We will classes that mimic System.Drawing but using Gdi to make all this interop easier.

The first step is to encapsulate the device context represented by its handle (hdc) in a class.

Code Copy HideScrollFull
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Cobra.Windows.Forms.Interop;

namespace Cobra.Windows.Forms.Gdi
{
/// <summary>
/// Represents a Gdi device context.
/// </summary>
public class DeviceContext : IDisposable
{
#region Fields

private IntPtr hdc;

#endregion

#region
Instance management

/// <summary>
/// Initializes a new instance of the <see cref="DeviceContext"/> class.
/// </summary>
/// <param name="hdc">The device context handle to wrap.</param>
protected DeviceContext(IntPtr hdc)
{
this.hdc = hdc;
}

/// <summary>
/// Creates a <see cref="DeviceContext"/> wrapper around specified HDC.
/// </summary>
/// <param name="hdc">The device context handler to wrap.</param>
/// <returns></returns>
public static DeviceContext Attach(IntPtr hdc)
{
return new DeviceContext(hdc);
}

/// <summary>
/// Creates a <see cref="DeviceContext"/> from a <see cref="Graphics"/> object.
/// </summary>
/// <param name="graphics">The <see cref="Graphics"/> object to wrap.</param>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext FromGraphics(Graphics graphics)
{
return new GraphicsDeviceContext(graphics);
}

/// <summary>
/// Creates a <see cref="DeviceContext"/> to paint a window.
/// </summary>
/// <param name="hwnd">The handle of the window.</param>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext FromHWnd(IntPtr hwnd)
{
return new WindowDeviceContext(hwnd, NativeGdi.GetWindowDC(hwnd));
}

/// <summary>
/// Creates a <see cref="DeviceContext"/> to paint a window.
/// </summary>
/// <param name="hwnd">The handle of the window.</param>
/// <param name="region">The handle of a region.</param>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext FromHWnd(IntPtr hwnd, IntPtr region)
{
return new WindowDeviceContext(hwnd, NativeGdi.GetDCEx(hwnd, region, NativeGdi.DCX_WINDOW | NativeGdi.DCX_INTERSECTRGN));
}

/// <summary>
/// Creates a new <see cref="DeviceContext"/> compatible width given device context.
/// </summary>
/// <param name="hdc">The handle of the device context.</param>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext CreateCompatible(IntPtr hdc)
{
IntPtr compatibleHdc = NativeGdi.CreateCompatibleDC(hdc);
return new CompatibleDeviceContext(compatibleHdc);
}

/// <summary>
/// Creates a new <see cref="DeviceContext"/> compatible width given device context.
/// </summary>
/// <param name="dc">The original <see cref="DeviceContext"/> object.</param>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext CreateCompatible(DeviceContext dc)
{
IntPtr compatibleHdc = NativeGdi.CreateCompatibleDC(dc.Handle);
return new CompatibleDeviceContext(compatibleHdc);
}

/// <summary>
/// Creates a screen device context.
/// </summary>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext FromScreen()
{
return FromHWnd(IntPtr.Zero);
}

/// <summary>
/// Creates a <see cref="DeviceContext"/> compatible with screen.
/// </summary>
/// <returns>A new <see cref="DeviceContext"/> object.</returns>
public static DeviceContext CreateScreenCompatible()
{
IntPtr screen = NativeGdi.GetDC(IntPtr.Zero);
try
{
return new CompatibleDeviceContext( NativeGdi.CreateCompatibleDC(screen) );
}
finally
{
NativeGdi.ReleaseDC(IntPtr.Zero, screen);
}
}

#endregion

#region
Dispose and finalization

/// <summary>
/// Releases internal resources.
/// </summary>
~DeviceContext()
{
Dispose(false);
}

/// <summary>
/// Releases internal resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
hdc = IntPtr.Zero;
}

#endregion

#region
Properties

/// <summary>
/// Gets device context handle.
/// </summary>
public IntPtr Handle
{
get { return hdc; }
}

#endregion

#region
Methods

/// <summary>
/// Creates a new clipping region that consists
/// of the existing clipping region minus the specified rectangle
/// </summary>
/// <param name="rect">The rectangle used to modify the clipping region.</param>
public void ExcludeClip(Rectangle rect)
{
NativeGdi.ExcludeClipRect(hdc, rect.Left, rect.Top, rect.Right, rect.Bottom);
GC.KeepAlive(this);
}

/// <summary>
/// Creates a new clipping region from the intersection
/// of the current clipping region and the specified rectangle.
/// </summary>
/// <param name="rect">The rectangle used to modify the clipping region.</param>
public void IntersectClip(Rectangle rect)
{
NativeGdi.IntersectClipRect(hdc, rect.Left, rect.Top, rect.Right, rect.Bottom);
GC.KeepAlive(this);
}

/// <summary>
/// fills a rectangle by using the specified brush.
/// This function includes the left and top borders,
/// but excludes the right and bottom borders of the rectangle.
/// </summary>
/// <param name="brush">The brush used to fill the rectangle.</param>
/// <param name="rect">The rectangle to be filled. </param>
public void FillRectangle(GdiBrush brush, Rectangle rect)
{
NativeGdi.FillRect(hdc, rect, brush.Handle);
GC.KeepAlive(this);
}

/// <summary>
/// Draws an image in the device context.
/// </summary>
/// <param name="image">The image to draw.</param>
/// <param name="point">The location where the image should be drawn.</param>
public void DrawImageUnscaled(GdiBitmap image, Point point)
{
DrawImageUnscaled(image, point.X, point.Y, image.Width, image.Height);
GC.KeepAlive(this);
}

/// <summary>
/// Draws an image in the device context.
/// </summary>
/// <param name="image">The image to draw.</param>
/// <param name="x">The x coordinate of the location
/// where the image should be drawn.</param>
/// <param name="y">The y coordinate of the location
/// where the image should be drawn.</param>
public void DrawImageUnscaled(GdiBitmap image, int x, int y)
{
DrawImageUnscaled(image, x, y, image.Width, image.Height);
GC.KeepAlive(this);
}

/// <summary>
/// Draws an image in the device context.
/// </summary>
/// <param name="image">The image to draw.</param>
/// <param name="rectangle">The rectangle where
/// the image should be drawn.</param>
public void DrawImageUnscaled(GdiBitmap image, Rectangle rectangle)
{
DrawImageUnscaled(image, rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
GC.KeepAlive(this);
}

/// <summary>
/// Draws an image in the device context.
/// </summary>
/// <param name="image">The image to draw.</param>
/// <param name="x">The x coordinate of the rectangle
/// where the image should be drawn.</param>
/// <param name="y">The y coordinate of the rectangle
/// where the image should be drawn.</param>
/// <param name="width">The width of the rectangle
/// where the image should be drawn.</param>
/// <param name="height">The height of the rectangle
/// where the image should be drawn.</param>
public void DrawImageUnscaled(GdiBitmap image, int x, int y, int width, int height)
{
using (DeviceContext sourceContext = CreateCompatibleDeviceContext())
{
using (GdiSelect select = sourceContext.SelectObject(image))
NativeGdi.BitBlt(hdc, x, y, width, height, sourceContext.Handle, 0, 0, NativeGdi.SRCCOPY);
}
}

/// <summary>
/// Saves the state of the device context.
/// </summary>
/// <returns>A <see cref="DeviceContextState"/> object containing device state.</returns>
public DeviceContextState Save()
{
GC.KeepAlive(this);
return new DeviceContextState(NativeGdi.SaveDC(hdc));
}

/// <summary>
/// Restores the state of the device context.
/// </summary>
/// <param name="state">The <see cref="DeviceContextState"/>
/// object containing the device state.</param>
/// <remarks>The <see cref="DeviceContextState"/> is obtained
/// by calling <see cref="Save"/>.</remarks>
public void Restore(DeviceContextState state)
{
NativeGdi.RestoreDC(hdc, state.state);
GC.KeepAlive(this);
}

/// <summary>
/// Applies a translate transformation to the device context.
/// </summary>
/// <param name="x">The x coordinate of the offset.</param>
/// <param name="y">The y coordinate of the offset.</param>
public void TranslateTransform(int x, int y)
{
NativeGdi.OffsetWindowOrgEx(hdc, x, y, IntPtr.Zero);
GC.KeepAlive(this);
}

/// <summary>
/// Applies a translate transformation to the device context.
/// </summary>
/// <param name="point">The offset of the translation.</param>
public void TranslateTransform(Point point)
{
TranslateTransform(point.X, point.Y);
}

/// <summary>
/// Selects a <see cref="GdiObject"/> in the device context.
/// </summary>
/// <param name="gdiObject">The <see cref="GdiObject"/> to select.</param>
/// <returns>A <see cref="GdiSelect"/> disposable object
/// used to deselect from device context.</returns>
public GdiSelect SelectObject(GdiObject gdiObject)
{
GC.KeepAlive(this);
return new GdiSelect(NativeGdi.SelectObject( new HandleRef(this, hdc), new HandleRef(gdiObject, gdiObject.Handle)), hdc);
}

/// <summary>
/// Creates a compatible device context.
/// </summary>
/// <returns>The newly created device context.</returns>
public DeviceContext CreateCompatibleDeviceContext()
{
return new CompatibleDeviceContext(NativeGdi.CreateCompatibleDC(hdc));
}

#endregion

#region
Specialized inner classes

private class GraphicsDeviceContext : DeviceContext
{
private Graphics graphics;

internal GraphicsDeviceContext(Graphics graphics) : base(graphics.GetHdc())
{
this.graphics = graphics;
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
if (Handle != IntPtr.Zero)
graphics.ReleaseHdc(Handle);
}

base.Dispose(disposing);
}
}

private class CompatibleDeviceContext : DeviceContext
{
internal CompatibleDeviceContext(IntPtr hdc) : base(hdc)
{}

protected override void Dispose(bool disposing)
{
if (Handle != IntPtr.Zero)
NativeGdi.DeleteDC(Handle);
base.Dispose(disposing);
}
}

private class WindowDeviceContext : DeviceContext
{
private IntPtr hwnd;

internal WindowDeviceContext(IntPtr hwnd, IntPtr hdc) : base(hdc)
{
this.hwnd = hwnd;
}

protected override void Dispose(bool disposing)
{
if (Handle != IntPtr.Zero)
NativeGdi.ReleaseDC(hwnd, Handle);
base.Dispose(disposing);
}
}

#endregion
}
}
. . .

Here is the interop code :

Code Copy HideScrollFull
using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Cobra.Windows.Forms.Interop
{
/// <summary>
/// Native interop for Gdi.
/// </summary>
internal sealed class NativeGdi
{
private NativeGdi(){}

#region Gdi structures

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;

public POINT(int x, int y)
{
this.x = x;
this.y = y;
}

public POINT(Point pt) : this(pt.X,pt.Y){}
}


[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;

public SIZE(int width, int height)
{
this.cx = width;
this.cy = height;
}

public SIZE(Size sz) : this(sz.Width,sz.Height) {}
}


[StructLayout(LayoutKind.Sequential)]
public class RECT
{
public int l;
public int t;
public int r;
public int b;

public RECT(int l,int t,int r,int b)
{
this.l = l;
this.t = t;
this.r = r;
this.b = b;
}

public RECT() {}

public RECT(Rectangle rect) : this(rect.Left,rect.Top,rect.Right,rect.Bottom) {}

public Rectangle ToRectangle()
{
return Rectangle.FromLTRB(l,t,r,b);
}

public static implicit operator Rectangle(RECT rect)
{
return rect.ToRectangle();
}

public static implicit operator RECT(Rectangle rect)
{
return new RECT(rect);
}
}

#endregion

#region
Create/delete DC

[DllImport("Gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(
IntPtr hdc   // handle to DC
);
[DllImport("Gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteDC(
IntPtr hdc   // handle to DC
);
#endregion

#region
Get/release DC

[DllImport("User32.dll")]
public static extern IntPtr GetWindowDC(
IntPtr hWnd   // handle to window
);
[DllImport("user32.dll", SetLastError=true)]
public static extern IntPtr GetDC(IntPtr hWnd);

public const int DCX_WINDOW           = 0x00000001;
public const int DCX_CACHE            = 0x00000002;
public const int DCX_NORESETATTRS     = 0x00000004;
public const int DCX_CLIPCHILDREN     = 0x00000008;
public const int DCX_CLIPSIBLINGS     = 0x00000010;
public const int DCX_PARENTCLIP       = 0x00000020;
public const int DCX_EXCLUDERGN       = 0x00000040;
public const int DCX_INTERSECTRGN     = 0x00000080;
public const int DCX_EXCLUDEUPDATE    = 0x00000100;
public const int DCX_INTERSECTUPDATE  = 0x00000200;
public const int DCX_LOCKWINDOWUPDATE = 0x00000400;
public const int DCX_VALIDATE         = 0x00200000;

[DllImport("user32.dll", SetLastError=true)]
public static extern IntPtr GetDCEx(
IntPtr hWnd,      // handle to window
IntPtr hrgnClip,  // handle to clipping region
int flags     // creation options
);

[DllImport("User32.dll")]
public static extern int ReleaseDC(
IntPtr hWnd,  // handle to window
IntPtr hDC     // handle to DC
);
#endregion

#region
Save/restore DC

[DllImport("Gdi32.dll")]
public static extern int SaveDC(
IntPtr hdc   // handle to DC
);
[DllImport("Gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RestoreDC(
IntPtr hdc,       // handle to DC
int nSavedDC   // restore state
);
#endregion

#region
Clipping

[DllImport("Gdi32.dll")]
public static extern int ExcludeClipRect(
IntPtr hdc,         // handle to DC
int nLeftRect,   // x-coord of upper-left corner
int nTopRect,    // y-coord of upper-left corner
int nRightRect,  // x-coord of lower-right corner
int nBottomRect  // y-coord of lower-right corner
);
[DllImport("Gdi32.dll")]
public static extern int IntersectClipRect(
IntPtr hdc,         // handle to DC
int nLeftRect,   // x-coord of upper-left corner
int nTopRect,    // y-coord of upper-left corner
int nRightRect,  // x-coord of lower-right corner
int nBottomRect  // y-coord of lower-right corner
);
#endregion

#region
Drawing

[DllImport("User32.dll")]
public static extern int FillRect(
IntPtr hDC,           // handle to DC
[MarshalAs(UnmanagedType.LPStruct)] RECT lprc,  // rectangle
IntPtr hbr         // handle to brush
);

[DllImport("Gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(
IntPtr hdcDest, // handle to destination DC
int nXDest,  // x-coord of destination upper-left corner
int nYDest,  // y-coord of destination upper-left corner
int nWidth,  // width of destination rectangle
int nHeight, // height of destination rectangle
IntPtr hdcSrc,  // handle to source DC
int nXSrc,   // x-coordinate of source upper-left corner
int nYSrc,   // y-coordinate of source upper-left corner
int dwRop  // raster operation code
);
public const int SRCCOPY            =0x00CC0020 /* dest = source                   */;

#endregion

#region
Transfoms

[DllImport("Gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OffsetWindowOrgEx(
IntPtr hdc,          // handle to device context
int nXOffset,     // horizontal offset
int nYOffset,     // vertical offset
IntPtr lpPoint   // original origin
);
#endregion

#region
Select

[DllImport("Gdi32.dll")]
public static extern IntPtr SelectObject(
HandleRef hdc,          // handle to DC
HandleRef hgdiobj   // handle to object
);
#endregion

#region
Brushes

[DllImport("Gdi32.dll")]
public static extern IntPtr CreateSolidBrush(
int crColor   // brush color value
);
#endregion

#region
Object Deletion

[DllImport("Gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(
IntPtr hObject   // handle to graphic object
);
#endregion
}
}
. . .

There are several important parts in the DeviceContext class. First of all the instance management. The hard part is Disposing the context because it depends on how the hdc was acquiered. This is easily solved using private specialized inner classes for each management type.

The Attach static method is used to create a simple wrapper around a device context handle that has been created elsewhere. A DeviceContext object created with this method won't delete neither release the HDC, the caller is responsible for this. Internally, a DeviceContext object is created.

The FromHWnd static method gets a HDC for the window and release it when the DeviceContext object is disposed. Internally, a WindowDeviceContext object is created.

The FromGraphics static method uses the GetDC method from the Graphics object to gets its handle, and releases it when disposed. Internally a GraphicsDeviceContext object is created.

The CreateCompatible and FromScreen static methods create a device context compatible with specified DC or with the screen, and delete is when disposed. Internally, a CompatibleDeviceContext object is created.

The Save and Restore methods use the very simple DeviceContextState class:

Code Copy HideScrollFull
namespace Cobra.Windows.Forms.Gdi
{
/// <summary>
/// Represents a saved <see cref="DeviceContext"/> state.
/// </summary>
/// <remarks>This class is created by the <see cref="DeviceContext.Save"/> mehtod.</remarks>
public sealed class DeviceContextState
{
internal int state;

internal DeviceContextState(int state)
{
this.state = state;
}
}
}
. . .

I decided to use a IDisposable pattern for object selection to make things more straight forward :

Code Copy HideScrollFull
using System;
using System.Runtime.InteropServices;
using Cobra.Windows.Forms.Interop;

namespace Cobra.Windows.Forms.Gdi
{
/// <summary>
/// Represents information necessary to revert an object selection in a <see cref="DeviceContext"/>.
/// </summary>
/// <remarks>This object is created by the <see cref="DeviceContext.SelectObject"/> method.</remarks>
public sealed class GdiSelect : IDisposable
{
private IntPtr hObject;
private IntPtr hdc;

internal GdiSelect(IntPtr hObject,IntPtr hdc)
{
this.hObject = hObject;
this.hdc = hdc;
}


/// <summary>
/// Releases the internal resources.
/// </summary>
~GdiSelect()
{
InternalDispose();
}

/// <summary>
/// Revert the selection in the <see cref="DeviceContext"/>.
/// </summary>
public void Dispose()
{
InternalDispose();
GC.SuppressFinalize(this);
}

private void InternalDispose()
{
NativeGdi.SelectObject(new HandleRef(null, hdc),new HandleRef(null,hObject));
GC.KeepAlive(this);
}
}
}
. . .

The next part is about Gdi objects. They all inherit from the GdiObject abstract class to stay close to Gdi infrastructure :

Code Copy HideScrollFull
using System;

namespace Cobra.Windows.Forms.Gdi
{
/// <summary>
/// Represents a Gdi graphic object.
/// </summary>
public abstract class GdiObject : IDisposable
{
IntPtr handle;

/// <summary>
/// Initializes a new instance of the <see cref="GdiObject"/> class.
/// </summary>
/// <param name="handle">A native GDI handle to wrap into a <see cref="GdiObject"/> instance.</param>
protected GdiObject(IntPtr handle)
{
this.handle = handle;
}

/// <summary>
/// Finalizes <see cref="GdiObject"/>.
/// </summary>
~GdiObject()
{
Dispose(false);
}

/// <summary>
/// Releases internal resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}


/// <summary>
/// Override to release internal resources.
/// </summary>
/// <param name="disposing"><c>false</c> when called from Finalize.</param>
protected virtual void Dispose(bool disposing)
{
}

/// <summary>
/// Gets the handle of the Gdi object.
/// </summary>
public IntPtr Handle
{
get
{
return handle;
}
}

/// <summary>
/// Clears the handle of the Gdi object.
/// </summary>
protected void ClearHandle()
{
this.handle = IntPtr.Zero;
}
}
}
. . .

The GdiBrush class represents a Gdi solid brush :

Code Copy HideScrollFull
using System;
using System.Drawing;
using Cobra.Windows.Forms.Interop;

namespace Cobra.Windows.Forms.Gdi
{
/// <summary>
/// Represents a Gdi brush.
/// </summary>
public class GdiBrush : GdiObject
{
private bool immutable;

/// <summary>
/// Initializes a new instance of the <see cref="GdiBrush"/> class.
/// </summary>
/// <param name="color">The color of the brush.</param>
/// <remarks>The brush is deleted when disposed.</remarks>
public GdiBrush(Color color) : base(NativeGdi.CreateSolidBrush(ColorTranslator.ToWin32(color)))
{
}

/// <summary>
/// Initializes a new instance of the <see cref="GdiBrush"/> class.
/// </summary>
/// <param name="brush">The handle of the brush object.</param>
/// <param name="immutable"><c>true</c> if the object should not be deleted when released.</param>
public GdiBrush(IntPtr brush, bool immutable) : base(brush)
{
this.immutable = immutable;
}



/// <summary>
/// Releases internal resources.
/// </summary>
/// <param name="disposing"><c>false</c> if called from Finalize.</param>
protected override void Dispose(bool disposing)
{
if (!immutable && Handle != IntPtr.Zero)
{
NativeGdi.DeleteObject(Handle);
ClearHandle();
}
}
}
}
. . .

The GdiBitmap class :

Code Copy HideScrollFull
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Cobra.Windows.Forms.Interop;

namespace Cobra.Windows.Forms.Gdi
{
/// <summary>
/// Represents a Gdi bitmap object.
/// </summary>
public class GdiBitmap : GdiObject
{
private Size size;
private bool sizeAvailable;
/// <summary>
/// Initializes a new instance of the <see cref="GdiBitmap"/> class.
/// </summary>
/// <param name="bitmap">Handle of a native Gdi bitmap.</param>
public GdiBitmap(IntPtr bitmap) : base(bitmap){}

/// <summary>
/// Creates a <see cref="GdiBitmap"/> from a Gdi Plus <see cref="Bitmap"/>.
/// </summary>
/// <param name="bitmap">The bitmap to convert to Gdi.</param>
/// <returns>A new <see cref="GdiBitmap"/> object.</returns>
public static GdiBitmap FromBitmap(Bitmap bitmap)
{
return new GdiBitmap(bitmap.GetHbitmap());
}

/// <summary>
/// Creates a <see cref="GdiBitmap"/> from a Gdi Plus <see cref="Bitmap"/>.
/// </summary>
/// <param name="bitmap">The bitmap to convert to Gdi.</param>
/// <param name="color">The color used for transparent areas.</param>
/// <returns>A new <see cref="GdiBitmap"/> object.</returns>
public static GdiBitmap FromBitmap(Bitmap bitmap, Color color)
{
return new GdiBitmap(bitmap.GetHbitmap(color));
}


/// <summary>
/// Releases internal resources.
/// </summary>
/// <param name="disposing"><c>false</c> when called from Finalize.</param>
protected override void Dispose(bool disposing)
{
if (Handle != IntPtr.Zero)
{
NativeGdi.DeleteObject(Handle);
ClearHandle();
}
}


/// <summary>
/// Gets the size of the bitmap.
/// </summary>
public Size Size
{
get
{
if (!sizeAvailable)
LoadSize();
return size;
}
}

/// <summary>
/// Gets the width of the bitmap.
/// </summary>
public int Width
{
get
{
if (!sizeAvailable)
LoadSize();
return size.Width;
}
}

/// <summary>
/// Gets the height of the bitmap.
/// </summary>
public int Height
{
get
{
if (!sizeAvailable)
LoadSize();
return size.Height;
}
}

private void LoadSize()
{
NativeGdi.BITMAP info = new Cobra.Windows.Forms.Interop.NativeGdi.BITMAP();
NativeGdi.GetObject(Handle,Marshal.SizeOf(typeof(NativeGdi.BITMAP)),info);
size = new Size(info.bmWidth,info.bmHeight);

sizeAvailable = true;
}
}
}
. . .

There's finally a sample simple control base that clears its background using Gdi rather than Gdi+ to improve performance :

Code Copy HideScrollFull
using System.Windows.Forms;
using Cobra.Windows.Forms.Gdi;

namespace GdiDrawing
{
/// <summary>
/// A control that fill its background using Gdi rather than Gdi+.
/// </summary>
public class MyGdiCustomControl : Control
{
public MyGdiCustomControl()
{
}
protected override void OnPaintBackground(PaintEventArgs e)
{
using (DeviceContext dc = DeviceContext.FromGraphics(e.Graphics))
{
dc.IntersectClip(e.ClipRectangle);
using (GdiBrush brush = new GdiBrush(BackColor))
dc.FillRectangle(brush, ClientRectangle);
}
}
protected override void OnPaint(PaintEventArgs pe)
{
// make your painting here !
// you can mix Gdi and Gdi+ !!

// Calling the base class OnPaint
base.OnPaint(pe);
}
}
}
. . .

Of course, this class offers only a limited support for Gdi, but feel free to add what ever you need. The object model makes adding new features a glimps.

 

by Skup

posted on Tuesday, March 22, 2005 7:04 PM

Feedback

# re: Managed Gdi Wrapper for Better Performance 11/1/2005 4:52 AM andyk

Firstly, great work!

Secondly, the code listings seem to be missing definitions for NativeGdi.GetObject() and NativeGdi.BITMAP.

# re: Managed Gdi Wrapper for Better Performance 7/21/2008 9:19 AM Evgen

Can you re post interop code....

Thx

# re: Managed Gdi Wrapper for Better Performance 7/21/2008 9:20 AM Evgen

and what about NativeGdi.GetObject() and NativeGdi.BITMAP

Title
 
Name
 
Url
Comments   
Protected by Clearscreen.SharpHIPEnter the code you see: