Introduction
Zoom may be helpful to view large image files on small screens.
This implementation was designed to execute both on PPC devices, where only a subset of GDI is available and on desktop computers with Win32 systems.
The preliminary versions relied on a private WTL adaptation for PPC. When WTL 7.1 was released, including Pocket PC support, it was time to make it an add-on.
zoomscrl.h and the sample files will compile and execute cleanly with WTL 7.1 in Visual Studio .NET, EVC 3.0 and 4.0.
WTL 7.5 soon coming
WTL is on a fast track now. WTL 7.5 is a work in progress at SourceForge.net, while I am writing the current working version is WTL 7.5.4291.0.
This version still lacks CSize
scalar operators and does not address all Pocket PC issues. So if you compile with this version you will get some strange PPC behaviour ...
zoomscrl.h will compile and execute cleanly with WTL 7.5.4291.0 in Visual Studio .NET, EVC 3.0 and 4.0.
BmpZoom project will compile and execute cleanly with WTL 7.5.4291.0 in Visual Studio .NET.
BmpZoomPPC project will compile cleanly and execute strangely with WTL 7.5.4291.0 in EVC 3.0 and 4.0.
When WTL 7.5 implements CSize
scalar operators, the compiler will dislike zoomscrl.h external definitions of the same. Just comment or delete zoomscrl.h line #39
#define _WTL_NO_SIZE_SCALAR
CSize scalar operators
When using scales, the CSize
class is the normal vehicle, but it lacks multiply and divide operators. These are implemented here at external level, hopefully they should be part of the CSize
class in the final WTL 7.5 release.
The templated writing allows minimal and adequate compilation.
template < class Num > inline CSize operator * ( tagSIZE s, Num n)
template < class Num > inline void operator *= ( tagSIZE & s, Num n)
template < class Num > inline CSize operator / ( tagSIZE s, Num n)
template < class Num > inline void operator /= ( tagSIZE & s, Num n)
These operators allow simple and easy to maintain writings such as:
CSize halfSize = mySize / 2;
Class CZoomScrollImpl<T> members
The class inherits from CScrollImpl<T>
which performs the scrolling part. We add a scaling layer and override the CScrollImpl
methods to provide them with scaled sizes and positions.
Data members
CSize m_sizeTrue
holds the unzoomed image size, CScrollImpl::m_sizeAll
holds the zoomed size. double m_fzoom
holds the zoom factor, accessible by double GetZoom()
.
Data access complements
The class provides some extra data access members allowing writings such as:
CSize sizePage = GetScrollPage();
These members are:
CSize GetScrollSize()
CSize GetScrollPage()
CSize GetScrollLine()
CPoint GetScrollOffset()
Zoom operations and access methods
SetZScrollSize
is called by the overridden SetScrollSize()
and in turn calls the base class CScrollImpl<T>::SetScrollSize()
after computing the zoomed size. This member comes in two blends:
void SetZScrollSize( CSize sizeTrue, double fzoom = 1., BOOL bRedraw = TRUE )
void SetZScrollSize( int cx, int cy, double fzoom=1., BOOL bRedraw = TRUE )
SetZoom
changes the zoom factor, scales the page and line scroll parameters, keeps the initial center point with the new zoom factor, and redraws the image if bRedraw
is TRUE
:
void SetZoom( double fzoom, BOOL bRedraw = TRUE )
double GetZoom()
returns the current zoom factor.
Helper coordinate methods
WndtoTrue
and TruetoWnd
methods compute the respective window and image coordinates for a single point, a rectangle, and an array of points.
CPoint WndtoTrue( CPoint ptW )
CPoint TruetoWnd( CPoint ptT )
void WndtoTrue( LPPOINT aptW, int nPts )
void WndtoTrue( LPRECT prectW )
void TruetoWnd( LPPOINT aptT, nPts )
void TruetoWnd( LPRECT prectT )
Drawing operations methods
These members assume adequate setting of data members, at least one preliminary call to SetScrollSize()
, and draw a bitmap or a memory DC with the current zoom and scroll settings.
void DrawBitmap( HBITMAP hbm, HDC hdestDC, DWORD dwROP = SRCCOPY)
void DrawDC( HDC hsourceDC, HDC hdestDC, DWORD dwROP = SRCCOPY)
Message map and handlers
BEGIN_MSG_MAP(CZoomScrollImpl<T>)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
#if _WTL_VER <= 0x0710
MESSAGE_HANDLER(WM_SIZE, OnSize)
#endif
CHAIN_MSG_MAP(CScrollImpl<T>)
END_MSG_MAP()
The WTL 7.1 CScrollImpl::OnSize
handler has sometimes problems with scrollbars appearance/disappearance, fixed in current WTL 7.5.
We erase the background without flickering if the SCRL_ERASEBACKGROUND
extended style is set which is the default in CScrollImpl
. Otherwise, we do nothing and set bHandled
to FALSE
.
Using the class
Add zooming capability
From an existing CScrollImpl
derived class window, change the inheritance to CZoomScrollImpl
, and change the OnDraw
member making use of the drawing methods.
Remove, if existing, the WM_ERASEBKGND
handler from your message map and chain to CZoomScrollImpl<CMyView>
.
Check that nothing noticeable happens as far as now... You are actually zoom enabled with a zoom factor equal to 1.0 and no way to change it.
Now implement some user interface to call SetZoom()
. That's the difficult part.
Drawing issues
The class zooms and scrolls on a raster image.
Vector drawing can be performed to a full size DIB or memory DC, the resulting raster image will then be zoomed and scrolled.
If you want to draw fixed size symbols or labels to your zoomed image, you may draw directly to the zoomed background in your DoPaint
member with something like:
void DoPaint( CDCHandle dc)
{
DrawBitmap( m_bmp, dc);
}
From BmpView to BmpZoom
BmpView is a very rich sample coming with the WTL distribution. In WTL 7.1, a Pocket PC blend BmpViewPPC is provided.
BmpZoom and BmpZoomPPC add zooming capability to the initial sample without any other change.
Changes are tagged with
when they are common,
when they are PPC specific. Some BmpViewPPC bug fixes are tagged with
.
CBitmapView zoom enabling
CBitmapView
is dramatically simplified. We change the inheritance, suppress the WM_ERASEBKGND
handler, and change the DoPaint
member to two lines. We call SetZScrollSize
in SetBitmap
to reset the zoom factor to 1.0 on loading of a new bitmap.
class CBitmapView : public CWindowImpl<CBitmapView>,
public CZoomScrollImpl< CBitmapView >
{
void SetBitmap(HBITMAP hBitmap)
{
SetScrollOffset(0, 0, FALSE);
SetZScrollSize( m_size);
}
BEGIN_MSG_MAP(CBitmapView)
CHAIN_MSG_MAP(CZoomScrollImpl<CBitmapView>);
END_MSG_MAP()
void DoPaint( CDCHandle dc)
{
if( !m_bmp.IsNull())
DrawBitmap( m_bmp, dc);
}
}
Implementing a trackbar
This is the rough part. We install a Trackbar common control in the toolbar so that user can set zoom as needed.
Changes are in CMainFrame
and are partly different for desktop and PPC.
First, we add a CTrackbarCtrl
member to CMainFrame
:
CBitmapView m_view;
CTrackBarCtrl m_ZoomCtrl;
At the end of OnCreate
handler, we create the disabled trackbar and position it properly.
Desktop Trackbar creation
We create the control as a child of the frame, with adequate size.
We have to SetLineSize(0)
because AddSimpleReBarBand()
will send TB_BUTTONCOUNT
to our control which will understand this as TBM_GETLINESIZE
(both are defined as WM_USER+24
). The Trackbar control will return 0 and the Rebar band will be correctly installed.
We set the Trackbar's band size to what the Toolbar left us and reset the Trackbar's line size to 1.
int OnCreate(LPCREATESTRUCT )
{
#ifndef _WIN32_WCE
CRect rCtrl(0,0,120,22);
m_ZoomCtrl.Create( m_hWnd,rCtrl,NULL ,WS_CHILD | WS_VISIBLE |
TBS_AUTOTICKS | TBS_ENABLESELRANGE );
m_ZoomCtrl.SetLineSize(0);
AddSimpleReBarBand( m_ZoomCtrl, _T("Zoom"), FALSE, 140, FALSE);
#if _WTL_VER <= 0x0710
::SendMessage( m_hWndToolBar, RB_MAXIMIZEBAND, 1, TRUE);
#else
CReBarCtrl rebar = m_hWndToolBar;
rebar.MaximizeBand(1,TRUE);
#endif
m_ZoomCtrl.SetLineSize(1);
#else
PPC trackbar creation
For PPC, we need to subclass the Menubar - in which we have created a menu entry 'Zoom Control' with ID ID_ZOOM
in the resource editor.
#ifdef WIN32_PLATFORM_PSPC
class CCtrlMenuBar : public CWindowImpl< CCtrlMenuBar,
CCECommandBarCtrlT<CToolBarCtrl> >
{
public:
DECLARE_WND_SUPERCLASS( _T("CtrlMenuBar"), _T("ToolbarWindow32") );
BEGIN_MSG_MAP(CCtrlMenuBar)
if ( uMsg != WM_NOTIFY )
FORWARD_NOTIFICATIONS()
END_MSG_MAP()
};
#endif
The subclassed Menubar will forward to our frame the WM_HSCROLL
messages.
We define a CCtrlMenuBar
member and subclass the menubar after its creation.
class CMainFrame : public CFrameWindowImpl<CMainFrame>,
public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
#ifndef _WIN32_WCE
, public CPrintJobInfo
#endif
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
#ifndef _WIN32_WCE
CCommandBarCtrl m_CmdBar;
CRecentDocumentList m_mru;
CMruList m_list;
#else
CCtrlMenuBar m_CmdBar;
#endif
int OnCreate(LPCREATESTRUCT )
{
#else
CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);
m_CmdBar.SubclassWindow(m_hWndCECommandBar);
#endif
#endif
At the end of OnCreate
, we create the control as a child of the Menubar with the dimensions and position of our ID_ZOOM
button.
#else
CRect rZoom;
m_CmdBar.GetRect(ID_ZOOM,rZoom);
m_ZoomCtrl.Create( m_hWndCECommandBar,rZoom, NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | TBS_TOP , 0, ID_ZOOM );
m_ZoomCtrl.SetWindowPos(HWND_TOP,rZoom.left,rZoom.top + 1,
rZoom.Width(),rZoom.Height() - 1,SWP_SHOWWINDOW);
#endif
m_ZoomCtrl.EnableWindow(FALSE);
We disable the trackbar at creation: at this moment we have nothing to zoom.
Trackbar preparation
When a new image is opened, we set the trackbar range from 100 to hundred times the zoom factor allowing the full image to fit in the current window size. So, we can get the desired zoom factor by dividing the trackbar position - which is an integer - by 100.0 (if position is 218 zoom should be 2.18). When we have something to zoom, we enable the trackbar.
This code is duplicated in the OnFileRecent
and OnFileOpen
handlers.
CSize sImg=m_view.GetScrollSize();
CSize sClient=m_view.m_sizeClient;
m_ZoomCtrl.SetRange(100, max( (100 * sImg.cx) /sClient.cx ,
(100 * sImg.cy) /sClient.cy));
m_ZoomCtrl.SetPos(100);
m_ZoomCtrl.EnableWindow();
When the image is deleted, we disable the trackbar and set its position to left.
void OnEditClear(UINT , int , CWindow )
{
m_view.SetBitmap(NULL);
m_ZoomCtrl.SetPos(0);
m_ZoomCtrl.EnableWindow(FALSE);
Trackbar zooming
When our frame gets the WM_HSCROLL
message, it may only come from the Trackbar. We add this line to the frame message map:
MSG_WM_HSCROLL(OnZoomCtrl)
We SetZoom
to a hundredth of the Trackbar thumb position.
void OnZoomCtrl( int , short , HWND )
{
if ( !m_view.m_bmp.IsNull())
m_view.SetZoom( m_ZoomCtrl.GetPos() / 100.0 );
}
This should be the end of our sample description, but there is still some work to do on BmpZoomPPC which behaves strangely ...
WTL 7.1 PPC weaknesses
We can compile and even run BmpZoomPPC but:
- Menu items are always active: fixed in WTL 7.5.4291.0.
- The frame is not properly positioned.
- The property sheet asserts wildly and is not PPC like.
These youth weaknesses should be addressed in WTL 7.5 final release, part of them are already fixed in WTL 7.5.4291.0.
In the meantime, we workaround with some local CMainFrame
members.
LRESULT OnInitMenuPopup(UINT , WPARAM wParam, LPARAM , BOOL& bHandled)
void UpdateLayout(BOOL bResizeBars = TRUE)
We also modify classes CBmpProperties
and CPageTwo
in props.h to have the property sheet and pages PPC compliant.
Still we are far from a well behaved PPC application. I hope to post in the CodeProject PPC section something like a well behaved WTL image viewer. See you there?
History
- Updated 10-31-2004 for WTL 7.5.4291.0 issues