Working with Images

   

The Windows GDI provides native support for the bitmap file format and presents many GDI functions that are specifically designed for use with bitmap objects. However, if you are interested in displaying other image formats, you will need to use other mechanisms. Specifically , you'll have to provide a conversion routine so that you can construct a bitmap object from the information contained from the nonbitmap image (that is, a JPEG, GIF, or PNG file).

Before diving into nonbitmap image formats, we'll first concentrate on the bitmap object and discuss some of the techniques that can be used. After a few bitmap examples, we'll look at how other image file formats can be supported. Our intention here is not to get into the specifics of each image file format, but to provide a general overview of the most common file formats and how to use them.

The Windows Bitmap Object

The GDI presents two types of bitmaps: device-dependent bitmaps (DDBs) and device-independent bitmaps (DIBs). The former is a type of GDI graphic object, defined by the Win32 API BITMAP structure, which can be used in much the same way as most other graphic objects. Namely, you can select a DDB into a memory-device context and use any of the applicable GDI functions to perform rendering. Unfortunately, the GDI does not provide a direct means by which the bits of a DDB can be accessed. In contrast, a DIB is defined by the information contained in a BITMAPINFO structure and an array of pixels. In this way, you always have direct access to the bits of a DIB. There is a catch, however: DIBs cannot be selected into a memory device context, so rendering a DIB is generally slower than rendering a DDB.

To overcome the limitations imposed by DDBs and DIBs, Microsoft engineers developed the hybrid DIB section bitmap . This is simply a composite of the former two types, defined by the DIBSECTION structure.

 typedef struct tagDIBSECTION {      BITMAP              dsBm;      BITMAPINFOHEADER    dsBmih;      DWORD               dsBitfields[3];      HANDLE              dshSection;      DWORD               dsOffset;  } DIBSECTION; 

Unlike a true DDB, the bits of a DIB section bitmap are readily accessible. Unlike a true DIB, DIB section bitmaps can be selected into a memory-device context. These two complementary aspects are particularly important when both efficient pixel manipulation and efficient rendering are required. In fact, the VCL TBitmap class is based on the DIB section bitmap.

Understanding and Using TBitmap

The TBitmap class is the VCL's encapsulation of the Windows bitmap object. The class descends from the abstract TGraphic base class, and it dynamically adapts to use either a DDB or a DIB section bitmap. This functionality is presented by the internal VCL CopyBitmap() function. It is from within this function that either a DDB is created via the GDI functions CreateBitmap() (for monochrome bitmaps) and CreateCompatibleBitmap() , or else a DIB section bitmap is created via the CreateDIB Section() GDI function. While it is beyond the scope of this text to discuss the specifics of the CopyBitmap() VCL function, let's examine those properties and member functions of the TBitmap VCL class that provide support for other image types.

The TBitmap class relies heavily on the TBitmapCanvas and TBitmapImage classes. The former, a descendant of the TCanvas class, expands its parent class to encapsulate the GDI memory device context. The latter, a descendant of the TSharedImage class, handles the resource counting and destruction of the DDB or DIB section bitmap. This latter task is accomplished via the DeleteObject() GDI function.

For image rendering support, the TBitmapCanvas class inherits the Draw() , StretchDraw() , and CopyRect() member functions from its parent class. The TBitmapCanvas:: CreateHandle() function performs the task of selecting the bitmap (and palette, when appropriate) into the underlying memory device context. Thus, when the Draw() , StretchDraw() , or CopyRect() member function is used, the TCanvas class can rely solely on the StretchBlt() or TransparentStretchBlt() GDI function.

Aside from the rendering member functions presented by the TCanvas class, the TBitmap class provides the ScanLine property. This property uses the internal TBitmap:: GetScanLine() member function, which simply returns an offset pointer to the bits of the DIB section bitmap. When you convert from other formats, you'll need access to these bits so that you can directly manipulate the pixels of the image. In this way, the ScanLine property significantly eases the task of pixel manipulation.

