Introduction

Recently, I needed to display some JPG and PNG files. I had an old copy of
LeadTools and the open source libraries for both formats, but wanted my
executable to be as small as possible. So I decided to give GDI+ a try. I
quickly found GDI+ to be poorly designed and very quirky, but it worked well
for my purposes until I discovered, to my horror, that GDI+ cannot load JPG or
PNG images stored as resources!

Like, I'm sure, other developers facing this issue, I disbelieved the
documentation and tried Bitmap::FromResource to no avail. While
perusing the Bitmap methods available, I ran across Bitmap::FromStream.

After a bit of testing and several errors, due mostly to the horrible GDI+
documentation, I came up with working code. After a night of rest, I decided to
encapsulate the code in a simple class to ensure memory got freed. The result
were two classes: CGdiPlusBitmap and CGdiPlusBitmapResource.

The Gotcha

Before discussing the code itself, there is a caveat with GDI+ that must be
addressed. With JPG, some TIFF and other formats, the original image
information must be available at all times. In other words, if you open a
bitmap using Bitmap::FromFile, you cannot delete or otherwise
change that file while the image is open. This same restriction applies to CGdiPlusBitmapResource.
(My testing found that PNG and BMP files don't seem to follow this
generalization, though I don't know if this is standard behavior or just a
fluke with my file set.)

GDI+ Initialization

GDI+ needs to be initialized before any GDI+ calls are made. I suggest adding
the following data member to the class derived from CWinApp:

The Class

I created two classes, with the base class being a very simple encapsulation of
Bitmap and the derived class encapsulating the global memory. I
suppose that if I ever had the patience and desire, I could extend that class,
but I have no need to do so. (If you're curious why I didn't simply derive from
the ATL class CImage, it's because the code was used in a
program that didn't use MFC or ATL. However, the code is so simple, it could
easily be modified to use CImage as the base class.)

