Image Processing using GDI+ and VC++ 6.0

Environment: GDI+ and VC6

I was recently working on a small project to convert an image to the WAP's WBMP format. WAP is the Wireless Application Protocol, for people who didn't know and WBMP stands for Wireless Bitmap.

I was having a hard time reading various formats (GIFs, JPGs, and BMPs) until I came across some excellent articles on using GDI+ at the CodeGuru site and also on the CodeProject site. These articles developed my interest further into image processing and I got hooked on it. What started out as a simple picture-to-WBMP convertor is fast growing into a complete image processing framework. The articles of Christian Grauss on using GDI+ and .NET for image processing are excellent resources. I wanted to code all stuff in VC++ and not .NET—simply because I am used to VC++.

For any image processing task, one needs to access raw pixel data. If you look at the GDI+ API for doing this, it's quite complex; besides, I also wanted to code my image processing routines so that I can port them to other environments. In other words, I did not want any GDI+ specific code in the core image processing routines. This compelling requirement forced me to make a choice on which pixel data format to use. I decided to use only the ARGB (Alpha, red, green, blue) mode of the image data that can easily be encoded in an 'int'. I wrote helper functions to create an array of ARGB encoded ints given a GDI+ Bitmap and also to create a GDI+ Bitmap from my integer array. Here are the functions:

// Given a Bitmap - get an integer encoded data of the pixels
int * Bitmap2PixelData(Bitmap *b) {

  // get width and height of Bitmap
  int width = b->GetWidth();
  int height = b->GetHeight();

  // make sure these are valid
  if ( width *height <= 0 )
    return NULL;

  // allocate memory for pixel data
  int *pixelData_ARGB = new int[width*height];

  if ( pixelData_ARGB == NULL )
    return NULL;

  // get the pixel values from the bitmap and encode
  // into the array allocated above.

  BitmapData bmData;
  Rect rect(0, 0, width, height);

  b->LockBits(rect,
    ImageLockModeRead,
    PixelFormat32bppARGB,
    &bmData);

  int stride = bmData.Stride;
  BYTE *p = (BYTE *)((void *)bmData.Scan0);

  int nOffset = stride - width*4;    // bytes to skip at end of
                                     // each row

  for(int y=0; y < height;y++) {
    for(int x=0; x < width; ++x ) {
      // GDI lies about RGB - internally it's BGR
      pixelData_ARGB[y*width+x] =  ARGB2Pixel(p[3], p[2], p[1],
                                              p[0]);
      p += 4;
    }
    p += nOffset;
  }
  b->UnlockBits(&bmData);

  // return the pixel data array
  return (pixelData_ARGB);
}

The preceding code uses ARGB2Pixel. This is an inline function that computes the pixel value—given Alpha, Red, Green and Blue values. It also clamps the ARGB component such that they all lie between 0 and 255, inclusive. Here is the code for that:

inline int ARGB2Pixel(int alpha, int r, int g, int b) {
  if ( alpha > 255 )
    alpha = 255
  else if ( alpha < 0 )
    alpha = 0;

  if ( r > 255 )
    r = 255
  else if ( r < 0 )
    r = 0;

  if ( g > 255 )
    g = 255
  else if ( g < 0 )
    g = 0;

  if ( b > 255 )
    b = 255
  else if ( b < 0 )
    b = 0;

  // shift r and g to encode
  r = (r << 16); 
  g = (g << 8 ); 
  return ( ((alpha << 24 )&0xff000000)  | r | g| b );
}

Given below is the function that takes an integer array and creates a GDI+ Bitmap that can then be displayed.

Bitmap * PixelData2BitMap(int width, int height,
                          int *pixData_ARGB) {

  // create a temporary Bitmap
  Bitmap bit(width, height, PixelFormat32bppARGB);

  // create its clone for returning
  Bitmap *b = bit.Clone(0, 0, bit.GetWidth(), bit.GetHeight(),
              PixelFormat32bppARGB);

  BitmapData bmData;
  Rect rect(0, 0, b->GetWidth(), b->GetHeight());
  b->LockBits(rect,
    ImageLockModeRead | ImageLockModeWrite,
    PixelFormat32bppARGB,
    &bmData);

  int stride = bmData.Stride;
  BYTE *p = (BYTE *)((void *)bmData.Scan0);
  int nOffset = stride - width*4;   // bytes to skip at end of
                                    // each row

  int pixel;

  for(int y=0; y < height;y++) {
    for(int x=0; x < width; ++x ) {
      // GDI lies about RGB - internally it's BGR
      pixel = pixData_ARGB[y*width+x];
      p[3] = (BYTE) ((pixel >> 24) & 0xff);    // alpha
      p[2] = (BYTE) ((pixel >> 16) & 0xff);    // pixel red
      p[1] = (BYTE) ((pixel >> 8 ) & 0xff);    // pixel green
      p[0] = (BYTE) ((pixel      ) & 0xff);    // pixel blue
      p += 4;
    }
    p += nOffset;
  }
  b->>UnlockBits(&bmData);

  return (b);
}

