═══ 1. Bitmap Class Overview ═══ What it does: The Bitmap class provides the means to create and manipulate bitmaps. With it, you can load and save bitmaps which are not part of a Presentation Manager resource. A Gpi bitmap can be used to create a Bitmap object or the object can create a Gpi bitmap. The class handles 1-, 4-, 8-, and 24-bpp bitmaps. It can read Windows bitmaps as well as read and save OS/2 version 1x and 2x bitmaps. The class was designed to provide direct access to image data in support of a graphical rendering (raytracing) application. As such, it provides a "buffer" for building and transferring bitmap data between an application and a Gpi bitmap. Member functions access the image data allowing the application to set or query image pels independent from a device context. Other functions transfer the image to or from a presentation space. The consequence of this is that a large amount of memory is allocated to the image data. However, it is possible to build a Gpi bitmap from the object and then discard the object. What it doesn't: It does not load bitmap arrays. However, a bitmap from an array can be loaded if the file is positioned at a bitmap file header. Neither does it save a bitmap array. A single bitmap can be written to an array file if the file is properly positioned. It will not update the bitmap array file header. Several member functions will raise exceptions if the bitmap is compressed. However, it can be decompressed. Tools: The class was developed using IBM C/Set++ Tools. It is provided as a DLL; it requires the runtime libraries provided with the IBM product. They are not distributed with the class. Files: The DLL, LIB, HPP, and INF files are provided as "freeware". They may be freely distributed and incorporated into any software you develop. Support: The author will provide support via CompuServe account 70252,3144. Source Code: Source code is available from the author for $20.00 US. The source includes HPP, CPP, DEF and WF (HyperWise) files. Please remit to: William A. Leonard 29 Bow Street Jamestown, RI 02835 Disclaimer: The author will not be liable for any bug, error, omission, defect, deficiency, or nonconformity in this software. The author also disclaims all implied warranties, including without limitation warranties of merchantability, performance, and fitness for a particular purpose. This software is provided "as is" and the user assumes the entire risk as to its quality and performance. ═══ 2. Data Members ═══ The following data members of the Bitmap class are private to the class: PBITMAPINFO2 _pbmi2 Pointer to a BITMAPINFO2 structure. The structure is created during construction of the Bitmap object or when a bitmap is loaded. It is not settable but can be accessed by Bitmap::bmi(). The structure is also accessible as a BITMAPINFOHEADER2 structure. The structure is destroyed when the Bitmap object is destroyed. PBYTE _pbmb Pointer to bitmap image data. The data is in the format described by the BITMAPINFOHEADER. The memory for this data is allocated during construction of the Bitmap object or when a bitmap is loaded. It is not settable but can be accessed by Bitmap::bmb(). ULONG _ulScanlineSize The size of a row of bitmap image data. The size is _pbmi2->cBitCount * _pbmi2->cx + 31) / 32) * 4 * _pbmi2->cPlanes The value is calculated when the object is constructed or when a bitmap file is loaded. It is not settable but can be accessed by Bitmap::scanLineSize(). IHandle _hdc Handle to device context. This is set when a Gpi bitmap is created from Bitmap::createGpiBitmap(). Otherwise, it is not settable. It can be accessed by Bitmap::hdc(). The DC is closed when the Bitmap object is destroyed. IPresSpaceHandle _hps Handle to a presentation space. This is set when a Gpi bitmap is created from Bitmap::createGpiBitmap(). Otherwise it is not settable. It can be accessed by Bitmap::hps(). The PS is destroyed when the Bitmap object is destroyed. IHandle _hpal Handle to a palette if palette manager functions are supported. This is set when a Gpi bitmap is created from Bitmap::createGpiBitmap(). Otherwise it is not settable. It can be accessed by Bitmap::hpal(). The palette is destroyed when the bitmap object is destroyed. IBitmapHandle _hbm Handle to a Gpi bitmap. This is set when the Gpi bitmap is created from Bitmap::createGpiBitmap(). Otherwise it is not settable. It can be accessed by Bitmap::hbm(). The Gpi bitmap is deleted when the Bitmap object is destroyed. ═══ 3. Member Functions ═══ ═══ 3.1. Constructors & Destructor ═══ Bitmap(IString const& fname) fname - the name of an unopened bitmap file. This constructor will build a Bitmap object and read the header and image data from the file. The file must not contain a bitmap array header. This constructor does not create a Gpi bitmap - call Bitmap::createGpiBitmap(). The file is closed after it is read. If the image is compressed, the memory allocated for the image data is not large enough to contain the uncompressed image. Bitmap(const IBitmapHandle& hbmp) hbmp - handle to an existing Gpi bitmap. This constructor builds a Bitmap object from the first 16 bytes of BITMAPINFOHEADER2 of the Gpi bitmap. That is, only cx, cy, cPlanes, and cBitCount are preserved from the Gpi bitmap. Space is allocated for the color table and bitmap image data but neither is initialized. This constructor does not create a Gpi bitmap. Bitmap(PBITMAPINFOHEADER2 const pbmp) pbmp - pointer to a BITMAPINFOHEADER2. This constructor builds a Bitmap object from a BITMAPINFOHEADER2. The header is copied. Space is allocated for the color table and image data but is uninitialized. Use this constructor for creating a bitmap from scratch. Simply create a BITMAPINFOHEADER2 and set: (ULONG)cbFix = 16, (ULONG)cx, (ULONG)cy, (USHORT)cPlanes, (USHORT)cBitCount. If other options are desired, set those members of the structure and modify cbFix. Memory allocated for the image data is determined from cx, cy, cPlanes, and cBitCount unless cbImage is given and is not zero. Compression is not used to calculate the size: the default memory allocation is large enough for the uncompressed image. This constructor does not create a Gpi bitmap - call Bitmap::createGpiBitmap(). ~Bitmap() The destructor frees memory allocated for the BITMAPINFOHEADER2, color table, and image data. If a Gpi bitmap is associated with the object, it, too, is destroyed along with its DC, PS, and palette. To avoid destroying the Gpi bitmap, DC, PS, and palette: call Bitmap::disassociate() first. ═══ 3.2. Accessors ═══ The following member functions access data of the Bitmap object - not a Gpi bitmap. In fact, a Gpi bitmap need not exist. See Gpi Bitmap Functions for accessing Gpi bitmaps. PBITMAPINFOHEADER2 bmp() This function returns a pointer to a BITMAPINFOHEADER2 structure describing the bitmap. Individual fields in the structure may be accessed or modified using this pointer. Certain Gpi functions that require a pointer to a BITMAPINFOHEADER2 may use the returned pointer. The returned pointer may be cast to a BITMAPINFO2 structure because the color table immediately follows the fixed portion of the structure. PBITMAPINFO2 bmi() This function returns a pointer to a BITMAPINFO2 structure describing the bitmap. Individual fields in the structure may be accessed or modified using this pointer. Certain Gpi functions that require a pointer to a BITMAPINFO2 may use the returned pointer. PRGB2 rgb(USHORT index=0) index - the index of the desired color. This function returns a pointer to an RGB2 structure in the color table. The default index can be used to return the entire array. Returns NULL if this is a 24-bpp bitmap or if the index is out of range. PRGB2 rgb(USHORT row, USHORT col) row - the row coordinate in the image. col - the column coordinate in the image. Returns a pointer to the color of a pel at a specific location in the image. For a 24-bpp bitmap, the returned pointer points to a RGB structure in the image data. For bitmaps with a color table, the returned pointer points to a RGB2 structure in the color table. If you use this accessor to change the rgb value: in a 24-bpp you will change the color of a single pel; otherwise you will change the color of all pels that share that color table entry. An exception is raised if the image is compressed. PBYTE bmb(USHORT row=0, USHORT col=0) row - the row coordinate in the image. col - the column coordinate in the image. Returns a pointer to a byte containing bitmap bits at a specific point in the image. The default arguments return the image data array. The default col returns the row array. An exception is raised if the image is compressed unless both row and col are zero. USHORT index(USHORT row, USHORT col) row - the row coordinate in the image. col - the column coordinate in the image. Returns the color index of a pel at a specific point in the image. For a 24-bpp bitmap, returns 0xffff. An exception is raised if the image is compressed. ULONG scanLineSize() Returns the size of a scan line (a row) of image data. This is always a multiple of 4 bytes. The scanline size is calculated for an uncompressed image. ═══ 3.3. Modifiers ═══ The Bitmap class allocates memory for the headers, color table, and image data. Accessors return pointers to these making them available to the application for direct manipulation. Therefore, there is no need for member functions to modify them... with one exception: void setIndex(USHORT indx, USHORT row, USHORT col) indx - the index to set. row - the row coordinate of the pel. col - the column coordinate of the pel. This function sets the color of a pel at the given coordinate. In a 24-bpp bitmap, this function has no effect. Otherwise, this function masks and shifts the color index and imbeds it in the image at the proper location. ═══ 3.4. Gpi Bitmap Functions ═══ The Bitmap object is device independent. It is also Gpi independent. These functions are provided to bridge the gap. IBitmapHandle createGpiBitmap(Boolean bPal=0) Opens a memory DC and creates a PS. If bPal is not false, it also creates a Gpi palette from the color table (if there is one and palette manager functions are supported) and selects it into the PS. An initialized Gpi bitmap is created from the BITMAPINFO2 data member and the image data. The Gpi bitmap is associated with the Bitmap object. An exception is raised if there is already a Gpi bitmap associated with the object. void blitTo(IPresSpaceHandle hps, IPoint& destPt, IRectangle& sourceRect) hps - the target presentation space handle. destPt - the lower left corner of the rectangle in the target hps. sourceRect - the rectangle defining the area in the image to be transferred. Performs GpiDrawBits() from the Bitmap object's image data to the target hps. If the target hps is 0, this function performs GpiDrawBits() to the associated Gpi bitmap's PS. Otherwise, the target hps may need to have a Gpi bitmap selected - see the documentation for GpiDrawBits(). sourceRect defines the rectangle of image data. It is intersected with the bitmap. The result is used for the transfer. destPt defines the lower left corner of the rectangle in hps. It is adjusted as necessary for the intersection rectangle. The source and (implied) target rectangles are the same size. The blit operation is limited to ROP_SRCCOPY, BBO_IGNORE. For other options, call the Gpi function directly. void blitTo(IPresSpaceHandle hps = 0) hps - the target presentation space Performs GpiDrawBits() from the Bitmap object's image data to the target hps. If the target hps is 0, this function performs GpiDrawBits() to the associated Gpi bitmap's PS. Otherwise, the target hps may need to have a Gpi bitmap selected - see the documentation for GpiDrawBits(). The entire bitmap is transferred. void blitFrom(IPresSpaceHandle hps, IPoint& sourcePt, IRectangle& destRect) hps - the source presentation space handle. sourcePt - the lower left corner of the source rectangle in the hps. destRect - the destination rectangle in the object's image data. Performs a GpiQueryBitmapBits() from the source hps to the image data of the Bitmap object. If hps is zero, then the hps data member is used as the source. destRect defines the target rectangle. It is intersected with the bitmap and the result is used in the transfer. sourcePt defines the lower left corner of the source rectangle. It is adjusted as necessary to the intersection rectangle. The (implied) source and target rectangles are the same size. Note that GpiQueryBitmapBits() transfers entire rows. The x coordinates of both destRect and sourcePt are ignored. The arguments were chosen to conform to the style of blitTo(). Also be aware that GpiQueryBitmapBits() may modify the BITMAPINFO2 data member structure: the format of the bitmap and the color table entries may be altered to match the presentation space/device context used as the source. A Gpi bitmap must be selected into the source PS. void blitFrom(IPresSpaceHandle hps = 0) hps - the source presentation space handle. Performs a GpiQueryBitmapBits() from the source hps to the image data of the Bitmap object. If hps is zero, then the hps data member is used as the source. The entire bitmap is transferred. GpiQueryBitmapBits() may modify the BITMAPINFO2 member structure: the format of the bitmap and the color table entries may be altered to match the presentation space/device context used as the source. A Gpi bitmap must be selected into the source PS. void disassociate() If a Gpi bitmap is associated with a Bitmap object, it lives and dies with the Bitmap object. If the Gpi bitmap is to be independent it must be disassociated from the object. This function breaks the association. Note that the DC, PS, and Gpi palette, too, are no longer associated with the Bitmap object. They must be closed and destroyed by the application. (Be sure to save their handles - the Bitmap object won't retain them.) Once disassociated, a new Gpi bitmap may be created and a new association formed. The association can be made only through the Bitmap::createGpiBitmap(). void deleteGpiBitmap() As a Bitmap object can create a Gpi bitmap, so, too, can it destroy a Gpi bitmap. This function will close the DC, destroy the PS, delete the Gpi palette, and delete the Gpi bitmap - but only if the Gpi bitmap is associated with the Bitmap object. The association is also disolved. IBitmapHandle hbm() This function is an accessor to the handle of the associated Gpi bitmap. IHandle hdc() This function is the accessor to the device context handle used by the associated Gpi bitmap. IPresSpaceHandle hps() This function is the accessor to the presentation space handle used by the associated Gpi bitmap. IHandle hpal() This function accesses the palette handle if a palette is associated with the Bitmap object. ═══ 3.5. Bitmap File I/O ═══ The Bitmap class can load Windows, OS/2 version 1x, and OS/2 version 2x bitmap files. The files can be bitmap arrays (icons) if the application can manage the BITMAPARRAYFILEHEADER2 structure. These functions transfer data between the Bitmap object and a file. The Gpi bitmap is not involved (except when loading a file - any previously associated Gpi bitmap is discarded) . void load(ifstream& BMPfile) BMPfile - an object of ifstream (an open input file) Reads the BITMAPFILEHEADER(2), BITMAPINFOHEADER(2), color table, and image data from an open file. The file must be positioned properly to the BITMAPFILEHEADER(2). This function is not intended to be called by the application unless the file is a bitmap array. Instead, use the constructor which takes a file name as the argument. If an association exists with a Gpi bitmap, that association is disolved and those Gpi resources discarded. void save1x(ofstream& BMPfile) BMPfile - an object of ofstream (an open output file) Saves a version 1x bitmap to an open file. Writing to the file begins at the current file position and includes the BITMAPFILEHEADER, BITMAPINFOHEADER, the RGB color table and bitmap bits. For a single-bitmap file, simply open the file, call this function, and close the file. For a bitmap array, be sure to update the BITMAPARRAYFILEHEADER. void save2x(ofstream& BMPfile) BMPfile - an object of ofstream (an open output file) Saves a version 2x bitmap to an open file. Writing to the file begins at the current file position and includes the BITMAPFILEHEADER2, BITMAPINFOHEADER2, the RGB2 color table and bitmap bits. For a single-bitmap file, simply open the file, call this function, and close the file. For a bitmap array, be sure to update the BITMAPARRAYFILEHEADER2. ═══ 4. Examples ═══ ═══ 4.1. Display a Bitmap from a file ═══ A Canvas class is derived from ICanvas. It has a data member (_pBitmap) pointing to a Bitmap object to be painted. When the Canvas is painted, the bitmap is drawn. Note that in blitTo() the invalid rectangle is intersected with the bitmap so that any invalid region outside the bitmap image does not cause unpredictable results. Canvas::Canvas(unsigned long id, IWindow* parent, IWindow* owner) : ICanvas(id, parent, owner) { _pBitmap = new Bitmap("\\os2\\bitmap\\shells.bmp") ; IPaintHandler::handleEventsFor(this) ; } Canvas::~Canvas() { delete _pBitmap ; } ... Boolean Canvas::paintWindow(IPaintEvent& paintEvent) { IPresSpaceHandle hps(paintEvent.presSpaceHandle()) ; paintEvent.clearBackground(paintEvent.rect()) ; _pBitmap->blitTo(hps, paintEvent.rect().bottomLeft(), paintEvent.rect()) ; paintEvent.setResult(true) ; return true; } ═══ 4.2. Capture the Screen & Save Compressed ═══ In this example the screen is captured, converted to 4-bpp, compressed, and saved to a BMP file in version 2x format. The application should hide its window first or the result will be a self-portrait. Bitmap* pBitmap ; BITMAPINFOHEADER2 bmp ; RECTL rectl ; HPS hpsScreen ; // Set up BITMAPINFOHEADER2 from screen parameters WinQueryWindowRect(HWND_DESKTOP, &rectl) ; bmp.cbFix = 20 ; bmp.cx = rectl.xRight ; bmp.cy = rectl.yTop ; bmp.cPlanes = 1 ; bmp.cBitCount = 4 ; bmp.ulCompression = BCA_RLE4 ; // create Bitmap object & Gpi bitmap pBitmap = new Bitmap(&bmp) ; pBitmap->createGpiBitmap() ; // blit from screen to Gpi bitmap (compressed) POINTL aptl[3] ; aptl[0].x = aptl[0].y = 0 ; aptl[1].x = bmp.cx ; aptl[1].y = bmp.cy ; aptl[2] = aptl[0] ; hpsScreen = WinGetScreenPS(HWND_DESKTOP) ; GpiBitBlt(pBitmap->hps(),hpsScreen,3,aptl,ROP_SRCCOPY,BBO_IGNORE) ; WinReleasePS(hpsScreen) ; // transfer bitmap to application store pBitmap->blitFrom() ; // save bitmap to file ofstream bmpFile("screen.bmp") ; pBitmap->save2x(bmpFile) ; bmpFile.close() ; delete pBitmap ; ═══ 4.3. Decompress Image ═══ If a bitmap is only to be displayed, it's not necessary to decompress it. If, however, it will be written to a file uncompressed or its pels manipulated, it must be decompressed. This is done by transferring it from application store to a hps and then transferring it back to a new application store. (This procedure ignores a custom color table.) Bitmap* decompress(IString const& filename) { Bitmap* compressedBitmap ; Bitmap* uncompressedBitmap ; compressedBitmap = new Bitmap(filename) ; // load bitmap if (compressedBitmap->bmp()->ulCompression) { // is compressed ? // create Gpi bitmap, hps, etc.; associate with compressed bitmap compressedBitmap->createGpiBitmap() ; // construct Bitmap object using Gpi bitmap dimensions // this constructor omits compression uncompressedBitmap = new Bitmap(compressedBitmap->hbm()) ; // convert format uncompressedBitmap->blitFrom(compressedBitmap->hps()) ; // discard compressed bitmap object & its Gpi bitmap delete compressedBitmap ; return uncompressedBitmap ; } else // is not compressed return compressedBitmap ; }