Here is an example of how to extend the CLayeredBitmapCtrl
to be used within a CScrollView
application:
Introduction
This article is about a CStatic
derived class that supports multiple bitmap layers. The CLayeredBitmapCtrl
class allows you to add/remove/show/hide bitmap layers on a static control. Each bitmap layer can have a user defined transparent color or retrieve the transparent color from a specified location in the bitmap. This allows the background bitmap to be visible through the transparent color of another bitmap layer. Tool tips are also used so that each layer can display its own description. Each layer can also have a non-transparent region that allows the tool tip to be displayed only when the mouse is over the non-transparent portion of the layer. The first layer is not usually set to transparent because it is used as the background. All of the other layers should have the transparent flag set, otherwise the first layer will not be visible, unless the layer is smaller and rectangular.
Background
I wrote this code because I needed a way of displaying a background image that contains multiple smaller images on it. I also needed a quick way of adding and removing images from the display. Being able to get more information about a particular image while the mouse was over it, was a must. That is why I used a technique to create a non-transparent region, so that only the visible portion of the image will trigger the tool tip description.
New Features (Added for Version 2.01)
- Ability to specify the background color of the control. Previously, the background was always black (default).
- The background color of the control can also be set to use the system color.
- As an added bonus, the control will reflect changes to the system color.
Existing Features
Added for Version 2.0
- Ability to move a layer with the mouse, within the control.
- Use of a focus rectangle when the left mouse button is clicked in a layer.
- Focus rectangle settings (including color) are layer dependent.
- Added methods for changing various layer attributes, including description, transparent color and location.
- Added a Create function so that the control can be created dynamically within a
CView
, etc.
From Initial Release
- Derived from
CStatic
which makes it easy to add to a dialog window.
- Support for multiple bitmap layers.
- Easy to add, remove, show and hide layers.
- Ability to swap layers.
- Enable tool tips to help identify each visible layer that contains a region.
CLayerInfo Class
The CLayerInfo
class contains all of the necessary elements for a layer object. The comparison functions are only provided for sorting the vector in the CLayeredBitmapCtrl
control. The CLayeredBitmapCtrl
class contains a vector of CLayerInfo
objects. A new copy of the CLayerInfo
object is added to the vector, which means that you can use a temporary CLayerInfo
variable when creating layers.
Construction/Destruction
public:
CLayerInfo();
CLayerInfo( const CLayerInfo &src );
virtual ~CLayerInfo();
Implementation
public:
CLayerInfo &Copy( const CLayerInfo &src );
CLayerInfo &operator=( const CLayerInfo &src );
bool operator==( const CLayerInfo &layerInfo ) const;
bool operator<( const CLayerInfo &layerInfo ) const;
bool operator>( const CLayerInfo &layerInfo ) const;
static bool CopyBitmap( const CBitmap &bmpSrc, CBitmap &bmpDest );
static bool CopyBitmapSection( CBitmap &bmpSrc,
CBitmap &bmpDest, CRect &rectSection, CDC *pDC = NULL );
static BITMAPINFO *PrepareRGBBitmapInfo( int nWidth, int nHeight );
static COLORREF GetColorFromBitmap( const CPoint &ptLocation,
CBitmap &bitmap );
Attributes
public:
int m_nLayerID;
int m_nLayerIndex;
CString m_strLayerDesc;
bool m_bVisible;
bool m_bUseRgn;
bool m_bTrackingEnabled;
bool m_bTracking;
bool m_bShowOnTopWhileTracking;
CPoint m_ptTrackingStart;
bool m_bTransparent;
COLORREF m_colTransparent;
CPoint m_ptTransparentPixel;
CPoint m_ptLocation;
bool m_bFocusRectangleEnabled;
bool m_bShowFocusRectangle;
COLORREF m_colFocusRectangle;
CBitmap m_bmp;
CRgn m_rgn;
CLayeredBitmapCtrl Class
The CLayeredBitmapCtrl
is a CStatic
derived class that provides the ability to add/remove/show/hide bitmap layers on a static control. The first layer is not usually set to transparent because it is used as the background. All of the other layers should have the transparent flag set, otherwise the first layer will not be visible. The exception to this is if you have more than one background layer and you only set the show flag for one or the other.
Construction/Destruction
public:
CLayeredBitmapCtrl();
virtual ~CLayeredBitmapCtrl();
Attributes
public:
CBitmap m_bmpCombined;
protected:
vector<CLayerInfo> m_vecLayerInfo;
bool m_bShowToolTips;
bool m_bToolTipsInitialized;
bool m_bHideTrackingToolTip;
CString m_strToolTip;
CToolTipCtrl m_toolTip;
bool m_bIsParentDlg;
bool m_bUseSysColor;
COLORREF m_colCtrlBG;
Public Methods
void CLayeredBitmapCtrl::InitToolTips();
Initializes the tool tip control.
void CLayeredBitmapCtrl::ShowToolTips( bool bShow );
Enables or disables tool tips.
bool CLayeredBitmapCtrl::ToolTipsEnabled() const;
Indicates whether or not tool tips are enabled.
void CLayeredBitmapCtrl::UseSysColor( bool bUseSysColor, bool bRedraw = false );
Specifies whether or not to use the system color for the background of the control.
bool CLayeredBitmapCtrl::UsingSysColor() const;
Indicates if the control is using the system color.
void CLayeredBitmapCtrl::SetCtrlBGColor( COLORREF colCtrlBG, bool bRedraw = false );
Allows the user to change the background color of the control.
COLORREF CLayeredBitmapCtrl::GetCtrlBGColor();
Gets the current background color of the control.
bool CLayeredBitmapCtrl::AddLayer( CLayerInfo &layerInfo );
Adds a layer object to the vector.
bool CLayeredBitmapCtrl::RemoveLayer( int nLayerID );
Removes a layer object from the vector, based on the layer ID.
bool CLayeredBitmapCtrl::ReindexLayers();
Allows the layer objects to be re-indexed based on their position in the vector. This is necessary if layers have been removed.
bool CLayeredBitmapCtrl::SwapLayers( int nFirstLayerID, int nSecondLayerID );
Swaps the indices of two layer objects.
bool CLayeredBitmapCtrl::MakeTopLayer( int nLayerID );
Moves the specified layer to the top of all other layers.
bool CLayeredBitmapCtrl::SetLayerVisibility( int nLayerID, bool bVisible );
Indicates if this layer is to be displayed or hidden.
bool CLayeredBitmapCtrl::SetLayerTransparency( int nLayerID, bool bTransparent = false, COLORREF colTransparent = RGB(0, 0, 0) );
Sets the layer transparency flag and color, if necessary. The transparent color is defaulted to black.
bool CLayeredBitmapCtrl::SetLayerTransparency( int nLayerID, bool bTransparent, CPoint &ptTransparentPixel );
Sets the layer transparency flag and location of the transparent pixel within the layer object's bitmap.
bool CLayeredBitmapCtrl::GetLayerTransparency( int nLayerID );
Gets the transparent flag of the specified layer object.
COLORREF CLayeredBitmapCtrl::GetLayerTransparencyColor( int nLayerID );
Gets the transparent color of the specified layer object.
bool CLayeredBitmapCtrl::SetLayerDescription( int nLayerID, CString &strLayerDesc );
Sets the description for the specified layer object.
bool CLayeredBitmapCtrl::GetLayerDescription( int nLayerID, CString &strLayerDesc );
Gets the description of the specified layer object.
bool CLayeredBitmapCtrl::SetLayerTracking( int nLayerID, bool bTrackingEnabled );
Sets the tracking enabled flag of a layer object.
bool CLayeredBitmapCtrl::GetLayerTracking( int nLayerID );
Gets the tracking enabled flag of a layer object.
bool CLayeredBitmapCtrl::SetLayerShowOnTopWhileTracking( int nLayerID, bool bShowOnTopWhileTracking );
Sets the show on top while tracking flag of a layer object.
bool CLayeredBitmapCtrl::GetLayerShowOnTopWhileTracking( int nLayerID );
Gets the show on top while tracking flag of a layer object.
bool CLayeredBitmapCtrl::SetLayerLocation( int nLayerID, const CPoint &ptNewLocation );
Sets the upper-left location point of a layer object.
bool CLayeredBitmapCtrl::GetLayerLocation( int nLayerID, CPoint &ptLocation );
Gets the upper-left location point of a layer object.
bool CLayeredBitmapCtrl::GetLayerSize( int nLayerID, CSize &size );
Gets the width and height of a layer object.
bool CLayeredBitmapCtrl::SetLayerEnableFocusRectangle( int nLayerID, bool bEnabled );
Sets the focus rectangle enabled flag of a layer object.
bool CLayeredBitmapCtrl::GetLayerEnableFocusRectangle( int nLayerID );
Gets the focus rectangle enabled flag of a layer object.
bool CLayeredBitmapCtrl::SetLayerFocusRectangleColor( int nLayerID, COLORREF colFocusRectangle );
Sets the focus rectangle color of a layer object.
COLORREF CLayeredBitmapCtrl::GetLayerFocusRectangleColor( int nLayerID );
Gets the focus rectangle color of a layer object.
int CLayeredBitmapCtrl::GetLayerIndex( int nLayerID );
Gets the layer object's index.
bool CLayeredBitmapCtrl::CalculateCenterOffset( CLayerInfo *pLayerInfo );
bool CLayeredBitmapCtrl::CalculateCenterOffset( int nLayerID );
Calculates the center offset for a layer based on the control's client rect. The second implementation can only be used if the layer object already exists in the vector.
bool CLayeredBitmapCtrl::CreateNonTransparentRgn( CLayerInfo *pLayerInfo );
bool CLayeredBitmapCtrl::CreateNonTransparentRgn( int nLayerID );
Creates a region composed of the non-transparent pixels of the layer's bitmap. The second implementation can only be used if the layer object already exists in the vector.
bool CLayeredBitmapCtrl::DoLayersIntersect( CLayerInfo *pLayerInfo1, CLayerInfo *pLayerInfo2, CRect *pRectIntersect = NULL );
bool CLayeredBitmapCtrl::DoLayersIntersect( int nLayerID1, int nLayerID2, CRect *pRectIntersect = NULL );
Determines if the bounding rectangle for two layers intersect. The second implementation can only be used if the layer object already exists in the vector.
bool CLayeredBitmapCtrl::SortLayers();
Sorts the layer objects based on the layer index. Note: there are no checks to make sure that there is only one layer object per index.
bool CLayeredBitmapCtrl::CombineLayers();
Combines all of the visible layers into one bitmap that will be displayed within the OnPaint
function.
bool CLayeredBitmapCtrl::DrawLayerOnBitmap( CBitmap *pBmpBackground, CLayerInfo *pLayerInfo );
Draws the specified layer onto a bitmap.
bool CLayeredBitmapCtrl::ShowVisibleLayers();
This function makes sure that the layers are sorted and combined before drawing them onto the screen.
CLayerInfo *CLayeredBitmapCtrl::CreateFocusLayer( CDC *pDC, int nX, int nY, int nWidth, int nHeight, COLORREF colFocus );
Creates a temporary focus rectangle layer at the specified location with the specified color.
BOOL CLayeredBitmapCtrl::Create( DWORD dwExStyle, const RECT &rect, CWnd *pParentWnd, UINT nID );
Allows the control to be created dynamically.
Using the code
To use this control in your application, add the LayeredBitmapCtrl.h and LayeredBitmapCtrl.cpp files to your project. Use the Resource Editor to add a Picture Control to your dialog. Edit the properties of the Picture Control and rename the control from IDC_STATIC
to some other name like IDC_LAYERED_DISPLAY
. Next, add the Notify style to the control. This allows the tool tip to work properly.
There are two ways to add a variable for the control.
- In order to see the
CLayeredBitmapCtrl
listed as an option in the ClassWizard, you must first delete the .clw file. Once you have deleted the file, open the ClassWizard. You will see a dialog box asking if you want to recreate the ClassWizard database from your source files. Choose Yes and make sure that the LayeredBitmapCtrl.h and LayeredBitmapCtrl.cpp are in the project directory. Now, choose your dialog and select Member Variables. Select the IDC_LAYERED_DISPLAY
control (or the name that you gave it) and add a variable. Type the variable name and select Control as the Category. You should see the CLayeredBitmapCtrl
listed under Variable type.
- Open the ClassWizard, choose your dialog, and select Member Variables. Select the
IDC_LAYERED_DISPLAY
control (or the name that you gave it) and add a variable. Type the variable name and select Control as the Category. Choose CStatic
for the Variable type. Once you are finished with the ClassWizard, open your dialog's header file. Change the control type from CStatic
to CLayeredBitmapCtrl
for your variable.
Don't forget to add the following to your dialog's header file:
#include "LayeredBitmapCtrl.h"
Make sure that you have a unique layer ID for each layer. For the demo project, I created an enumerated list so that I knew each ID would be unique. I setup the layers within a function that I call at the end of the InitDialog
function. However, the layers can be created at anytime. Also, within InitDialog
, you can specify the control's background color. In the demo, I use the CLayeredBitmapCtrl::UseSysColor
function so that the control's background color will reflect changes to the system color.
The CLayeredBitmapCtrl
class uses another class called CLayerInfo
. This class contains all of the information and data objects for each layer, including a CBitmap
and CRgn
.
How to setup layers
This is the method I used in the demo to setup the various layers. For the first layer, I don't specify a transparent color or create a region. The location of the bitmap is defaulted to the upper left-hand corner of the control. Since this layer is the same size as the control, there is no need to change its location.
CLayerInfo *pLayerInfo = NULL;
pLayerInfo = new CLayerInfo();
pLayerInfo->m_nLayerID = VALLEY_OF_FIRE_LAYER;
pLayerInfo->m_strLayerDesc = _T("Valley of Fire");
pLayerInfo->m_bmp.LoadBitmap( IDB_VALLEY_OF_FIRE_BITMAP );
m_layeredDisplay.AddLayer( *pLayerInfo );
delete pLayerInfo;
Here is an example of a transparent layer with a region:
pLayerInfo = new CLayerInfo();
pLayerInfo->m_nLayerID = CHAMELEON_BOB_LAYER;
pLayerInfo->m_strLayerDesc = _T("Chameleon Bob");
pLayerInfo->m_bTransparent = true;
pLayerInfo->m_ptTransparentPixel.x = 1;
pLayerInfo->m_ptTransparentPixel.y = 1;
pLayerInfo->m_ptLocation.x = 200;
pLayerInfo->m_ptLocation.y = 210;
pLayerInfo->m_bTrackingEnabled = true;
pLayerInfo->m_bFocusRectangleEnabled = true;
pLayerInfo->m_colFocusRectangle = RGB( 0, 255, 0 );
pLayerInfo->m_bmp.LoadBitmap( IDB_CHAMELEON_BOB_BITMAP );
m_layeredDisplay.CreateNonTransparentRgn( pLayerInfo );
pLayerInfo->m_rgn.OffsetRgn( pLayerInfo->m_ptLocation.x,
pLayerInfo->m_ptLocation.y );
m_layeredDisplay.AddLayer( *pLayerInfo );
delete pLayerInfo;
Here is an example of a transparent bitmap. I use magenta RGB(255,0,255)
for the transparent color, because it's not very likely that I'll have that color in my background image.
For example
Chameleon Bob
If you want to have a layer displayed, add the following code before you call the CLayeredBitmapCtrl::AddLayer
function:
pLayerInfo->m_bVisible = true;
Or, you can specify the visibility after the layer has been added, by calling the CLayeredBitmapCtrl::SetLayerVisibility
function.
m_layeredDisplay.SetLayerVisibility( CHAMELEON_BOB_LAYER, true );
Once you have setup the layers, call the CLayeredBitmapCtrl::ShowVisibleLayers
function to display the combined bitmap layers on the control.
m_layeredDisplay.ShowVisibleLayers();
Points of Interest
I had looked at various examples of transparent bitmaps and read a section from the Windows 2000 Graphics API Black Book, by Damon Chandler, Michael Fotsch, and eventually, I came up with a solution to my problem. It took a lot of tweaking to get it right, due to the various sizes of the bitmaps. To allow for the focus rectangle and the ability to move layers, I came up with the idea of being able to draw a layer on any bitmap instead of just the combined bitmap. Here is the code used to combine the visible layers onto one bitmap, as well as the code for drawing a layer on to any bitmap:
bool CLayeredBitmapCtrl::CombineLayers()
{
CDC *pDC = NULL;
CRect rectCtrl;
CSize sizeCtrl, sizeLayer;
int nIndex = 0;
CLayerInfo *pCurrentLayer = NULL;
pDC = GetDC();
GetClientRect( &rectCtrl );
sizeCtrl = CSize::CSize( rectCtrl.Width(), rectCtrl.Height() );
m_bmpCombined.DeleteObject();
m_bmpCombined.CreateCompatibleBitmap( pDC, sizeCtrl.cx, sizeCtrl.cy );
m_bmpCombined.SetBitmapDimension( sizeCtrl.cx, sizeCtrl.cy );
CDC tmpDC;
CBitmap *pOldBmp = NULL;
tmpDC.CreateCompatibleDC( pDC );
pOldBmp = tmpDC.SelectObject( &m_bmpCombined );
tmpDC.FillSolidRect( 0, 0, rectCtrl.Width(),
rectCtrl.Height(), m_colCtrlBG );
tmpDC.SelectObject( pOldBmp );
if ( !m_vecLayerInfo.empty() )
{
for ( nIndex = 0; nIndex < m_vecLayerInfo.size(); nIndex++ )
{
pCurrentLayer =
reinterpret_cast<CLayerInfo *>(&m_vecLayerInfo[nIndex]);
if ( pCurrentLayer->m_bVisible )
{
DrawLayerOnBitmap( &m_bmpCombined, pCurrentLayer );
}
}
}
ReleaseDC( pDC );
return true;
}
bool CLayeredBitmapCtrl::DrawLayerOnBitmap( CBitmap *pBmpBackground,
CLayerInfo *pLayerInfo )
{
CDC *pDC = NULL;
CDC srcDC, destDC, maskDC, compositeDC, overlayDC;
CRect rectCtrl;
CSize sizeCtrl, sizeLayer;
CBitmap *pOldDestBmp = NULL, *pOldSrcBmp = NULL;
CBitmap bmpMask, *pOldMaskBmp = NULL;
CBitmap bmpComposite, *pOldCompositeBmp = NULL;
CBitmap bmpOverlay, *pOldOverlayBmp = NULL;
COLORREF colOld;
CPalette *pPalette = NULL;
if ( (NULL == pBmpBackground) || (NULL == pLayerInfo) )
{
return false;
}
if ( NULL == pLayerInfo->m_bmp.GetSafeHandle() )
{
return false;
}
pDC = GetDC();
srcDC.CreateCompatibleDC( NULL );
destDC.CreateCompatibleDC( NULL );
maskDC.CreateCompatibleDC( NULL );
compositeDC.CreateCompatibleDC( NULL );
overlayDC.CreateCompatibleDC( NULL );
GetClientRect( &rectCtrl );
sizeCtrl = CSize::CSize( rectCtrl.Width(), rectCtrl.Height() );
pOldDestBmp = destDC.SelectObject( pBmpBackground );
pOldSrcBmp = srcDC.SelectObject( &(pLayerInfo->m_bmp) );
sizeLayer = pLayerInfo->m_bmp.GetBitmapDimension();
if ( pLayerInfo->m_bTransparent )
{
if ( -1 != pLayerInfo->m_ptTransparentPixel.x )
{
pLayerInfo->m_colTransparent =
srcDC.GetPixel( pLayerInfo->m_ptTransparentPixel );
}
bmpMask.CreateBitmap( sizeCtrl.cx, sizeCtrl.cy, 1, 1, NULL );
bmpOverlay.CreateCompatibleBitmap( &destDC,
sizeCtrl.cx, sizeCtrl.cy );
bmpComposite.CreateCompatibleBitmap( &destDC,
sizeCtrl.cx, sizeCtrl.cy );
pOldMaskBmp = maskDC.SelectObject( &bmpMask );
pOldOverlayBmp = overlayDC.SelectObject( &bmpOverlay );
pOldCompositeBmp = compositeDC.SelectObject( &bmpComposite );
colOld = srcDC.SetBkColor( pLayerInfo->m_colTransparent );
maskDC.SetStretchBltMode( COLORONCOLOR );
maskDC.StretchBlt( pLayerInfo->m_ptLocation.x,
pLayerInfo->m_ptLocation.y,
sizeLayer.cx, sizeLayer.cy, &srcDC, 0, 0,
sizeLayer.cx, sizeLayer.cy, NOTSRCCOPY );
maskDC.SetBkColor( colOld );
overlayDC.BitBlt( 0, 0, sizeCtrl.cx,
sizeCtrl.cy, &maskDC, 0, 0, NOTSRCCOPY );
overlayDC.BitBlt( 0, 0, sizeCtrl.cx,
sizeCtrl.cy, &destDC, 0, 0, SRCAND );
compositeDC.BitBlt( 0, 0, sizeCtrl.cx,
sizeCtrl.cy, &maskDC, 0, 0, SRCCOPY );
maskDC.SelectObject( pOldMaskBmp );
bmpMask.DeleteObject();
pPalette = destDC.GetCurrentPalette();
if ( pPalette )
{
pPalette = compositeDC.SelectPalette( pPalette, FALSE );
compositeDC.RealizePalette();
}
compositeDC.SetStretchBltMode( COLORONCOLOR );
compositeDC.StretchBlt( pLayerInfo->m_ptLocation.x,
pLayerInfo->m_ptLocation.y,
sizeLayer.cx, sizeLayer.cy, &srcDC, 0, 0,
sizeLayer.cx, sizeLayer.cy, SRCAND );
compositeDC.BitBlt( 0, 0, sizeCtrl.cx,
sizeCtrl.cy, &overlayDC, 0, 0, SRCPAINT );
overlayDC.SelectObject( pOldOverlayBmp );
bmpOverlay.DeleteObject();
destDC.BitBlt( 0, 0, sizeCtrl.cx,
sizeCtrl.cy, &compositeDC, 0, 0, SRCCOPY );
compositeDC.SelectPalette( pPalette, FALSE );
compositeDC.SelectObject( pOldCompositeBmp );
bmpComposite.DeleteObject();
}
else
{
destDC.BitBlt( pLayerInfo->m_ptLocation.x,
pLayerInfo->m_ptLocation.y,
sizeCtrl.cx, sizeCtrl.cy, &srcDC, 0, 0, SRCCOPY );
}
srcDC.SelectObject( pOldSrcBmp );
destDC.SelectObject( pOldDestBmp );
ReleaseDC( pDC );
return true;
}
Here is the OnPaint
function. In this function, a copy of the combined bitmap is made so that we can draw the focus rectangle if necessary. Also, depending on the m_bShowOnTopWhileTracking
flag, the top layers will be redrawn above the tracked layer. If the flag is set, then the code determines if the rectangle surrounding the tracked layer intersects with the current layer. If the two rectangles intersect, then only the portion of the current layer that intersects with the tracked layer is redrawn. It is still a slow process, but a nice effect. That is why I added the flag, so that the tracked layer can be displayed on top of all of the other layers without having to redraw them. Note, that the tracked layer's index is not modified. So once the mouse button is released, the tracked layer will be drawn under the visible top layers again.
void CLayeredBitmapCtrl::OnPaint()
{
CPaintDC dc( this );
CDC displayDC;
CRect rectCtrl;
CBitmap *pOldBmp = NULL;
CBitmap bmpTemp;
bool bShowTopLayers = false;
int nIndex = 0;
CLayerInfo *pCurrentLayer = NULL;
CLayerInfo *pTrackedLayer = NULL;
CLayerInfo *pTmpLayer = NULL;
CRect rectIntersect;
CLayerInfo::CopyBitmap( m_bmpCombined, bmpTemp );
GetClientRect( &rectCtrl );
displayDC.CreateCompatibleDC( &dc );
for ( nIndex = 0; nIndex < m_vecLayerInfo.size(); nIndex++ )
{
pCurrentLayer =
reinterpret_cast<CLayerInfo *>(&m_vecLayerInfo[nIndex]);
if ( bShowTopLayers )
{
if ( pCurrentLayer->m_bVisible )
{
rectIntersect.SetRectEmpty();
if ( NULL != pTrackedLayer )
{
if ( DoLayersIntersect( pTrackedLayer,
pCurrentLayer, &rectIntersect ) )
{
pTmpLayer = new CLayerInfo( *pCurrentLayer );
pTmpLayer->m_ptTransparentPixel.x = -1;
pTmpLayer->m_ptTransparentPixel.y = -1;
pTmpLayer->m_ptLocation.x = rectIntersect.left;
pTmpLayer->m_ptLocation.y = rectIntersect.top;
int nTmpWidth = rectIntersect.Width();
int nTmpHeight = rectIntersect.Height();
CSize sizeTracked = pTrackedLayer->m_bmp.GetBitmapDimension();
CSize sizeCurrent = pCurrentLayer->m_bmp.GetBitmapDimension();
if ( pTrackedLayer->m_ptLocation.x
>= pCurrentLayer->m_ptLocation.x )
{
if ( sizeTracked.cx < sizeCurrent.cx )
{
if ( sizeCurrent.cx > nTmpWidth )
{
rectIntersect.left = rectIntersect.left -
pCurrentLayer->m_ptLocation.x;
}
else
{
rectIntersect.left = sizeCurrent.cx - nTmpWidth;
}
}
else
{
rectIntersect.left = sizeCurrent.cx - nTmpWidth;
}
}
else
{
rectIntersect.left = 0;
}
rectIntersect.right = rectIntersect.left + nTmpWidth;
if ( pTrackedLayer->m_ptLocation.y
>= pCurrentLayer->m_ptLocation.y )
{
if ( sizeTracked.cy < sizeCurrent.cy )
{
if ( sizeCurrent.cy > nTmpHeight )
{
rectIntersect.top = rectIntersect.top -
pCurrentLayer->m_ptLocation.y;
}
else
{
rectIntersect.top = sizeCurrent.cy - nTmpHeight;
}
}
else
{
rectIntersect.top = sizeCurrent.cy - nTmpHeight;
}
}
else
{
rectIntersect.top = 0;
}
rectIntersect.bottom = rectIntersect.top + nTmpHeight;
CLayerInfo::CopyBitmapSection( pCurrentLayer->m_bmp,
pTmpLayer->m_bmp, rectIntersect, &dc );
DrawLayerOnBitmap( &bmpTemp, pTmpLayer );
delete pTmpLayer;
}
}
}
}
if ( true == pCurrentLayer->m_bTracking )
{
DrawLayerOnBitmap( &bmpTemp, pCurrentLayer );
pTrackedLayer = pCurrentLayer;
if ( false == pCurrentLayer->m_bShowOnTopWhileTracking )
{
bShowTopLayers = true;
}
}
if ( true == pCurrentLayer->m_bShowFocusRectangle )
{
CLayerInfo *pLayerInfo = NULL;
CSize sizeBitmap;
sizeBitmap = pCurrentLayer->m_bmp.GetBitmapDimension();
pLayerInfo = CreateFocusLayer( &dc, pCurrentLayer->m_ptLocation.x,
pCurrentLayer->m_ptLocation.y,
sizeBitmap.cx, sizeBitmap.cy,
pCurrentLayer->m_colFocusRectangle );
if ( NULL != pLayerInfo )
{
DrawLayerOnBitmap( &bmpTemp, pLayerInfo );
delete pLayerInfo;
}
}
}
pOldBmp = displayDC.SelectObject( &bmpTemp );
dc.BitBlt( rectCtrl.left, rectCtrl.top, rectCtrl.Width(), rectCtrl.Height(),
&displayDC, 0, 0, SRCCOPY );
displayDC.SelectObject( pOldBmp );
}
Acknowledgements
- Ivaylo Byalkov - For his
PrepareRGBBitmapInfo
function from his Accelerated Smooth Bitmap Resizing article, which made it easier for copying bitmaps.
- David Forrester - I used a modified version of his
MakeRegion
function, from his Irregular Shaped Bitmap Dialog article, to make a non-transparent region for each of the bitmap layers (if desired).
- X.Q.Wang - For his suggestion for a focus rectangle around the selected layer.
- Shilps - For finding a compiler error in the
FindLayer
function when compiled with VC++ 7.0 .NET.
- Michel Wassink - For his suggestion to allow the user to change the background color of the control and also allow the control to reflect system color changes.
History
- 1.0 - 27 Sep 2003
- 2.0 - 17 Jun 2004
- Added the ability to move layers with the mouse.
- Improved drawing code so that layers can be drawn on to any bitmap.
- Changed the demo application to show some of the new features.
- Added a focus rectangle to aid in the selection of a layer. This method can be turned on or off at the layer level.
- 2.01 - 8 Dec 2004
- Added the ability to change the control's background color, and reflect system color changes if the system color is used.
- Hopefully, this time I corrected all of the compiler errors with VC++ 7.0 .NET.