Scroll and Zoom

This example demonstrates how to change the size and how to scroll the live video image.

The example is located in the %TOPLEVEL%\Samples\VC\scrollandzoom directory of the IC Imaging Control installation. It is derived from the classlib Demoapp example and implements the new features of scroll and zoom.

Changing the size of the displayed live video image lets the image zoom in and out. This means that the video image is stretched bigger or smaller.

Scrolling means moving the live video image in a parent window.

image

Zoom

Zooming of the live video image, further named "video window", is achieved by changing the size of the live video image. The Grabber implements methods to retrieve and set the video window's size.

getWindowPosition( long&x0, long&y0, long&width, long&height ) retrieves the current position and size of the video window. The default position is at (0/0) in the parent window and the default size is set to the size of the current video format.

Changing the video window's size by calling the method setWindowSize(long width, long height) stretches the video images to the new size. This results in digital zooming.

In the sample, a combo box is placed in the toolbar. The combo box contains zoom values from 10% to 300%. These values are not limits but they are good enough for this example. The OnSelchangeZoom of CMainFrame is the handler of the combo box. The new zoom factor is set in this handler.

void CMainFrame::OnSelchangeZoom()
{
    float fZoomFactor = 1.0f;
    long lWidth;        // New width of the video window
    long lHeight;       // New Height of the video window
    if( m_Grabber.isDevValid() && m_Grabber.isLive()  )
    {
        fZoomFactor = m_cZoomCombo.GetCurSel() + 1;  // It is a zero based value,
                                                     // so 1 has to be added;
        fZoomFactor /= 10.0f;                        // calculate to percent.
        // Get the current video format size
        lWidth = m_Grabber.getAcqSizeMaxX();
        lHeight = m_Grabber.getAcqSizeMaxY();
        // Calculate the zoom factor
        lWidth  = (long)((float)lWidth  * fZoomFactor);
        lHeight = (long)((float)lHeight * fZoomFactor);
        // Turn off the default window position, so the grabber can change
        // the video window size.
        m_Grabber.setDefaultWindowPosition(false);
        // Set the new window size
        m_Grabber.setWindowSize(lWidth, lHeight);
    }
}

(This code can be found in the file samples/vc71/scrollandzoom/mainframe.cpp.)

Important: Before setting the video window size, the method setDefaultWindowPosition() must be called once with the parameter false. If this is not performed, the video window size always is set to the default values. Then, resizing the video window is not possible.

Adding Scrolling Capabilities

The window that contains the video window has to implement the scrollbars and has to handle the WM_HSCROLL and WM_VCSROLL messages.

Implementing the Scrollbars

The example has a child window of the class "CChildView". The class "CChildView" is derived from MFC's "CWnd".

The method CChildView::SetupScrollBars sets the current position and size of the video window and initializes the scrollbars. This method has to be called, if the size of the video window changes (i.e. on a zoom or on video format change). To hide a scrollbar, the nPage member of the SCROLLINFO structure must be greater than the nMax member. If they are equal, the scrollbar will be displayed automatically. If the video window's size is equal to the parent window's size, an if statement must be used. This causes the scrollbars not to be displayed.

Setting up the scrollbars is a little tricky. Take a look at the following to see how it is done:

void CChildView::SetupScrollBars()
{
    long posx;
    long posy;
    long width;
    long height;
    RECT clientRect;    // Rectangle of the client window
    long clientEdge;    // Will contain the length of the edges
    SCROLLINFO si;        // Structure to setup the scroll bars
    ZeroMemory(&si, sizeof(SCROLLINFO) );
    si.cbSize = sizeof(si);
    if( m_pGrabber->isDevValid())
    {
        // Get the size of the client rect, that is our window.
        GetClientRect( &clientRect );
        // Retrieve the actual size and position of the Grabber's video window.
        if( m_pGrabber->getWindowPosition( posx, posy, width, height ) == true )
        {
            // Make sure that the whole client area is used.
            clientEdge = clientRect.right - clientRect.left;
            if( clientEdge > width + posx && posx < 0)
            {
                posx -= posx + width - clientEdge;
                if( posx > 0) posx = 0;
            }
            clientEdge = clientRect.bottom - clientRect.top;
            if( clientEdge > height + posy && posy < 0)
            {
                posy -= posy + height - clientEdge;
                if( posy > 0) posy = 0;
            }
            m_pGrabber->setWindowPosition(posx ,posy);
            si.fMask = SIF_PAGE|SIF_RANGE|SIF_POS;
            si.nMin  = 0;
            if( height > clientRect.bottom) // Hide the scroll bar, if the sizes are equal.
            {
                // The page size for the scroll bar is the width or height of the client window.
                si.nPage =  clientRect.bottom ;
                // The maximum scroll value is the width or height of the video window. If
                // the maximum is less than the page size, the scroll bar is hidden.
                si.nMax = height;
                // The current scroll position is the negative value of the video window's
                // position.
                si.nPos = -posy;
            }
            else
            {
                si.nMax     = 2;
                si.nPage = si.nMax +1; // This hides the scroll bar.
            }
            // Setup the vertical scroll bar.
            SetScrollInfo( SB_VERT,&si);
            if( width > clientRect.right) // Hide the scroll bar, if the sizes are equal.
            {
                // Do the same for the horizontal scrollbar.
                si.nPage = clientRect.right;
                si.nMax =  width;
                si.nPos = -posx;
            }
            else
            {
                si.nMax     = 2;
                si.nPage = si.nMax +1; // This hides the scrollbar.
            }
            SetScrollInfo( SB_HORZ,&si);
            PaintImage();
        }
    }
}