I'm not going to bother going over the CGdiPlusBitmap class except
to say that it has a single, public, data member Bitmap* m_pBitmap.
(In the class I prefaced the GDI+ objects with the Gdiplus namespace
in case the developer doesn't want to declare using namespace Gdiplus;.)

The CGdiPlusBitmapResource class has several constructors and
several overloaded Load functions. The overloaded functions simply
allow lazy programmers, like myself, to not have to type MAKEINTRESOURCE.
The main Load function takes the resource name and type as strings
and is the key to the class. This code follows in its entirety:

I find the code very self explanatory, though those that know the return value
of ::LoadResource is an HGLOBAL may find the apparent
double copy using CopyMemory confusing. In brief CreateStreamOnHGlobal
requires a HGLOBAL handle allocated by GlobalAlloc using
the GMEM_MOVEABLE flag.

The Demo

The demo is a Visual Studio .NET 2003 project with ANSI and UNICODE builds. It
allows you to load resampled JPG or PNG resources (For the curious, I took both
photographs in Oahu, Hawaii for, and while filming content of, a multimedia
product. One is of Laie Bay, the other is a sunset viewed from Waikiki.)

GDI+ Disclaimer

I am not a GDI+ expert, nor am I a big fan, even if I do find GDI+ occasionally
very useful. Please don't ask me questions about it.

Why not IPicture?

I've been asked why I didn't use IPicture. The answer is threefold;
first, IPicture does not support PNG images. Second, IPicture
is pretty much only an image loader, with little more capability than the
standard GDI bitmap calls. Third, IPicture decodes the image data
immediately. JPG and GIF images will use more memory than this class.

Updates

22 April 2004

CreateStreamOnHGlobal now uses FALSE for the second
argument since Bitmap requires that the memory be retained for at least JPG
images and I decided to err on the side of caution. Interestingly, my testing
showed this flag is often ignored, but I received reports that this wasn't
always the case and was technically incorrect.

In addition, while fixing this bug I realized I wasn't clearing up the global
memory on failure. That resulted in the code being rearranged as it is now.

15 June 2004

If Gdiplus::Bitmap::FromStream fails, I added what I believe is
redundant, but more correct code in handling m_pBitmap.
(Unfortunately, the documentation is silent on the subject as to whether NULL
will always be returned on failure.)

3 September 2004

Added a section on GDI+ initialization.

Fixed a potential memory leak on image load failure in the sample application.

Share

About the Author

Joe is one of those software engineers with a film degree. His first paid programming job (what, you think film is a good way to make a living?) was writing games for Apple II's using 6502 assembly. He soon moved to 80x86 assembly, C, C++ (for a long time), C# and then back to C++ with some work in C#.

He first wrote software for Windows 3.0 in 1990. Save for some continued work in DOS and a horrid, and mercifully brief, foray into OS/2, he has concentrated on designing and writing software for all versions and types of Windows except RT.

there is a plugin called AddText.api . Can u please tell how to use that API for my Visual C++ Application.
In that API there is a Method to create TEXT in a PDF File.
I don't know how to use API's for a Visual C++ Application..
Thanks in advance....

I'm trying to load a Bitmap from the resource.
CGdiPlusBitmapResource* m_pBitmap = new CGdiPlusBitmapResource;
m_pBitmap->Load(IDB_BITMAP1, RT_BITMAP); It fails in Gdiplus::Bitmap::FromStream with the GetLastStatus returning InvalidParameter. Any guess as to where my problem is?

Is there a memory leak if the m_pBitmap->GetLastStatus() does not return Ok.
In this case you set the m_pBitmap to NULL. And the m_pBitmap object might have been allocated some memory. Better way to do it is to call Empty().

Hi,
I want to apply Bitmap::ConvertFormat () method for my project. But above method is not in the GDI+ version 1.0 .So I have to download GDI+ version 1.1. From where can I download it? ,I search it .But I didn't get suitable URL for that. please help
Is there other way to do this?.

Then what do I need to do?
However I want to add the Bitmap::ConvertFormat Method to my Project.MSDN says that Minimum requirement for above method is ading GDI+ version 1.1. Then How do I use above Method?
Help me please

To save a JPEG you can do the following. This is just a code snippet, but you can use MSDN to fill out the details. (I have no idea of what the encoderParameters are for PNG so this is the best I can do.):

The memory that the Bitmap object uses must remain accessable to that object. As long as the bitmap object is using that memory it must remain locked.

As for CreateStreamOnHGlobal, you don't want to set the second parameter to TRUE for the same reason.

Moreover, there is nothing "unsafe" with keeping a GlobalLock() "open" on memory for indeterminate periods of time. It's simply memory. (Your local C++ heap uses locked memory for the life of the program. The program itself is "locked" to memory locations.)

Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke

So the second parameter to CreateStreamOnHGlobal() should *not* be TRUE, that is to say it should be FALSE; but in your code it is TRUE. I was having problems until I set it to FALSE.

The GlobalLock() part still confuses me. I'm actually reading a file (of a type unsupported by GDI+) and I can't call ReadFile(...) on a MOVEABLE memory block unless I first call GlobalLock() but I've been calling GlobalUnlock() right after I read it and it seems fine, but I didn't test all situations; that's why I'm confused.

So I'm doing this:
// GlobalAlloc() a moveable block the same size as my file
// gotta lock it
// read my custom image type into the memory
// now I unlock it
// create the stream with 2nd param to FALSE
// create the Bitmap from the stream
// release the stream

I'm used to malloc and new and don't fully understand locking memory; I also don't really understand IStream. I think the IStream is just a *way* to get stuff (memory) from one place to another and so the stream itself can be released after it's done it job, is that about right ? Your code helped me a lot, I just want to understand what's going one a little better. Thanks man.

I have conflicting information. When creating a GDI+ Bitmap from a file, it uses that file as backing store. The documentation is very unclear whether Bitmap does the same thing. After experimenting, I found it entirely depended on the type of image being loaded. It appears that JPG and TIFF images require the buffer to remain, but PNG images do not.

(I also confirmed that CreateStreamOnHGlobal doesn't always free locked memory if the second parameter is TRUE, at least on some platforms.)

GlobalAlloc(GMEM_MOVEABLE, <somesize>) allocates a moveable block of memory. The system can move this block if it needs to. By locking the memory, you obtain an address and can use the block without worry the system will move it behind your back.

It is entirely possible that Bitmap is actually storing the GlobalHandle internally and simply locing it when required. Since nothing in the documentation states that, I have to assume it doesn't. I may try to test this later.

Anyone who thinks he has a better idea of what's good for people than people do is a swine.
- P.J. O'Rourke

the demo runs.
i build it into another project and i´ve the same problem.
the view is the same, the resources are the same.
i do it into a lib, in one workspace.
one simple project(Doc/View) runs,
the other with the same View did not!
i think i have checked all but...