Skip navigation

#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;

#pragma comment( lib, "gdiplus.lib" )
#pragma warning( disable : 4018 )
#pragma warning( disable : 4996 )

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );

INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow )
{
  // attach a console
  AllocConsole();
  AttachConsole( GetCurrentProcessId() );
  freopen( "CON", "w", stdout );

  // Start up GDI+.
  GdiplusStartupInput gdiplusStartupInput;
  ULONG_PTR gdiplusToken;
  GdiplusStartup( &gdiplusToken, &gdiplusStartupInput, NULL );

  WNDCLASSEX wndClassex = { 0 };
  wndClassex.cbClsExtra     = 0;
  wndClassex.cbSize         = sizeof( WNDCLASSEX ) ;
  wndClassex.cbWndExtra     = 0;
  
  wndClassex.hbrBackground  = ( HBRUSH )GetStockObject( WHITE_BRUSH );
  wndClassex.hCursor        = LoadCursor( NULL, IDC_ARROW );
  wndClassex.hIcon          = LoadIcon( NULL, IDI_APPLICATION );
  wndClassex.hIconSm        = NULL;
  wndClassex.hInstance      = hInstance;
  wndClassex.lpfnWndProc    = WndProc;
  wndClassex.lpszClassName  = TEXT( "GDIPLUSWINDOW" );
  wndClassex.lpszMenuName   = NULL;
  wndClassex.style          = CS_HREDRAW | CS_VREDRAW;

  if( !RegisterClassEx( &wndClassex ) ) { puts( "problem registering wndclassex" ); system("pause"); return 1; }
  HWND hWnd = CreateWindowEx( 0,
    TEXT( "GDIPLUSWINDOW" ),   // window class name
    TEXT( "Welcome to GDI+!" ),  // window caption
    WS_OVERLAPPEDWINDOW,      // window style
    20,            // initial x position
    20,            // initial y position
    640,            // initial width
    480,            // initial height
    NULL,                     // parent window handle
    NULL,                     // window menu handle
    hInstance,                // program instance handle
    NULL );                    // creation parameters

  ShowWindow( hWnd, iCmdShow );
  UpdateWindow( hWnd );

  MSG msg;
  while( GetMessage( &msg, NULL, 0, 0 ) )
  {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }

  GdiplusShutdown( gdiplusToken );
  return msg.wParam;
}

void MessageBox( TCHAR* fmt, TCHAR* title, int options, va_list args )
{
  TCHAR buf[ 1024 ];
  int index = wvsprintf( buf, fmt, args );
  buf[index] = '\n';
  buf[index+1] = 0;
  wprintf( buf );
  MessageBox( HWND_DESKTOP, buf, title, options );
}

void info( TCHAR* fmt, ... )
{
  va_list list;
  va_start( list, fmt );
  MessageBox( fmt, TEXT( "Info" ), MB_OK|MB_ICONINFORMATION, list );
}

void error( TCHAR* fmt, ... )
{
  va_list list;
  va_start( list, fmt );
  MessageBox( fmt, TEXT( "Error" ), MB_OK|MB_ICONERROR, list );
}