The method SetupScrollBars has to be called, whenever the size of the video window changes. This is the case when the zoom factor changes, a WM_SIZE event occurs, setDefaultWindowSize(true) or liveStart() is called.

Scrollbars produce the WM_HSCROLL and the WM_VSCROLL messages. They are handled in the methods OnHScroll and OnVScroll:

///////////////////////////////////////////////////////////////////////////////
/*! The OnVScroll message handler is called, if the user modifies the vertical scroll bar.
*/
void CChildView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    UNREFERENCED_PARAMETER( pScrollBar );
    OnScroll(nSBCode, nPos, SB_VERT);
}
///////////////////////////////////////////////////////////////////////////////
/*! The same has to be done for the horizontal scrollbar.
*/
void CChildView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    UNREFERENCED_PARAMETER( pScrollBar );
    OnScroll(nSBCode, nPos, SB_HORZ);
}

The message handlers for WM_HSCROLL and WM_VSCROLL both call the method OnScroll because the handling is the same for both events. The scrollbar that has initiated the event is passed as a parameter to OnScroll.

First of all, the current size and position of the video window is retrieved in OnScroll. These values are used later for setWindowPosition. Then a case statement checks what kind of action has to be performed on the scrollbar and calculates the new scrollbar value. The flag bSIsScroll indicates whether a new scroll position was calculated or not. If this flag was set to true, the scrolling is done by passing the previously retrieved window parameters and the new scroll position to setWindowPosition. This moves the video window in the client window of the application.

//////////////////////////////////////////////////////////////////////////
/*! OnScroll handles the HScroll and VScroll events.
*/
void CChildView::OnScroll(UINT nSBCode, UINT nPos, int iScrollBar)
{
    long posx;
    long posy;
    long width;
    long height;
    bool bIsScroll = false;
    SCROLLINFO si;    // Structure to setup the scroll bars.
    ZeroMemory(&si, sizeof(SCROLLINFO) );
    // Get the current size and position of the video window.
    m_pGrabber->getWindowPosition( posx, posy, width, height);
    si.cbSize = sizeof(si);
    si.fMask = SIF_POS;
    GetScrollInfo( iScrollBar, &si);
    switch( nSBCode )
    {
        case SB_LINERIGHT :    si.nPos++;
                            bIsScroll = true;
                            break;
        case SB_LINELEFT :  si.nPos--;
                            bIsScroll = true;
                            break;
        case SB_PAGERIGHT : si.nPos+=5;
                            bIsScroll = true;
                            break;
        case SB_PAGELEFT :  si.nPos-=5;
                            bIsScroll = true;
                            break;
        case SB_THUMBTRACK:
        case SB_THUMBPOSITION:    si.nPos   = nPos;
                                bIsScroll = true;
                                break;
    }
    // Check whether a scroll event has been sent.
    if( bIsScroll == true )
    {
        // Scroll vertically or horizontally by passing the scroll bar position
        // as parameter to setWindowPosition.
        // The scroll position of the scroll bar has to be passed as a negative
        // value to setWindowPosition.
        if( iScrollBar == SB_HORZ )
        {
            m_pGrabber->setWindowPosition(-si.nPos, posy );
        }
        else
        {
            m_pGrabber->setWindowPosition(posx ,-si.nPos);
        }
        // Very important: The new scroll position must be passed to the scroll bar.
        // If not, the scroll bar would not show the new position.
        si.fMask  = SIF_POS;
        SetScrollInfo( iScrollBar, &si, TRUE);
        OnPaint();
    }
}