Let's now look at some code examples that demonstrate some TBitmap techniques. Much of the image-processing code to be demonstrated in this section is built into the IPro.bpr sample image-processing application project, available in the ImageProcessing folder on the CD-ROM that accompanies this book.

Essential TBitmap Operations

Let's take a look at the example shown in Listing 15.10, which demonstrates how to load a bitmap image. We'll use a TImage component dropped on our form to perform the activities with its AutoSize property set to true . We set it to true so that the TImage object can automatically adjust to different image sizes that could be loaded.

Listing 15.10 Loading a Bitmap Image
 void __fastcall TForm1::ButtonLoadClick(TObject *Sender)  {      OpenDialog1->Filter = "Bmp files (*.bmp)*.BMP";      if (OpenDialog1->Execute())      {          Image1->Picture->Bitmap->LoadFromFile(OpenDialog1->FileName);      }  } 

In this example, we use a TOpenDialog object, called OpenDialog1 , to browse for and select a BMP file. Then, we use the LoadFromFile() method provided by the TBitmap class of the Image1 object to load the selected bitmap.

After we have the bitmap displayed within Image1 , we can begin to manipulate this image using its canvas. Let's take a look at a simple example (Listing 15.11) that paints a border around the image.

Listing 15.11 Placing a Border Around an Image
 void __fastcall TForm1::ButtonBorderClick(TObject *Sender)  {     // let's draw a border around the image     TRect MyRect(0,0,Image1->Width,Image1->Height);     Image1->Canvas->Pen->Color = TColor(EditColor->Text.ToIntDef(0)); //clBlue;     Image1->Canvas->Pen->Style = psSolid; // solid line     Image1->Canvas->Pen->Width = EditPenWidth->Text.ToIntDef(1);     Image1->Canvas->Brush->Style = bsClear;     Image1->Canvas->Rectangle(MyRect);  } 

Let's now take a look at Listing 15.12, which demonstrates how to save this bitmap, including the border around the original image.

Listing 15.12 Saving a Bitmap
 void __fastcall TForm1::ButtonSaveClick(TObject *Sender)  {      SaveDialog1->Filter = "Bmp files (*.bmp)*.BMP";      if (SaveDialog1->Execute())      {          Image1->Picture->Bitmap->SaveToFile(SaveDialog1->FileName);      }  }  

In this example, we allow the user to select a filename to save the image, and use the SaveToFile() method provided by the TBitmap class to physically save the file to disk. If a border, text, or anything else was painted on the image, that information will now be saved with the image.

Flicker-Free Bitmap Manipulation

To perform image-processing operations, it's important to know how to access the individual pixel values of the image. The easiest way to obtain individual pixel value is to use the Pixels property of TCanvas . The following code shows how the mouse position and pixel value can be accessed and displayed to the user when the mouse is moved across the image.

 void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift,                                          int X, int Y)  {      EditX->Text = X;      EditY->Text = Y;      Edit PixelValue->Text = Image1->Canvas->Pixels[X][Y];  } 

The current coordinate of the mouse is passed into the Image1MouseMove() event handler as X and Y . The pixel value is a TColor , and its interpretation depends on the PixelFormat . For grayscale images, the gray level is given by the lowest -order byte of the pixel value. For 24-bit color images, the lower three bytes represent RGB color intensities for blue, green, and red, respectively. The value $00FF0000 represents pure blue, $0000FF00 is pure green, and $000000FF is pure red.

You can also change a pixel value using TCanvas->Pixels[][] . For example, adding the following line to the Image1MouseMove() event handler will mark the movement of the mouse over the image with the color white:

 Image1->Canvas->Pixels[X][Y] = clWhite; 

Using Canvas->Pixels[X][Y] is straightforward, but extremely slow. Therefore, it should be used only for infrequent or causal access to pixel values. A more efficient method to track and display the movement of the mouse is as shown in Listing 15.13:

