Image Processing using GDI+ and VC++ 6.0

Friday Feb 7th 2003 by Deepak Gupta
Share:

GDI+ can be used for image processing. Learn a simpler way to use the methods that directly address pixels to allows image processing capabilities.

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
Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved