Typically, an image class loads the image from a file, renders the image on the display with the StretchDIBits() API, performs any number of image processing manipulations on the image, and then saves the modified image to a new file. The sections that follow explain how to perform each of these actions with the SECImage family of classes.
SECImage is serializable, so if you include an SECImage instance in your document, it is serialized with the rest of your data.
The standard method for reading images is LoadImage(). LoadImage() is a virtual function in SECImage that is implemented by each format-specific derived image class. The SECImage derivative classes read image data from a format-specific image file and translate it to the intermediate format used by the parent SECImage class. When an image is loaded, the m_pPalette member of SECImage is created, which creates a palette for the loaded image based on the loaded color map.
The following code loads a .PCX file.
// . . . SECPcx pcx; if (pcx.LoadImage("check.pcx") == FALSE) ASSERT(1); //LoadImage FAILED! //Now we have an image loaded, and it can be displayed |
Two Objective Toolkit classes use LZW compression: SECGIF and SECTiff. These classes contain stub routines where the LZW compression algorithms belong. As a result, if you attempt to load or save images of GIF or TIFF format, an error occurs at run time.
GIF is a popular format for graphics to be viewed in Web browsers, while TIFF (tag-based image file format) is a digital data format compatible with a variety of scanners, faxes, and other image-processing applications.
We offer a GIF/TIFF Unlock Pack (lzwcode.zip) that allows you to replace the stubbed classes with the source code of the algorithm.
Once an image has been successfully created or loaded from an image file, you can display it on any device context (DC) by treating the data contained by SECImage as a device independent bitmap (DIB). A DIB is typically rendered to a DC via the StretchDIBits() API. Objective Toolkit encapsulates the StretchDIBits() call through its own StretchDIBits() method.
The advantage of using the Objective Toolkit encapsulated StretchDIBits() is that it increases the resolution of the image you're manipulating to a resolution greater than the one available on your DC. In this case, Objective Toolkit automatically displays the image correctly. For example, if you attempted to display a 24-bits-per-pixel image on an 8-bits-per-pixel display in the WIN32 environment, Objective Toolkit would make a call to the CreateHalftone() palette API, which creates a palette with the closest matching color values to the image in memory. The approximated palette contains the standard colors for the image. In the 16-bit environment, Objective Toolkit uses its own internal quantize routine to perform an operation similar to CreateHalftone().
The following code displays an image.
void CImageView::OnDraw(CDC* pDC) { SECImage * pImage = pDoc->GetImage(); CPalette *pOldPalette; // If a palette has been created for the image, select it. if (pImage->m_pPalette) pOldPalette = pDC->SelectPalette(pImage->m_pPalette, TRUE); // Call encapsulated StretchDIBits API pImage->StretchDIBits(pDC, 0,0, pDoc->GetDocSize().cx, pDoc->GetDocSize().cy, 0,0, pImage->m_dwWidth, pImage->m_dwHeight, pImage->m_lpSrcBits, pImage->m_lpBMI, DIB_RGB_COLORS, SRCCOPY ); // Restore the palette if (pImage->m_pPalette) pDC->SelectPalette(pOldPalette, TRUE); } |
The SECImage family of classes allows you to convert from one image format to another easily. For example, if you wanted to convert an GIF image to an JPEG image, you would add a few lines of code using the Objective Toolkit image classes.
Image format conversion is performed using the ConvertImage() method. Given a source image and a destination image, ConvertImage() can use image instances interchangeably. For example, a .PCX image can be loaded into memory through an instance of the SECPcx class and then converted with an instance of any other derived image class. Unlike CopyImage(), ConvertImage() does not duplicate image data. Once an image is converted, you can safely delete the source image class instance.
The following code demonstrates how to convert an SECPcx image into an SECGif image:
void TestConversion(SECPcx *pSrc) { // Create destination instance SECGif *pDest = new SECGif(); pDest->ConvertImage(pSrc); // Now pDest is a valid image, while pSrc // contains no data... pDest->SaveImage("convert.gif"); // Convert back to validate the source image again. pSrc->ConvertImage(pDest); // delete the destination, will not affect // pSrc data members at all delete(pDest); } |
SECImage supplies a CopyImage() routine that allows you to create a duplicate of a source image. You can use this routine to make a copy of an image before allowing your user to change it. Then, you can allow the user to revert to the original image.
CopyImage() behaves the same way the ConvertImage() method does, except it does not alter the source image so the user can use it after the copy is performed.
The following code copies a TIFF image to a JPEG image:
void TestCopy(SECTiff *pSrc) { // create the destination image SECJpeg *pJpeg = new SECJpeg(); if (!pJpeg) return; if (!pJpeg->CopyImage(pSrc)) { delete pJpeg; return; } // Now you have two independent copies of the // same image in 2 different formats // destruction of object will destroy the // copied image data, while pSrc is left intact. delete pJpeg; } |
Images often require a large amount of memory. Creating a copy does not perform any compression or savings in memory. In effect, it requires twice the amount of memory.
Once you create an image and load it into memory, you can manipulate the image through a number of SECImage methods.
For example, you can flip and rotate the image. You can flip an image vertically or horizontally by calling the FlipVert() or FlipHorz() methods. You can rotate images 90 degrees counter-clockwise by calling the Rotate90() method.
ContrastImage() accepts a signed integer to modify the contrast of the image. A positive value increases the sharpness of the image and a negative value decreases its sharpness.
CropImage() accepts coordinates of a clipping rectangle used to crop the image currently loaded in memory. Coordinates are passed in as the left, top, right and bottom positions. If positions are passed that extend beyond the maximum reach of the current image, the clipping coordinates are limited to the rightmost and bottommost positions of the image.
The following code demonstrates how to use the image manipulation methods.
void Manipulate(SECPcx *pSrc) { // First rotate it pSrc->Rotate90(); // Next Flip it on the horizontal axis pSrc->FlipHorz(); // Flip again on the vertical axis pSrc->FlipVert(); // Dull the image by decreasing contrast pSrc->ContrastImage(-1); // Then crop the image to a hypothetical region pSrc->CropImage(50, 50, 100, 100); } |
SECImage and derivatives allow you to save an image to a file via the SaveImage() method. SaveImage() succeeds only when a legitimate image is loaded in the image class. The following code demonstrates how to save a .PCX image to a file named new.pcx.
if (pcx.SaveImage("new.pcx") == FALSE) ASSERT(1); //SaveImage failed! |
Using Objective Toolkit, you can create a device-specific bitmap from an SECImage instance. To do this, ensure that then image is at the same resolution as the device. Otherwise, the CBitmap is created for an incorrect display type. You can create a CBitmap object with the MakeBitmap() method, which returns a pointer to a new CBitmap object. MakeBitmap() accepts a pointer to the current device context as an argument (CDC pointer).
When you perform GDI drawing functions to a device context (CDC object), you need to convert the image of the bitmap selected in the device context to the SECImage format. To do so, use the CreateFromBitmap() method, which accepts CBitmap and CDC pointers as arguments. The created SECImage instance contains the same dimensions and depth as the CBitmap itself. If the CDC is a memory device context, ensure that you clear the bitmap with a GDI call before making the call to CreateFromBitmap().
This is a two-step process:
Create a CBitmap object from your CDC.
Instantiate an SECImage object and call SECImage::CreateFromBitmap().
You can create a CBitmap with the following code:
CBitmap bitmap; bitmap.CreateCompatibleBitmap(&dc); CDC dcMem; dcMem.CreateCompatibleDC(&dc); CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap); // Todo: draw to the memory dc here... dcMem.SelectObject(pOldBitmap); |
The SECImage base class does not support direct loading of image data from a resource; however, you can load images indirectly. After you have imported your image file as binary data in the resource editor, do the following to load it:
Obtain a pointer to the resource data.
Attach the data to a CMemFile object.
Instantiate an SECImage-derived object and call LoadImage() using the CMemFile.
The following section of code demonstrates this:
// The image is stored as a resource in file format. HINSTANCE hinst = AfxGetInstanceHandle(); HRSRC hRes = FindResource(hinst, szResNavn, szResType); if (hRes == NULL) { TRACE2("Couldn't find restype %s resource %s!\n", szResType,szResNavn); return FALSE; } // need the pointer to the image data and it's length DWORD len = SizeofResource(hinst, hRes); BYTE* lpImage = (BYTE*)LoadResource(hinst, hRes); ASSERT(lpRes); // CMemFile is CFile-derived, and will soon be used to // to load the image using SECImage::LoadImage CMemFile* pImgMemFile = new CMemFile(); SECJpeg* pJpeg; \\ let's assume I know the image will be JPEG // Attach the image data to a CMemFile, which will allocate space. // ImageBufLen is the size of the image buffer pImgMemFile->Attach(lpImage, lImageBufLen); // now use the CMemFile to load into the SECJpeg object // for manipulation or display pJpeg = new SECJpeg(); if (!pJpeg->LoadImage(pImgMemFile)) { TRACE0("Couldn't LoadImage"); return FALSE; } // can delete the CMemFile now since LoadImage allocated its own // space delete pImgMemFile; pImgMemFile = NULL; FreeResource((HANDLE)lpRes); return(str); . . . |
Although the SECImage class does not directly support streaming, you can save SECImage to a CMemFile and then stream the data from CMemFile.
// The image is stored in the database in file format. It has // been retrieved here to a buffer called pImageInBuffer; LPBYTE lpImage = pImageInBuffer; CMemFile* pImgMemFile = new CMemFile(); \\ CFile derived class SECJpeg* pJpeg; \\ let's assume I know the image will be JPEG // Attach the image data to a CMemFile, which will allocate space. // ImageBufLen is the size of the image buffer pImgMemFile->Attach(lpImage, lImageBufLen); // now use the CMemFile to load into the SECJpeg object // for manipulation or display pJpeg = new SECJpeg(); if (!pJpeg->LoadImage(pImgMemFile)) error("Couldn't LoadImage"); // can delete the CMemFile now since LoadImage allocated its // own space delete pImgMemFile; pImgMemFile = NULL; // display the image in the pJpeg and or whatever manipulations // are required to it ... // now save it back to a CMemFile pImgMemFile = new CMemFile; if (!pJpeg->SaveImage(pImgMemFile)) error("Couldn't SaveImage"); // can now get the image back to the database via the lpImage ptr // this will store the image length in lImageBufLen and the // image itself // into lpImage lImageBufLen = pImgMemFile->GetLength(); lpImage = pImageMemFile->Detach(); // probably don't want to delete the pImageMemFile until you've // copied lpImage to the database or at lease to some other buffer // since Detach() just returns a pointer to CMemFile's buffer. |
Ensure that you set the nGrowBytes for the CMemFile. The default is 1024.
Copyright © Rogue Wave Software, Inc. All Rights Reserved.
The Rogue Wave name and logo, and Stingray, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.