The core issue is passing the wrong pixel format to the Bitmap constructor. PixelFormatCanonical
is not one of the supported pixel formats. It's a bit mask used to determine whether a pixel format is canonical (see IsCanonicalPixelFormat). You'll have to use a real pixel format, like the default PixelFormat32bppARGB
.
The following code produces the desired output:
First up, a small helper class for GDI+ initialization. This ensures, that the d'tor (i.e. the call to GdiplusShutdown
) is executed after all other objects have been destroyed. With respect to order of destruction, it serves the same purpose as the additional scope in the OP. In addition, it also allows for exceptions to be thrown.
#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include <stdexcept>
using std::runtime_error;
struct GdiplusInit {
GdiplusInit() {
GdiplusStartupInput inp;
GdiplusStartupOutput outp;
if ( Ok != GdiplusStartup( &token_, &inp, &outp ) )
throw runtime_error( "GdiplusStartup" );
}
~GdiplusInit() {
GdiplusShutdown( token_ );
}
private:
ULONG_PTR token_;
};
This code was taken from the MSDN sample Retrieving the Class Identifier for an Encoder.
int GetEncoderClsid( const WCHAR* format, CLSID* pClsid )
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize( &num, &size );
if ( size == 0 )
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)( malloc( size ) );
if ( pImageCodecInfo == NULL )
return -1; // Failure
GetImageEncoders( num, size, pImageCodecInfo );
for ( UINT j = 0; j < num; ++j )
{
if ( wcscmp( pImageCodecInfo[j].MimeType, format ) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free( pImageCodecInfo );
return j; // Success
}
}
free( pImageCodecInfo );
return -1; // Failure
}
Finally, the GDI+ rendering code. It uses objects with automatic storage duration throughout, making it more compact and safer.
void drawImage( int width, int height ) {
GdiplusInit gdiplusinit;
//Create a bitmap
Bitmap myBitmap( width, height, PixelFormat32bppARGB );
Graphics g( &myBitmap );
Pen blackpen( Color( 255, 0, 0, 0 ), 3 );
//draw on bitmap
g.DrawLine( &blackpen, 1, 1, 200, 200 );
// Save bitmap (as a png)
CLSID pngClsid;
int result = GetEncoderClsid( L"image/png", &pngClsid );
if ( result == -1 )
throw runtime_error( "GetEncoderClsid" );
if ( Ok != myBitmap.Save( L"C:\test\test.png", &pngClsid, NULL ) )
throw runtime_error( "Bitmap::Save" );
}
int main()
{
drawImage( 200, 200 );
return 0;
}
Note: It looks like GetEncoderClsid
shouldn't be required, since those are well-known constants. However, trying to pass the appropriate WIC CLSID (CLSID_WICPngEncoder
) to Bitmap::Save
only produced a FileNotFound
error.