Some hints to scrollbars:

1. The newly calculated scroll position must be passed to the related scrollbar. If this is not done, the new value is not accepted by the scrollbar and scrolling is not possible.

2. After a WM_HSCROLL or WM_VSCROLL message is sent, a second one is initiated by the system. The second message should not processed for scrolling because it passes an invalid nPos value (it is 0) to the message handler.

Extending the Zoom Function

Now that we have implemented scrolling, we have to extend CMainFrame::OnSelchangeZoom() because the current implementation resets the video window's position to (0/0) every time the zoom factor is changed.

We call getWindowPosition to retrieve the current position of the window and calculate the window position according to the new zoom factor. The position that is thus calculated can be set with setWindowPosition after the call to setDefaultWindowPosition(false), which reset the window position to (0/0).

void CMainFrame::OnSelchangeZoom()
{
    float fZoomFactor = 1.0f;
    float fOldZoomFactor = 1.0f;
    long lXpos;
    long lYpos;
    long lWidth;        // New width of the video window.
    long lHeight;        // New heigth of the video window.
    if( m_Grabber.isDevValid()  )
    {
        fZoomFactor = m_cZoomCombo.GetCurSel() + 1.0f; // It is a zero based value, so 1 has to be added.
        fZoomFactor /= 10.0f;                        // Convert to percent.
        // Retrieve the current window position.
        m_Grabber.getWindowPosition(lXpos,lYpos,lWidth, lHeight);
        // Calculate the old zoom factor to recalculate the window position
        // at zoom factor 1.0.
        fOldZoomFactor = (float)lWidth / (float)m_Grabber.getAcqSizeMaxX();
        lXpos = (long)((float)lXpos / fOldZoomFactor);
        lYpos = (long)((float)lYpos / fOldZoomFactor);
        lXpos = (long)((float)lXpos * fZoomFactor);
        lYpos = (long)((float)lYpos * fZoomFactor);
        // Get the current video format size.
        lWidth = m_Grabber.getAcqSizeMaxX();
        lHeight = m_Grabber.getAcqSizeMaxY();
        // Calculate the new width and height according to the zoom factor.
        lWidth  = (long)((float)lWidth  * fZoomFactor);
        lHeight = (long)((float)lHeight * fZoomFactor);
        // Turn off the default window position, so the grabber can change
        // the video window size.
        m_Grabber.setDefaultWindowPosition(false);
        // Set the new window position and size.
        m_Grabber.setWindowPosition(lXpos,lYpos);
        m_Grabber.setWindowSize( lWidth, lHeight);
        m_wndView.SetupScrollBars();
    }
}

void CMainFrame::OnSelchangeZoom()
{
    float fZoomFactor = 1.0f;
    float fOldZoomFactor = 1.0f;
    long lXpos;
    long lYpos;
    long lWidth;        // New width of the video window.
    long lHeight;       // New height of the video window.
    if( m_Grabber.isDevValid() )
    {
        fZoomFactor = m_cZoomCombo.GetCurSel() + 1; // It is a zero based value, so 1 has to be added.
        fZoomFactor /= 10.0f;                        // Convert to percent.
        // Retrieve the current window position.
        m_Grabber.getWindowPosition(lXpos,lYpos,lWidth, lHeight);
        // Calculate the old zoom factor to recalculate the window position
        // at zoom factor 1.0.
        fOldZoomFactor = (float)lWidth / (float)m_Grabber.getAcqSizeMaxX();
        lXpos = (long)((float)lXpos / fOldZoomFactor);
        lYpos = (long)((float)lYpos / fOldZoomFactor);
        lXpos = (long)((float)lXpos * fZoomFactor);
        lYpos = (long)((float)lYpos * fZoomFactor);
        // Get the current video format size.
        lWidth = m_Grabber.getAcqSizeMaxX();
        lHeight = m_Grabber.getAcqSizeMaxY();
        // Calculate the new width and height according to the zoom factor.
        lWidth  = (long)((float)lWidth  * fZoomFactor);
        lHeight = (long)((float)lHeight * fZoomFactor);
        // Turn off the default window position, so the grabber can change
        // the video window size.
        m_Grabber.setDefaultWindowPosition(false);
        // Set the new window position and size.
        m_Grabber.setWindowPosition(lXpos,lYpos);
        m_Grabber.setWindowSize( lWidth, lHeight);
        m_wndView.SetupScrollBars();
    }
}

Related Methods

Zooming, resizing and scrolling related methods are:

<< Programmer's Guide