The sample project also has some neat undo-redo stuff. It does not save any images yet. Nevertheless, it's quite easy to add support for various image formats (use GDI+ to save as well). There are several articles on CodeGuru to do that.

Downloads

Download source - 20 Kb


Comments

  • Image Processing

    Posted by sagarshinde111 on 04/21/2010 01:06am

    it's a very good code. From that code I get an Idea about image processing. Please tell about how to take any image from the system at runtime and how to show it's x and y coordinates when we move the mouse over the image.

    Reply
  • The Memory could not be ''read''

    Posted by Dreamzdoll on 12/04/2008 01:18am

    Hi! Even i have a similar problem. In a MFC, MDI, MultiThreaded Application with Serial Communication in it (Rs-232), i have to update the toolbar buttons every 180 millisecs and save the recieved data into a ''DBF'' every time. How do i handle this problem. Please help!

    Reply
  • Gdiplus need to be initialized

    Posted by walkwith on 04/16/2005 10:49pm

    To compile and run above code sample, you have to initialize the GDI+ library first.
    
    // By Kim Joo-woong
    
    // must be class or global variable, because of being used in initializing and deinitializing
    ULONG_PTR			gdiplusToken;
    
    // initializing code - add below code to startup GDI+ library
    GdiplusStartupInput gdiplusStartupInput;	
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
    // deinitializing code - add below code to free GDI+ library
    GdiplusShutdown( gdiplusToken );

    Reply
  • please help me

    Posted by naveenkumarsk on 08/11/2004 08:57am

    hi naveen here,i need help on to read a file which contains hexadecimal numbers i.e 96x6 format,after reading i want to store in a buffer, again from buffer i want to display the pixels on screen

    Reply
  • compiling error

    Posted by Legacy on 05/21/2003 12:00am

    Originally posted by: Hello

    error C2664: 'LockBits' : cannot convert parameter 1 from 'class Gdiplus::Rect' to 'const class Gdiplus::Rect *'

    No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

    Am I wrong to set some configuration?

    Reply
  • colors used in a bitmap with 24 bit color depth

    Posted by Legacy on 05/04/2003 12:00am

    Originally posted by: MAJED

    I have a bitmap with few number of colors but it is stored in format of 24 bit.
    the problem is:
    to calculate the number of this colors and what is it without using GetPixel() API function for performance needs.

    if there is an effective way so please advice me.
    Best Regards

    Eng.Majed

    Reply
  • Memory Reference Errors occured: Using above code for real-time applications

    Posted by Legacy on 04/24/2003 12:00am

    Originally posted by: Atif Bashir

    AOA

    Memory Reference error is occured and the application terminates when I want to use the image-processing code provided by you i.e.

    When I use to process the images/frames at some rate e.g. 20 fps, the program executes for a minute approximately and suudenly gives Memory Reference Error and also demands to
    Increase the Virtual Memory Paging File size to run the window properly.

    Can you advise me about the matter.

    I'll be very grateful for your early response.

    Kind Regards
    ------------------------ Atif

    Reply
  • Help Need in doing ImageComparission

    Posted by Legacy on 04/11/2003 12:00am

    Originally posted by: Raju Selvaraj

    Dear Friend,
    Iam Raju Selvaraj.Working in BGI Tech Chennai,
    Friend i've been assigned a project in Image Processing topic "Image Comparission".objective is to compare to bitmaps and to high light difference in the images.iam familier with VC++,but no knowledge about Image PRocessing.
    I'll be thakfull if u help me in doing project.

    I went thru u r code,it seems very useful but the thing is i couldnt understatnd funcnalities.It'll be better if u send me ur code with some documentation.

    Reply to me if possible to "raju_obuli@yahoo.com".

    Expecting u r valule reply.
    Advance Thanks,
    Raju selvaraj.

    Reply
  • Image Processing using GDI+ and VC++ 6.0

    Posted by Legacy on 04/11/2003 12:00am

    Originally posted by: Raju Selvaraj

    Hi Deepa,
    When i tried to run ur program i got a error message "Cannot open include file: 'gdiplus.h': No such file or directory", what might be the problem.And i am new to Image processing ,recently i've assingned project of "Image Comparission" in VC++.Can u help me in developing that.
    I'll be thakfull if u help me.Expecting u r valuble reply.
    advance Thanks,
    Regards,
    Raju Selvaraj

    Reply
  • transparented GIF

    Posted by Legacy on 03/14/2003 12:00am

    Originally posted by: Mihai

    How can I save an Bitmap object in .gif file - BUT with transparency background ? I use:

    GetEncoderClsid(_T("image/gif"), &encoderClsid);
    Bitmap bmp(hBmp, hpal);
    stat = bmp.Save(strFileName, &encoderClsid, NULL);
    //I looked for some &encoderParameters);

    Thank you in advance,
    Mihai

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Due to internal controls and regulations, the amount of long term archival data is increasing every year. Since magnetic tape does not need to be periodically operated or connected to a power source, there will be no data loss because of performance degradation due to the drive actuator. Read this white paper to learn about a series of tests that determined magnetic tape is a reliable long-term storage solution for up to 30 years.

Most Popular Programming Stories

More for Developers

RSS Feeds