Listing 15.13 Tracking and Displaying a Moving Mouse
 void __fastcall TForm1::Image1MouseDown(TObject *Sender,        TMouseButton Button, TShiftState Shift, int X, int Y)  {      if (Button == mbLeft)      {          DoubleBuffered = true;          Image1->Canvas->MoveTo(X,Y);          MouseDown = true;      }  }  //                                      - void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift,        int X, int Y)  {          EditX->Text = X;          EditY->Text = Y;          EditPixelValue->Text = Image1->Picture->Bitmap->Canvas->Pixels[X][Y];          if (MouseDown)     // follow movement of the mouse          {             Image1->Canvas->Pen->Color = clWhite;             Image1->Canvas->LineTo(X,Y);          }  }  //                                      - void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button,        TShiftState Shift, int X, int Y)  {          MouseDown = false;          DoubleBuffered = false;  }  

In this example, we are using the MoveTo() method to initialize the pen position when the mouse is pressed, and the LineTo() method is used to reflect the mouse movement.

To reduce the flicker that's often associated with bitmap manipulation, the form's DoubleBuffered property is set to true when the mouse-dragging begins. When the mouse is released, the DoubleBuffered property returns to false. When it's false the control's image is rendered directly to the window, when it's true the control's image is first painted to an in-memory bitmap. Keep in mind that although DoubleBuffered reduces the amount of flicker it is still more memory intensive . That is why we set it back to false on the release of the mouse button.

Rotating a Bitmap

The ability to rotate an image is one of the common requests when working with Bitmap images. Digital cameras , for example, are often used in a vertical position to take a portrait picture. When this portrait picture is pulled off the camera, it will be brought into an application, such as a paint program, sideways . The user often needs the capability to rotate the image 90 degrees. Let's take a look at how that can be done in Listing 15.14.

