Introduction
The main purpose of the CExBitmap
class is to provide the capability to undo and redo changes to the bitmap attach to the CExBitamp
object. To accomplish these tasks it was necessary to provide the ability to store a backup copy of the currently attach bitmap in the CExBitmap
object as well. By allowing multiple backups to be stored in the class it becomes much more flexible, which makes it more useful for paint programs.
To further enhance the CExBitmap
class I decided to create the utility class CDibData
. The purpose of the CDibData
class is to provide support for the direct manipulation of the bitmaps image, as well as provide methods for: loading, saving, and color-depth conversion of bitmaps. CDibData
is used to simplify the rotation of bitmaps in 90 degree increments, as well as for loading and saving of bitmap.
The undo and redo arrays store what I call partial bitmaps. A partial bitmap is a bitmap that represents only a part (sub-bitmap) of the original bitmap, in order to reduce the a memory required to store the bitmaps. A partial bitmap object stores the partial bitmap, the size of the original bitmap from which it was copied, and the rectangle representing the location (relative to the upper left hand corner of original source bitmap) from which it was copied. When an undo or redo occurs, the source size information is used to restore the bitmap to the size it was at the time of saving and the positional rectangle is used to copy the partial bitmap to the location from which is was copied, the information to reverse the action is automatically stored in the opposite array, so that the action can be reversed. If the undo or redo array limit is reached then the first item is deleted and all items stored in the array are moved down by one and the new item being stored is placed at the end of the array (LIFO). The undo array copies the partial bitmap data from the backup bitmap, therefore it is necessary to update the backup after making any changes to the original bitmap. The redo array copies the partial bitmap from the original bitmap. The maximum size of the undo/redo array is settable by the user and is only limit by the amount of memory available (or maximum number of items that can be store in an object array).
The backup array always contains bitmaps the same size as the original, in order to reduce the number of reallocation needed. This is because I expect very few backups to be stored, usually no more than one or two. The backup array also stores the bitmaps as partial bitmaps. The number of backups that can be stored is only limit by the amount of memory available (or maximum number of items that can be store in an object array).
Files required by CExBitmap
File |
Contents |
ExBitmap.h |
Extended bitmap class header
|
|
ExBitmap.cpp |
Extended bitmap class code |
CDibData.h |
DIB data class used by CExBitmap |
CDibData.cpp |
DIB data class code |
CWorkDC.h |
Working DC class used by CExBitmap |
CWorkDC.cpp |
Working DC class code |
Quantize.h |
CQuantizer class used by CDibData |
Quantize.cpp |
CQuantizer class code |
MyTrace.h |
Debug trace functions used by CExBitmap and CDibData |
MyTrace.cpp |
Debug trace code |
Using the code
- Loading a bitmap:
- You may load the bitmap in any way you wish, as the
CExBitmap
class only needs a bitmap handle in order to do its' thing. I have provided the method LoadDIB()
, to simplify loading of bitmap and to support bitmap types that are not supported by LoadImage()
(namely top-down bitmaps).
- Saving a bitmap:
- Call
SaveDIB()
which supports the following features: compression (4 and 8 bpp), color-depth conversion, and color palette optimization.
- Using the undo/redo features:
- 1. Call
SetUndoSize()
if you wish to use an undo/redo limit other than the default limit of 10.
- 2. Call
SaveUndo()
before modifying bitmap.
- 3. Call
Backup()
after modifying bitmap.
- 4. Call
Undo()
from inside your undo handler.
- 5. Call
Redo()
from inside your redo handler.
Note: If you used EnableAutoUndo()
to enable automatic undo/redo for Flip()
and Rotate()
, then you do not need to do the above for those methods.
Drawing methods
Method |
Prupose |
Draw |
Simplifies drawing of bitmap. |
TransBlt |
Transparent blit, supports most versions of windows. |
DrawBackup |
If TESTING_UNDOREDO defined. |
DrawUndo |
If TESTING_UNDOREDO defined. |
DrawRedo |
If TESTING_UNDOREDO defined. |
Manipulation methods
Method |
Prupose |
CopyBitmap |
Supports full, partial, and stretch copying. |
ExpandBitmap |
Expands/Shrinks bitmap size. |
ClearBimap |
Clears all or part of the bitmap to a given color. |
Flip |
Flips bitmap either horizontally, vertically, or both. |
FlipHorizontal |
Flips bitmap horizontally. |
FlipVertical |
Flips bitmap vertically. |
Rotate |
Rotates bitmap by 90, 180, or 270 degrees clockwise. |
Note: If your program is only going to run on NT 3.1 and above the you can define WINNT31_ROTATE in you project so that rotation is accomplished using world transforms.
Backup methods
Method |
Prupose |
UseBackup |
Sets which backup image is currently selected. |
RestoreBackup |
Restores bitmap from backup, partial or full restore. |
RemoveBackups |
Frees all backups from memory. |
GetBackupSize |
Current size of backup array (contents of array may not be valid). |
IsBackupValid |
Determines if the currently selected backup is valid. |
Undo/Redo methods
Method |
Prupose |
SetUndoSize |
Sets the maximum size of the undo and redo arrays. |
SaveUndo |
Saves all or part of the bitmap to end of undo array. |
Undo |
Restores the part of the bitmap stored at the end of undo array and stores the change to the redo array. |
Redo |
Restores the part of the bitmap stored at the end of the undo array and stores the change to the undo array. |
RemoveUndo |
Frees all undoes stored in the undo array. |
RemoveRedo |
Frees all redoes stored in the redo array. |
GetUndoSize |
Get the number of items currently stored in the undo array. |
GetRedoSize |
Get the number of items currently stored in the redo array. |
IsModified |
Determines if the bitmap has been modified; based on the number of undoes it would require to restore bitmap back to its' original state. |
EnableAutoUndo |
Automatically place items in the undo and redo arrays, only applies to Rotate() and Flip() . |
Loading and Saving
Method |
Prupose |
SaveDIB |
Saves bitmap to file, can be used for color-depth conversion. |
LoadDIB |
Loads bitmap from file, including top-down bitmaps. |
Examples of using the undo/redo methods:
void CExBmpDemoView::WhatEver()
{
CExBmpDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDoc->m_exBitmap.SaveUndo();
pDoc->m_exBitmap.Backup();
}
void CExBmpDemoView::OnRotate180()
{
CExBmpDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(pDoc->m_exBitmap.Rotate(1) )
{
AdjustScrollBars();
Invalidate();
}
}
void CExBmpDemoView::OnRotate180()
{
CExBmpDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDoc->m_exBitmap.SaveUndo();
if( pDoc->m_exBitmap.Rotate(1) )
{
backuppDoc->m_exBitmap.Backup();
AdjustScrollBars();
Invalidate();
}
}
void CExBmpDemoView::FlipOrRotate(BOOL bFlip, int nDirection)
{
CExBmpDemoDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if( pDoc->m_exBitmap.GetSafeHandle() )
{
if( m_bRectSelected )
{
if( m_nMag < 1 )
m_nMag = 1;
m_rectSel.OffsetRect(-m_nBorderWidth, -m_nBorderWidth);
CRect rectSrc(m_rectSel.left/m_nMag, m_rectSel.top/m_nMag,
m_rectSel.right/m_nMag, m_rectSel.bottom/m_nMag);
m_rectSel.OffsetRect(m_nBorderWidth, m_nBorderWidth);
CExBitmap exTempBitmap;
{
CWorkDC dcWork(NULL,&pDoc->m_exBitmap);
if( !exTempBitmap.CreateCompatibleBitmap(
&dcWork, rectSrc.Width(), rectSrc.Height()) )
return;
}
CRect rectDest(0, 0, rectSrc.Width(), rectSrc.Height());
if( !exTempBitmap.CopyBitmap(
pDoc->m_exBitmap, rectSrc, rectDest) )
return;
if( bFlip )
{
if( !exTempBitmap.Flip(nDirection) )
return;
}
else
{
if( !exTempBitmap.Rotate(nDirection) )
return;
}
SIZE sizeTempBitmap = exTempBitmap.GetSize();
CRect rectUndo(rectSrc.left, rectSrc.top,
rectSrc.left + max(sizeTempBitmap.cx, rectSrc.Width()),
rectSrc.top + max(sizeTempBitmap.cy, rectSrc.Height()));
pDoc->m_exBitmap.SaveUndo(&rectUndo);
pDoc->m_exBitmap.ClearBitmap(RGB(255,255,255), &rectSrc);
pDoc->m_exBitmap.CopyBitmap(
exTempBitmap, FALSE, rectSrc.left, rectSrc.top);
pDoc->m_exBitmap.Backup();
if( !bFlip )
{
if( nDirection != 1 )
{
if( sizeTempBitmap.cx != rectSrc.Width() ||
sizeTempBitmap.cy != rectSrc.Height() )
{
m_bRectSelected = FALSE;
}
}
}
AdjustScrollBars();
Invalidate();
}
else if( bFlip )
{
if( pDoc->m_exBitmap.Flip(nDirection) )
{
AdjustScrollBars();
Invalidate();
}
}
else
{
if( pDoc->m_exBitmap.Rotate(nDirection) )
{
AdjustScrollBars();
Invalidate();
}
}
}
}
Points of Interest
You can use the Doxygen.dat (in the ExBitmap directory) file with the Doxywizard to generate HTML documentation for this class. You can download Doxygen and Doxywizard at Doxygen.org.
For those who care: I have change the comment style used in both ExBitmap.xxx and CDibData.xxx, to improve the documentation generated by Doxygen.
Some questions and answers:
- Question: Why might you need to be able to save more than one backup of bitmap?
- Answer: When I was designing a bitmap editor for my use: I decided that when drawing a polygon, that I wanted the ability to undo the drawing in one step instead of undoing it one line at a time. The solution was to use two backups: one for normal undoes/redoes and one for use while drawing the polygon.
- Question: Why is there a colored rectangle drawn around the partial bitmaps displayed under the headings Undo and Redo?
- Answer: The colored rectangle is used to show the positional relationship of the partial bitmap (displayed) to the origanal bitmap, from which it was copied.
- Question: Why use a separate utility class (
CDibData
) to gain direct access to bitmap bits?
- Answer: I created
CExBitmap
first and did not like messing it up.
- Question: Other than the undo/redo features, what part of the code do you think is worth examining?
- Answer: Take a look at the
Flip()
and Rotate()
methods. I have provided two version of the rotation method: one that uses CDibData
objects and one that uses world transforms (NT/2000 and above only).
- Question: Is the
CDibData
class useful by itself?
- Answer:See article CDibData.
- Question: Where are the references?
- Answer: The only reference I used when creating this class was MSDN. Now CDibData, on the other hand, required a lot of research (reference list in CDibData.cpp).