Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Add zooming to WTL CScrollImpl

0.00/5 (No votes)
2 Nov 2004 1  
CZoomScrollImpl extends WTL CScrollImpl to allow continuous zooming.

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 // remove this line when 

       // WTL supports CSize scalar operators 

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 ) // in place coord transformation
  • void WndtoTrue( LPRECT prectW ) // in place coord transformation
  • void TruetoWnd( LPPOINT aptT, nPts ) // in place coord transformation
  • void TruetoWnd( LPRECT prectT ) // in place coord transformation

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 //    _WTL_VER <= 0x0710

    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); // Zoomed and scrolled bitmap is shown 

    // Your unzoomed drawing routine here : use the TruetoWnd() 

    // helpers if necessary

}

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 // ZOOM when they are common, // ZOOMPPC when they are PPC specific. Some BmpViewPPC bug fixes are tagged with // BMPVIEWPPC.

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 > // ZOOM


{
    //...

    void SetBitmap(HBITMAP hBitmap)
    {
        //...

        SetScrollOffset(0, 0, FALSE);
        SetZScrollSize( m_size); // ZOOM

    }
    
    BEGIN_MSG_MAP(CBitmapView)
        CHAIN_MSG_MAP(CZoomScrollImpl<CBitmapView>); // ZOOM

    END_MSG_MAP()
 
    void DoPaint( CDCHandle dc)
    {
        if( !m_bmp.IsNull())
        DrawBitmap( m_bmp, dc); // ZOOM 

    }
}

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; // ZOOM

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 /*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); // see explanation

    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 // _WTL_VER <= 0x0710

    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 // ZOOMPPC

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 // WIN32_PLATFORM_PSPC // ZOOMPPC

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 // _WIN32_WCE

{
public:
    DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)

#ifndef _WIN32_WCE
    CCommandBarCtrl m_CmdBar;
    CRecentDocumentList m_mru;
    CMruList m_list;
#else
    CCtrlMenuBar m_CmdBar; // ZOOMPPC

#endif // _WIN32_WCE
//...

int OnCreate(LPCREATESTRUCT /*lpCreateStruct*/) 
{
    //... 

#else // WIN32_PLATFORM_PSPC

    CreateSimpleCEMenuBar(IDR_MAINFRAME, SHCMBF_HMENU);
    m_CmdBar.SubclassWindow(m_hWndCECommandBar); // ZOOMPPC

#endif // WIN32_PLATFORM_PSPC

#endif // _WIN32_WCE

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 //_WIN32_WCE

    m_ZoomCtrl.EnableWindow(FALSE);

// ZOOM end

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 /*uNotifyCode*/, int /*nID*/, CWindow /*wnd*/)
{
    //...

    m_view.SetBitmap(NULL);
    m_ZoomCtrl.SetPos(0); // ZOOM

    m_ZoomCtrl.EnableWindow(FALSE); // ZOOM

    //...

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) // ZOOM

We SetZoom to a hundredth of the Trackbar thumb position.

void OnZoomCtrl( int /*iType*/, short /*iTrackPos*/, HWND /*hWndTrackBar*/ )
{
    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 /*uMsg*/, WPARAM wParam, LPARAM /*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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here