Motive
How many of you developers are using GDI+? I reckon, not many. As a seasoned
Windows C++ programmer developing for desktop applications, I've been
using the archaic GDI on regular basis. Since the release of GDI+ (see (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdicpp)
I've been using GDI+ in new applications and also converting existing
applications to use GDI+. There numerous compelling reasons why you should be
using GDI+, here are some:
- Successor to GDI
- Compatible with .NET
- Optimizes many of the capabilities of GDI
- Supports: Gradient Brushes, Independent Path Objects, Transformations and
the Matrix Object, Scalable Regions, Alpha Blending and support for multiple
image formats:
- BMP
- GIF
- JPEG
- Exif
- PNG
- TIFF
- ICON
- WMF
- EMF
During my development of Windows GUI based applications with GDI+, I've come
across the need to display animated GIFs, whilst GDI+ does not support
displaying animated GIFs directly, in can be done with a little coding.
Implementation
Firstly lets look at drawing a simple image using GDI+.
void CMyWnd::OnPaint()
{
CPaintDC dc(this);
Graphics graphics(&dc);
Image image(L"Test.Gif");
graphics.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());
}
Straight away we can see the simplistic C++ interface for using GDI objects.
This makes it a sure fire way for using GDI objects. There is no need for using
SelectObject
to select GDI objects in and out of the device context.
For those of the readers who are not familiar with the format of an animated
GIF, animated GIF is actually a series of GIF frames with a delay time
associated each
frame, therefore each frame can have a different delay time.
My implementation of encapsulating an animated GIF functionality derives a
class called
from ImageEx
from the GDI+ Image
class. The first task
is to determine whether or not a GIF is of the animated kind. The following code
demonstrates this:
bool ImageEx::TestForAnimatedGIF()
{
UINT count = 0;
count = GetFrameDimensionsCount();
GUID* pDimensionIDs = new GUID[count];
GetFrameDimensionsList(pDimensionIDs, count);
m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
m_pPropertyItem = (PropertyItem*) malloc(nSize);
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
delete pDimensionIDs;
return m_nFrameCount > 1;
}
m_pPropertyItem->value
is actually a pointer to an array of
longs, each long is a delay time which corresponds to a GIF frame. Because
GetPropertyItem
returns a different size depending on what property using
interested in, a Size is require and its the programmer responsibility to
allocate and deallocate the memory associated with GetPropertyItem
.
The size is determined by calling GetPropertyItemSize
supplying
property tag your interested in.
Once the number of frames and delay times
have been retrieved from the image, a thread is created which calls to
DrawFrameGIF
until the object destructs. See DrawFrameGIF
below:
bool ImageEx::DrawFrameGIF()
{
::WaitForSingleObject(m_hPause, INFINITE);
GUID pageGuid = FrameDimensionTime;
long hmWidth = GetWidth();
long hmHeight = GetHeight();
HDC hDC = GetDC(m_hWnd);
if (hDC)
{
Graphics graphics(hDC);
graphics.DrawImage(this, m_rc.left, m_rc.top, hmWidth, hmHeight);
ReleaseDC(m_hWnd, hDC);
}
SelectActiveFrame(&pageGuid, m_nFramePosition++);
if (m_nFramePosition == m_nFrameCount)
m_nFramePosition = 0;
long lPause = ((long*) m_pPropertyItem->value)[m_nFramePosition] * 10;
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
return dwErr == WAIT_OBJECT_0;
}
One other interesting aspect of this class is it has the ability of loading a
image directly as a resource from an executable. I usually import my GIF into a
project and give it a resource type of "GIF" and then rename the resource ID from a
numeric constant to a string (see the example code).
Conclusion
If you familiar with the concepts and programming of GDI, GDI+ provides some
advance features and the interface almost mirrors the .NET GDI namespace. The
sample apps and code include the full listings for implementing animated GIF in
your code applications. For code reference just search for GDI+ within the code.
One thing I haven't included in this article is starting up and shutting the GDI+
subsystem.