Resizing an image with GDI+ with C++

Resizing images

Resizing images is a fairly common need; there are lots of libraries around that do this. However, I just needed to create thumbnails on-the-fly, and I certainly didn’t want to triple the size of my binary by linking against ImageMagick just to support resizing common image types.

GDI+ has the capability to do this, but surprisingly I was unable to find a clear example of how to do this just using GDI+ in C++ — lots of examples in C#, but I needed to do it in C++.

In the hopes that the next person to attempt this will simply find this blog post, here is how to do it in 6 easy(ish) steps:

Step 1: Initialize GDI+

If you haven’t already done so, you need to initailize GDI+. This is easy, and can be done with the following commands (also note the header file you need to include):

// We'll use these headers:#include <Gdiplus.h>#include <string>// In your header file, class, etc: ULONG_PTR(m_gdiplusToken);// Somewhere where it will run once before you need to use GDI: GdiplusStartupInput gdiplusstartupinput; GdiplusStartup(&m_gdiplusToken, &gdiplusstartupinput, NULL);

Step 2: Load the image:

std::wstring filename(L"someImage.jpg"); Gdiplus::Bitmap bmp(filename.c_str());

That was hard, wasn’t it?

Step 3: Resize the image:

I wrote a function from this. I adapted it from one I found somewhere, but now I can’t find the link. Note that this will preserve the aspect ratio of the image so that it fits in the specified boundaries but does not stretch. This means that if you specify 128×128, the resulting image might actually be something like 128×96, 102×128, etc.

Gdiplus::Bitmap* GDIPlusImageProcessor::ResizeClone(Bitmap *bmp, INT width, INT height){ UINT o_height = bmp->GetHeight(); UINT o_width = bmp->GetWidth(); INT n_width = width; INT n_height = height; double ratio = ((double)o_width) / ((double)o_height); if (o_width > o_height) { // Resize down by width n_height = static_cast<UINT>(((double)n_width) / ratio); } else { n_width = static_cast<UINT>(n_height * ratio); } Gdiplus::Bitmap* newBitmap = new Gdiplus::Bitmap(n_width, n_height, bmp->GetPixelFormat()); Gdiplus::Graphics graphics(newBitmap); graphics.DrawImage(bmp, 0, 0, n_width, n_height); return newBitmap;}

And then later after you load the image into bmp:

INT width = height = 128; Gdiplus::Bitmap *resizedBmp = ResizeClone(&bmp, width, height);

Step 4: Get the encoder:

The Gdiplus::Bitmap object stores the data in raw format so that it can be manipulated. At this point you get to decide what format to save it in. I just wanted to resize it, so I keep it in the original format. I created a simple map to map the supported image filenames to the mimetype, which we can use to find the correct encoder.

Here is what I store in my map:

m_mtMap[".jpeg"] = "image/jpeg"; m_mtMap[".jpe"] = "image/jpeg"; m_mtMap[".jpg"] = "image/jpeg"; m_mtMap[".png"] = "image/png"; m_mtMap[".gif"] = "image/gif"; m_mtMap[".tiff"] = "image/tiff"; m_mtMap[".tif"] = "image/tiff"; m_mtMap[".bmp"] = "image/bmp";

I found a utility function for looking up the encoder from the mimetype:

int GDIPlusImageProcessor::GetEncoderClsid(const WCHAR* form, CLSID* pClsid){ UINT num; UINT size; ImageCodecInfo* pImageCodecInfo=NULL; GetImageEncodersSize(&num,&size); if(size==0) return -1; pImageCodecInfo=(ImageCodecInfo*)(malloc(size)); if(pImageCodecInfo==NULL) return -1; GetImageEncoders(num,size,pImageCodecInfo); for(UINT j=0;j<num;j++) { if(wcscmp(pImageCodecInfo[j].MimeType,form)==0) { *pClsid = pImageCodecInfo[j].Clsid; free(pImageCodecInfo); return j; } } free(pImageCodecInfo); return -1;}

Step 5: Save the image:

Now that we have a function to get the encoder CLSID, we can save the image. If we want to save it as a file, we can do it like this:

CLSID encId; std::wstring extension = getExtension(filename); std::wstring mimetype = getMimeTypeFrom(extension);if (GetEncoderClsid(mimetype, &encId) > -1) { resizedBmp->Save(std::wstring(L"Output") + extension, &encId);}

Now, in my case this wasn’t actually the goal; I needed the byte data for that file. I guess I could have saved it and then opened it and read it back, but since I am using this in a FireBreath plugin for Facebook that can be problematic. You can get the bytes directly like this:

// If we were able to determine the filetype, save it to a stream IStream *buffer; CreateStreamOnHGlobal(NULL, true, &buffer); resizedBmp->Save(buffer, &encId); // Find the size of the resulting buffer STATSTG statstg; buffer->Stat(&statstg, STATFLAG_DEFAULT); ULONG bmpBufferSize = (ULONG)statstg.cbSize.LowPart; int length = bmpBufferSize; // Seek to the beginning of the stream LARGE_INTEGER li = {0}; buffer->Seek(li, STREAM_SEEK_SET, NULL); // Copy the resulting data from the stream into our result object char *data = new char[bmpBufferSize]; res.content_type = content_type; ULONG bytesRead; buffer->Read(data, bmpBufferSize, &bytesRead); buffer->Release();

Step 6: Deinitialize GDI+ properly

You need the token you created in step 1:

GdiplusShutdown(m_gdiplusToken);

That’s it!

Hope it helps someone!

http://colonelpanic.net/2010/11/resizing-an-image-with-gdi-with-cpp/