TCHAR* GetStatusString( Gdiplus::Status status )
{
  TCHAR* statuses[] = {
    TEXT( "Ok: Indicates that the method call was successful." ),
    TEXT( "GenericError: Indicates that there was an error on the method call, which is identified as something other than those defined by the other elements of this enumeration." ),
    TEXT( "InvalidParameter: Indicates that one of the arguments passed to the method was not valid." ),
    TEXT( "OutOfMemory: Indicates that the operating system is out of memory and could not allocate memory to process the method call. For an explanation of how constructors use the OutOfMemory status, see the Remarks section at the end of this topic." ),
    TEXT( "ObjectBusy: Indicates that one of the arguments specified in the API call is already in use in another thread." ),
    TEXT( "InsufficientBuffer: Indicates that a buffer specified as an argument in the API call is not large enough to hold the data to be received." ),
    TEXT( "NotImplemented: Indicates that the method is not implemented." ),
    TEXT( "Win32Error: Indicates that the method generated a Win32 error." ),
    TEXT( "WrongState: Indicates that the object is in an invalid state to satisfy the API call. For example, calling Pen::GetColor from a pen that is not a single, solid color results in a WrongState status." ),
    TEXT( "Aborted: Indicates that the method was aborted." ),
    TEXT( "FileNotFound: Indicates that the specified image file or metafile cannot be found." ),
    TEXT( "ValueOverflow: Indicates that the method performed an arithmetic operation that produced a numeric overflow." ),
    TEXT( "AccessDenied: Indicates that a write operation is not allowed on the specified file." ),
    TEXT( "UnknownImageFormat: Indicates that the specified image file format is not known." ),
    TEXT( "FontFamilyNotFound: Indicates that the specified font family cannot be found. Either the font family name is incorrect or the font family is not installed." ),
    TEXT( "FontStyleNotFound: Indicates that the specified style is not available for the specified font family." ),
    TEXT( "NotTrueTypeFont: Indicates that the font retrieved from an HDC or LOGFONT is not a TrueType font and cannot be used with GDI+." ),
    TEXT( "UnsupportedGdiplusVersion: Indicates that the version of GDI+ that is installed on the system is incompatible with the version with which the application was compiled." ),
    TEXT( "GdiplusNotInitialized: Indicates that the GDI+API is not in an initialized state. To function, all GDI+ objects require that GDI+ be in an initialized state. Initialize GDI+ by calling GdiplusStartup." ),
    TEXT( "PropertyNotFound: Indicates that the specified property does not exist in the image." ),
    TEXT( "PropertyNotSupported: Indicates that the specified property is not supported by the format of the image and, therefore, cannot be set." ),
    TEXT( "ProfileNotFound: Indicates that the color profile required to save an image in CMYK format was not found." ),
    TEXT( "INVALID STATUS CODE" )
  };
  if( status < 0 || status > Gdiplus::Status::PropertyNotSupported + 1 ) // ProfileNotFound may not be there
    status = (Gdiplus::Status)( Gdiplus::Status::PropertyNotSupported + 2 ); // gives last error (INVALID STATUS CODE)

  return statuses[ status ];
}

struct ImageEncoders
{
private:
  UINT byteSize;  // byteSize of encoders on system
  UINT NumberOfEncoders; // number of encoders on system
  ImageCodecInfo* imageCodecs;

public:
  enum ImageFormat { BMP, JPG, GIF, TIF, PNG };
  ImageEncoders()
  {
    // How many encoders do we have on the system?
    Gdiplus::GetImageEncodersSize( &NumberOfEncoders, &byteSize );
    if( !byteSize || !NumberOfEncoders )
    {
      error( TEXT( "ERROR: There are no image encoders available, num=%d, size=%d" ),
        NumberOfEncoders, byteSize );
      return;
    }

    // Allocate space to get the ImageCodeInfo descriptor for each codec.
    imageCodecs = ( ImageCodecInfo* )malloc( byteSize );
    Gdiplus::GetImageEncoders( NumberOfEncoders, byteSize, imageCodecs );

    wprintf( TEXT( "CODECS:\n" ) );
    // Print the codecs we know
    for( int i = 0; i < NumberOfEncoders; i++ )
    {
      wprintf( TEXT( "  * Codec %d = Ext:%s Description:%s\n" ),
        i, imageCodecs[i].FilenameExtension, imageCodecs[i].FormatDescription );
    }
  }

  // File types lists look like *.jpg;*.jpeg;*.jfif
  bool InFileTypesList( const TCHAR* ext, const TCHAR* filetypesList )
  {
    TCHAR* dup = wcsdup( filetypesList ); // We have to form a writeable copy of the FileTypesList
    TCHAR* delimiters = TEXT( "*.;" );
    TCHAR* tok = wcstok( dup, delimiters ); // 1st call is on dup, subsequent on NULL.
    // So we want this call outside the while loop anyway.
    bool in = 0;
    do
    {
      if( ! wcsicmp( ext, tok ) )
        in = 1;
      else  // Pull the next token
        tok = wcstok( NULL, delimiters ); // wcstok retains state, so you pass NULL to use last tokenized string
    } while( tok && !in );
    free( dup ); // release the manipulatable duplicate.
    return in;
  }

  CLSID GetCLSIDForExtension( const TCHAR* ext )
  {
    CLSID clsid = CLSID_NULL; // Start with assuming invalid clsid.

    // Use a case-insensitive comparison
    for( int i = 0; i < NumberOfEncoders; i++ )
    {
      if( InFileTypesList( ext, imageCodecs[ i ].FilenameExtension ) )
      {
        wprintf( TEXT("%s is type %s\n"), ext, imageCodecs[i].FormatDescription );
        clsid = imageCodecs[ i ].Clsid; // Found a CLSID for this extension
        break;
      }
    }

    return clsid;
  }