Listing 15.14 Rotating an Image
 void __fastcall TForm1::ButtonRotate90Click(TObject *Sender)  {      //create and setup a temporary source bitmap      Graphics::TBitmap *source   = new Graphics::TBitmap;      source->Assign(Image1->Picture->Bitmap);      source->PixelFormat  =  Image1->Picture->Bitmap->PixelFormat;      // create and setup a temporary destination bitmap      Graphics::TBitmap *dest     = new Graphics::TBitmap;      dest->Width                 = source->Height;      dest->Height                = source->Width;      dest->PixelFormat           = source->PixelFormat;      if (RadioButtonPixelsArray->Checked) //Rotate one pixel at a time      {          for (int x=0;x<source->Width;x++)            for(int y=0;y<source->Height;y++)              dest->Canvas->Pixels[y][source->Width-1-x] =                      source->Canvas->Pixels[x][y];      }      else  // uses the faster scanline method      {        RGBTRIPLE* pixels;        TColor color;        for(int y=0;y<source->Height;y++)        {          pixels = (RGBTRIPLE*)source->ScanLine[y];          for (int x=0;x<source->Width;x++)             dest->Canvas->Pixels[y][source->Width-1-x] =                TColor(RGB(pixels[x].rgbtRed,                        pixels[x].rgbtGreen,                        pixels[x].rgbtBlue));        }      }      //now assign destination bitmap back to Image1 & cleanup      Image1->Picture->Bitmap = dest;      delete dest;      delete source;  }  

In this example we use two methods to rotate a color image 90 degrees. The first method, based on the value of RadioButtonPixelsArray , walks through all the pixels in the source image row by row and transfers those pixels to the destination image column by column. It's fairly simple, but it is not the most effective way to accomplish the rotation. A better way is provided through the ScanLine() function, which is used for the second method. With ScanLine() we can grab a whole row at one time and transfer the color pixels to each column of the destination image.

WARNING

The scanline example, as provided in Listing 15.14, works only for color images. We used the RGBTRIPLE data type for each pixel anticipating it would contain a red, green, and blue value. The Pixel format for a black and white image is much different. If you try to rotate a black and white image without adding the code to black and white, an access violation will occur.


One of the things that needs to be done before either rotation method is applied, is to properly assign the Height , Width , and PixelFormat of both the destination and source bitmap. When the rotation processing is complete, we need to assign the Canvas with the new rotated image, and then delete the temporary bitmaps that we used.

Cropping a Bitmap

Suppose your application is capable of bringing in an image, but the user is interested in displaying a smaller region of the image in view. In this situation, we need code to be able to crop the image to a selected region. Let's take a look at the example in Listing 15.15.

Listing 15.15 Crop to Selection
 void __fastcall TForm1::ButtonCropToSelectionClick(TObject *Sender)  {      if (lX == -1) return; // no zoom window to work with      // create and setup a temporary destination bitmap      Graphics::TBitmap *dest     = new Graphics::TBitmap;      dest->Width                 = abs(lX - oX);      dest->Height                = abs(lY - oY);      TRect  OldOne = Rect(oX,oY,lX,lY);      TRect  NewOne = Rect(0,0,dest->Width, dest->Height);      FreeZoomWindow();  // frees crop region      //create and setup a temporary source bitmap      Graphics::TBitmap *source   = new Graphics::TBitmap;      source->Assign(Image1->Picture->Bitmap);      source->PixelFormat  =  Image1->Picture->Bitmap->PixelFormat;      dest->PixelFormat           = source->PixelFormat;      dest->Canvas->CopyRect(NewOne,source->Canvas,OldOne);      //now assign destination bitmap back to Image1 & cleanup      Image1->Picture->Bitmap->FreeImage();      Image1->Picture->Bitmap->Assign(dest);      delete dest;      delete source;  }  

In this example, we take the coordinates from our crop region and use the CopyRect() function to transfer the area of interest into a new bitmap. The new bitmap is then assigned to the Canvas of the TImage object.

NOTE

All the examples provided on working with bitmaps are applicable to support similar effects for other images such as JPEG, GIF, and PNG. The key thing to remember is that within the confines of the VCL and Windows GDI, nonbitmap images should be internally transformed to a bitmap image during the execution of a program. When managed as a bitmap image within memory, the techniques described for manipulating a bitmap can be applied.


JPEG Images

A majority of images used by consumers are not Bitmap images, they are mostly JPEG images (pronounced "jaypeg"). JPEG is an image compression protocol developed by the Joint Photographic Experts Group. Digital cameras, picture CDs, Web images, scanners all often produce or provide JPEG images. So, more than likely, if you're interested in supporting image viewing or manipulation in your applications, you want to be able to support JPEG images.

Unlike bitmap images, JPEG images are compressed in a lossy fashion, meaning that some information is discarded during compression. Although the decompressed image is not identical to the original, for most natural images there is little or no degradation in visual quality. Moreover, the degree of compression can be adjusted, allowing for decompressed images of varying quality.

NOTE

The JPEG compression process consists of three stages: reduction of pixel redundancy via a Discrete Cosine Transform (DCT), quantization of transform data (DCT coefficients), and reduction of data redundancy. It is in the second stage (quantization) that information is discarded. Typically, this is where human visual system (HVS) characteristics are taken into account, although there is no strict specification for the type of quantization that should be performed. For more information on the JPEG image format specifics, refer to http://www.jpeg.org/.


The VCL provides support for JPEG images through the TJPEGImage class. The main compression and decompression routines are handled via the JPEG image compression library of the Independent JPEG Group (IJG). Like the TBitmap class, TJPEGImage is a descendant of the TGraphic class.

The TJPEGImage class implements the TPersistent:: Assign() and AssignTo() member functions so that you can easily convert between instances of TJPEGImage and TBitmap . Moreover, because the TJPEGImage class is designed for use with the Windows GDI and the rest of the VCL, the class maintains an internal bitmap representation of the underlying image such that rendering via the GDI is possible. The JPEG data itself is maintained via the TJPEGData class.

Loading a JPEG

Let's take a look at how to load a JPEG image onto a TImage control, as demonstrated in Listing 15.16.

Listing 15.16 Loading a JPEG Image onto a TImage Control
 void __fastcall TForm1::ButtonLoadClick(TObject *Sender)  {      //This code requires "jpeg.hpp" to be included in the source file      OpenDialog1->Filter =         "Bmp files (*.bmp)*.BMP JPEG images (*.jpg)  *.jpg; " ;      if (OpenDialog1->Execute())      {          if (!FileExists(OpenDialog1->FileName))              return; // make sure it exists, else get out.          AnsiString temp2 = ExtractFileName(OpenDialog1->FileName);          AnsiString temp = ExtractFileExt(OpenDialog1->FileName);          AnsiString Ext = temp.LowerCase();          if (Ext.AnsiPos("jpg") > 0)  // it's a jpg          {   // Decompress the jpeg image into a bitmap.              TJPEGImage *myjpeg = new TJPEGImage();              myjpeg->LoadFromFile(OpenDialog1->FileName);              myjpeg->DIBNeeded();  // used when jpeg image needs bitmap rep              Image1->Picture->Bitmap->Assign(myjpeg);              delete myjpeg;          }          else if (Ext.AnsiPos("bmp") > 0)          {              Image1->Picture->Bitmap->LoadFromFile(OpenDialog1->FileName);          }          EditFile->Text        = OpenDialog1->FileName;          EditWidth->Text       = Image1->Width;          EditHeight->Text      = Image1->Height;          EditPixelFormat->Text = Image1->Picture->Bitmap->PixelFormat;      }  }  

In this example we examine the file extension after a file has been selected from the Open dialog. If it is a JPEG image, a TJPEGImage object is created, which loads the file using the LoadFromFile() method. We use DIBNeeded() to reassign the JPEG image internally into a bitmap representation. We can actually leave this call off, however, as soon as we assign a JPEG image to a bitmap. This processing will be performed automatically by the Assign() routine if a DIB had not yet been created. After we assign the JPEG image to our TImage object, the JPEG image is deleted to free up memory.

Saving an Image as a JPEG

We can also save bitmap images as JPEG images. An example is provided in Listing 15.17.

Listing 15.17 Saving an Image as a JPEG
 void __fastcall TForm1::ButtonSaveClick(TObject *Sender)  {      //This code requires "jpeg.hpp" to be included in the source file      SaveDialog1->Title = "Save Image";      SaveDialog1->DefaultExt = "jpg";      SaveDialog1->Filter =         "JPEG images (*.jpg)  *.jpg;  Bmp files (*.bmp)*.BMP" ;      SaveDialog1->FilterIndex = 1;      if (SaveDialog1->Execute())      {          AnsiString temp2 = ExtractFileName(SaveDialog1->FileName);          AnsiString temp = ExtractFileExt(SaveDialog1->FileName);          AnsiString Ext = temp.LowerCase();          if (Ext.AnsiPos("jpg") > 0)  // it's a jpg          {   // Decompress the jpeg image into a bitmap.              TJPEGImage *jp = new TJPEGImage();              try              {                jp->Assign(Image1->Picture->Bitmap);                jp->SaveToFile(SaveDialog1->FileName);              }              __finally              {                delete jp;              }          }          else  if (Ext.AnsiPos("bmp") > 0)          {             Image1->Picture->Bitmap->SaveToFile(SaveDialog1->FileName);          }      }  }  

In this example, if the user chooses to save the image as a JPEG, we simply Assign() the bitmap contained within the TImage object to TJPEGImage object. The SaveToFile() method for the TJPEGImage object is then called to save the image to disk. Finally, we delete the TJPEGImage object to free up memory.

JPEG Performance Properties

The TJPEGImage class provides several properties designed to manage the quality and performance of JPEG data. These properties include CompresionQuality , Performance , Scale , ProgressiveDisplay , ProgressiveEncoding , and Smoothing .

The CompressionQuality property can be used to adjust the amount of degradation incurred during compression for an image being saved. Values range from 1 to 100. A lower value will result in a smaller file size, but poorer picture quality. Conversely, a higher value will result in better image quality, but a larger file size .

The Performance property is used for decompressing the JPEG data on load, which affects the display of the internal bitmap image. There are two choices: jpBestQuality and jpBestSpeed . Setting Performance to jpBestSpeed can lead to some dithering in the internal bitmap image, but it will be faster. The Scale property determines the resolution (size) of the image to be displayed during decompression. Choices include full-size down to an eighth -size image.

The ProgressiveDisplay and ProgressiveEncoding properties allow for support of progressive decompression, where the currently decompressed portion of the image can be viewed before the entire image is decompressed. These properties, along with the Smoothing property, are ideal for situations in which a progressive transmission scheme is employed.

JPEG I/O Operations

Support for file, stream, and Clipboard operations is provided via the LoadFromClipboardFormat() , LoadFromStream() , LoadFromFile() , SaveToClipboardFormat() , SaveToStream() , and SaveToFile() member functions. These are inherited from the TGraphic class, and their use is entirely straightforward. Note that for Clipboard operations, the TJPEGImage class uses internal bitmap representation.

GIF Images

Another popular image format is the Graphics Interchange Format (GIF, pronounced "jiff"), which was created in 1987 by CompuServe Corporation. Unlike JPEG images, GIF images are compressed in a lossless fashion, meaning no information is lost during compression. That is, the decompressed image is identical to the original. GIF images also support progressive display (interlacing), multiple images (animated GIF), and transparency as of the latest format revision (GIF89a). More information on GIF can be found at http://www.geocities.co.jp/SiliconValley/3453/gif_info/.

Many developers are reluctant to support the GIF format, the use of which has been shrouded by licensing issues. In fact, it is not the format itself that is in question; rather it is the specification for the use of the LZW (Lempel-Ziv-Welch) compression algorithm. Unisys Corporation holds the patent for this modification of the Lempel-Ziv 78 (LZ78) compression algorithm. CompuServe has publicly granted a royalty-free license to use the GIF format, but Unisys requires that developers purchase a license.

The VCL does not provide native support for the GIF format. To display GIF images, you'll need to convert the (decompressed) data to the bitmap format. Although this can be done manually through the ScanLine property, there is still the issue of decompressing the data and reading or writing the data to or from a file. There are several third-party libraries available to handle this task. Of particular interest is the TGIFImage VCL component effort from Project JEDI, which can be found at http://www.delphi-jedi.org/ and also http://www.torry.net/gif.htm.

PNG Images

An image format that seems to be growing in popularity is the Portable Network Graphics (PNG, pronounced "ping") format. PNG was designed to expand upon and relieve the patent hassle of the GIF format. Like its predecessor, a PNG image is compressed in a lossless manner. Unlike the GIF format, PNG does not rely on the LZW algorithm. Instead, a variation of the Lempel-Ziv 77 (LZ77) compression algorithm is employed. This is the same compression algorithm used by the major file compression applications such as WinZIP.

Like the GIF format, the PNG format allows for transparent pixels. However, through the use of an Alpha channel, PNG images may also contain pixels of variable transparency (alpha blending). Moreover, in contrast to the GIF format, PNG images are not limited to 256 colors. To compensate for display monitor variations, the PNG specification allows for encoded gamma information. There is also support for progressive display, accomplished via a two-dimensional interlacing scheme. Unfortunately, the PNG format does not allow for multiple images (animation).

To provide support for display of PNG images, you'll need a means by which to convert the PNG format to a DIB. As always, you can perform this conversion manually; in that case, the latest PNG format specifications are needed. You can find them at http://www.libpng.org/pub/png/spec/PNG-Contents.html.

As with many image formats, there are several third-party libraries that can perform this conversion for you. For example, the freePNGDIB conversion library by Jason Summers, found at http://home.mieweb.com/jason/imaging/pngdib/, provides the read_png_to_dib() function that can read a PNG image file and yield a DIB ( BITMAPINFO , color table, and bits). It also presents the write_dib_to_png() function for writing a PNG file from a DIB.

Using this library, to initialize a TBitmap object with information contained in a PNG file, you first call the read_png_to_dib() function to create a DIB from the PNG, and then use the SetDIBits() GDI function (or the ScanLine property) to fill the TBitmap object. This is demonstrated in Listing 15.18.

Listing 15.18 Converting a PNG Format Image to a TBitmap
 if (OpenDialog1->Execute())  {     TCHAR filename[MAX_PATH];     lstrcpyn(filename, OpenDialog1->FileName.c_str(), MAX_PATH);     // declare and clear the PNGD_P2DINFO structure     PNGD_P2DINFO png2dib;     memset(&png2dib, 0, sizeof(PNGD_P2DINFO));     // initialize the structure size and filename     png2dib.structsize = sizeof(PNGD_P2DINFO);     png2dib.pngfn = filename;     // convert from PNG to DIB     if (read_png_to_dib(&png2dib) == PNGD_E_SUCCESS)     {        Graphics::TBitmap* Bitmap = Image1->Picture->Bitmap;        Bitmap->Width = png2dib.lpdib->biWidth;        Bitmap->Height = png2dib.lpdib->biHeight;        HBITMAP hBmp = Bitmap->ReleaseHandle();        HDC hDC = Canvas->Handle;        try        {           //           // TODO: add palette support...           //                  // convert from DIB to TBitmap           SetDIBits(hDC, hBmp, 0,              png2dib.lpdib->biHeight, png2dib.bits,              reinterpret_cast<LPBITMAPINFO>(png2dib.lpdib),              DIB_RGB_COLORS);        }        catch (...)        {           Bitmap->Handle = hBmp;           GlobalFree(png2dib.lpdib);        }        Bitmap->Handle = hBmp;        GlobalFree(png2dib.lpdib);     }  }  

Similarly, to create a PNG file from information contained in a TBitmap object, you first construct a DIB from the TBitmap via the GetDIBSizes() and GetDIB() VCL utility functions (from graphics.pas ), and then use the write_dib_to_png() function to write the PNG file. This is demonstrated in Listing 15.19.

Listing 15.19 Converting a TBitmap Image to PNG Format
 if (SaveDialog1->Execute())  {     TCHAR filename[MAX_PATH];     lstrcpyn(filename, SaveDialog1->FileName.c_str(), MAX_PATH);     BITMAPINFO bmi;     Graphics::TBitmap* Bitmap = Image1->Picture->Bitmap;     //     // determine the size of the DIB info     // (BITMAPINFOHEADER + color table) and the     // size of the bits (pixels)     //     unsigned int info_size = 0, bits_size = 0;     GetDIBSizes(Bitmap->Handle, info_size, bits_size);     // allocate memory for the bits     unsigned char *bits = new unsigned char[bits_size];     try     {        // get the BITMAPINFOHEADER + color table and the bits        if (GetDIB(Bitmap->Handle, Bitmap->Palette, &bmi, bits))        {           // declare and clear the PNGD_D2PINFO structure           PNGD_D2PINFO dib2png;           memset(&dib2png, 0, sizeof(PNGD_D2PINFO));           // initialize the structure           dib2png.structsize = sizeof(PNGD_D2PINFO);           dib2png.flags = PNGD_INTERLACED;           dib2png.pngfn = filename;           dib2png.lpdib = &bmi.bmiHeader;           dib2png.lpbits = bits;           // convert the DIB to PNG, then save to file           if (write_dib_to_png(&dib2png) != PNGD_E_SUCCESS)           {              throw EInvalidGraphic("Error Saving PNG!");           }        }     }     catch (...)     {        delete [] bits;     }     delete [] bits;  }  

Included on the companion CD-ROM is a project ( PROJ_PNGDIB_DEMO.CPP in the PNGDEMO folder) that demonstrates the use of the PNGDIB library.


     
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net