  CLSID GetCLSIDByMime( const TCHAR* mimetype )
  {
    CLSID clsid = CLSID_NULL;
    for( int i = 0; i < NumberOfEncoders; i++ )
    {
      // Straight comparison with listed mime type.
      if( !wcsicmp( mimetype, imageCodecs[ i ].MimeType ) )
      {
        clsid = imageCodecs[ i ].Clsid;
        break;
      }
    }
    return clsid;
  }

  ~ImageEncoders()
  {
    free( imageCodecs );
  }

  static bool Save( Image* im, TCHAR* filename )
  {
    ImageEncoders encoders;

    // Extract the extension
    TCHAR* dotLocation = wcsrchr( filename, TEXT('.') );
    if( dotLocation ) // found.
    {
      CLSID clsid = encoders.GetCLSIDForExtension( dotLocation+1 );
      if( clsid != CLSID_NULL )
      {
        Gdiplus::Status status = im->Save( filename, &clsid );
        if( status != Gdiplus::Status::Ok )
        {
          error( TEXT( "ImageEncoders::Save( %s ): Failed to save: %s" ), filename, GetStatusString( status ) );
          return 0;
        }
        else
          wprintf( TEXT( "%s saved successfully\n" ), filename );
        return 1;
      }
    }

    error( TEXT( "ImageEncoders::Save( %s ): Failed to save; invalid extension" ), filename );
    return 0;
  }

};


LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
  HDC hdc;
  PAINTSTRUCT ps;

  switch( message )
  {
    case WM_PAINT:
    {
      // "Start painting":  The HDC "handle to a device context"
      // is the 'surface' on which you draw.
      // You don't draw directly to a window. Rather you draw to a "device context".
      // The reason its this way is it makes it so you can draw to ANY SURFACE.
      hdc = BeginPaint( hWnd, &ps );

      #pragma region painting code
      // Create a Graphics object for our window's hdc
      Graphics g( hdc );

      // Create a pen.
      Pen bluePen( Color( 128, 0, 0, 255 ) );
      bluePen.SetWidth( 8.0 );

      g.DrawLine( &bluePen, 0, 0, 100, 100 );
      g.DrawEllipse( &bluePen, 0, 0, 40, 40 );
      g.DrawEllipse( &bluePen, 0, 0, 50, 50 );

      SolidBrush blueBrush( Color( 120, 0, 0, 255 ) );
      g.FillRectangle( &blueBrush, 60, 60, 190, 180 );

      SolidBrush redBrush( Color( 123, 255, 0, 0 ) );
      Font arialFont( TEXT( "Arial" ), 12.0 );
      PointF pTextPos( 20, 20 );

      g.DrawString( TEXT( "This is GDI+!" ),
        strlen( "This is GDI+!" ),
        &arialFont, pTextPos, &redBrush );
      g.FillRectangle( &redBrush, 30, 30, 100, 180 );

      TCHAR* imageFilename = TEXT( "picture.jpg" );
      Image *im = new Image( imageFilename );
      if( im->GetLastStatus() != Gdiplus::Ok )
      {
        error( TEXT( "Image `%s` not provided, please copy one into the src folder" ), imageFilename );
        system( "start ." ); // opens current folder in windows explorer
      }
      else
      {
        g.DrawImage( im, 50.f, 50.f );

        // Test saving as PNG and JPG
        ImageEncoders::Save( im, TEXT( "COPY.png" ) );
        ImageEncoders::Save( im, TEXT( "COPY.jpg" ) );
      }
      #pragma endregion
      // "Wrap it up"
      EndPaint( hWnd, &ps );
    }
    return 0;

    case WM_KEYDOWN:
      switch( wParam )
      {
        case VK_ESCAPE:
          PostQuitMessage( 0 );
          break;
      }
      return 0;

    case WM_DESTROY:
      PostQuitMessage( 0 );
      return 0;

    default:
      return DefWindowProc( hWnd, message, wParam, lParam );
  }
} // WndProc
Advertisements

One Trackback/Pingback

  1. By Load .GIF in Win32 | Bobobobo's Weblog on 20 Apr 2016 at 6:03 pm

    […] An example is here: https://github.com/superwills/eternity/blob/master/eternity/GDIPlusTexture.cpp#L133, or here. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: