Click here to Skip to main content
15,895,011 members
Articles / Programming Languages / C++

A better Zoomin utility

Rate me:
Please Sign up or sign in to vote.
4.93/5 (32 votes)
17 Jun 20054 min read 81.5K   1.2K   56  
A better Zoomin utility.
// ColorShow.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include "ColorShow.h"
#include "Picker.h"
#include "Shower.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

DECLARE_MESSAGE(UWM_POINT)
DECLARE_MESSAGE(UWM_PICKER)
/////////////////////////////////////////////////////////////////////////////
// CColorShow

CColorShow::CColorShow()
   {
    dx = 16;
    dy = 16;
    dropper = ::LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_PICKER));
    selection.SetRectEmpty();
   }

CColorShow::~CColorShow()
{
}


BEGIN_MESSAGE_MAP(CColorShow, CStatic)
        //{{AFX_MSG_MAP(CColorShow)
        ON_WM_LBUTTONDOWN()
        ON_WM_MOUSEMOVE()
        ON_WM_LBUTTONUP()
        ON_WM_PAINT()
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CColorShow message handlers

/****************************************************************************
*                            CColorShow::StartPick
* Inputs:
*       CPoint pt: Current point
* Result: void
*       
* Effect: 
*       Starts the pick operation. Initiates capture mode, changes the
*       cursor to the pickup cursor, and indicates by setting select to
*       FALSE that we are in pickup mode, not selection mode.
****************************************************************************/

void CColorShow::StartPick(CPoint pt)
    {
     SetCapture();
     ::SetCursor(dropper);
     select = FALSE;
    } // CColorShow::StartPick

/****************************************************************************
*                          CColorShow::OnLButtonDown
* Inputs:
*       UINT nFlags:
*       CPoint point:
* Result: void
*       
* Effect: 
*       Starts a selection operation. Sets capture mode and indicates that
*       the mode is "select" mode, which influences how mouse-moves are handled
****************************************************************************/

void CColorShow::OnLButtonDown(UINT nFlags, CPoint point) 
   {
    SetCapture();
    CRect r;
    GetClientRect(&r);
    previous = anchor = point;
    select = TRUE;
        
    CStatic::OnLButtonDown(nFlags, point);
   }

/****************************************************************************
*                          CColorShow::MakeSelection
* Result: CRect
*       A normalized rectangle for the current selection
****************************************************************************/

CRect CColorShow::MakeSelection()
    {
     CRect r;
     r.left = previous.x;
     r.top = previous.y;
     r.right = anchor.x;
     r.bottom = anchor.y;
     r.NormalizeRect();
     return r;
    } // CColorShow::MakeSelection

/****************************************************************************
*                          CColorShow::DrawSelection
* Inputs:
*       CDC & dc: DC to use
* Result: void
*       
* Effect: 
*       Draws a focus rectangle.
****************************************************************************/

void CColorShow::DrawSelection(CDC & dc)
    {
     CRect r = MakeSelection();
     dc.DrawFocusRect(&r);
    } // CColorShow::DrawSelection

/****************************************************************************
*                           CColorShow::OnMouseMove
* Inputs:
*       UINT nFlags: ignored
*       CPoint point: The point where the mouse is
* Result: void
*       
* Effect: 
*       The point is in client coordinates, but because we are in mouse
*       capture mode, these could well be outside the client area of the window.
*       If the mouse was clicked in the window, indicating a sub-selection is
*       being done, a selection rectangle is rubber-banded. If it is in
*       pickup mode, with the picker activated, the coordinates are converted
*       to screen coordinates and the image is recaptured.
****************************************************************************/

void CColorShow::OnMouseMove(UINT nFlags, CPoint point) 
   {
    if(GetCapture())
       { /* has capture */
        if(select)
           { /* selection */
            CClientDC dc(this);
            DrawSelection(dc);
            previous = point;
            DrawSelection(dc);
           } /* selection */
        else
           { /* pickup */
            where = point;
            ClientToScreen(&where);
            RecomputeImage();
           } /* pickup */
       } /* has capture */
    else
       { /* no capture */
        // Simply report the pixel under the point. This is the pixel
        // under the cursor as it moves in the exploded selection
        CClientDC sourceDC(this);
        color = sourceDC.GetPixel(point);
        GetParent()->SendMessage(UWM_POINT, 0, (LPARAM)color);
       } /* no capture */
        
    CStatic::OnMouseMove(nFlags, point);
   }

/****************************************************************************
*                           CColorShow::OnLButtonUp
* Inputs:
*       UINT nFlags: ignored
*       UINT CPoint: Point at which the button is release
* Result: void
*       
* Effect: 
*       If we are the dragging mode, releases capture. If it was an internal
*       selection, creates a logical selection rectangle which is later
*       converted to device space to do the drawing. If an external search,
*       notifies the parent window that dragging has ended.
****************************************************************************/

void CColorShow::OnLButtonUp(UINT nFlags, CPoint point) 
   {
    if(GetCapture() != NULL)
       { /* had capture */
        ReleaseCapture();
        if(select)
           { /* selection */
            CClientDC dc(this);
            DrawSelection(dc); // erase the selection
            CRect r = MakeSelection(); // create a selection rectangle
            selection = MapSelection(r);
            Invalidate();
           } /* selection */
        else
           { /* pickup */
            GetParent()->SendMessage(UWM_PICKER, (WPARAM)FALSE); // indicate release
            selection.SetRectEmpty();
           } /* pickup */
       } /* had capture */
    CStatic::OnLButtonUp(nFlags, point);
   }

/****************************************************************************
*                             CColorShow::GetSize
* Result: CSize
*       The size to use for computations
****************************************************************************/

CSize CColorShow::GetSize()
    {
     CRect r;
     GetClientRect(&r);
     CSize sz(r.Width() + 1, r.Height() + 1);
     return sz;
    } // CColorShow::GetSize

/****************************************************************************
*                            CColorShow::PrepareDC
* Inputs:
*       CDC & dc: DC to prepare
* Result: void
*       
* Effect: 
*       Sets the mapping mode
****************************************************************************/

void CColorShow::PrepareDC(CDC & dc)
    {
     CSize sz = GetSize();
     dc.SetMapMode(MM_ISOTROPIC);
     dc.SetWindowExt(dx, dy); // Note: Win32 Programming p. 267 says window-before-viewport
     dc.SetViewportExt(sz.cx, sz.cy);
    } // CColorShow::PrepareDC

/****************************************************************************
*                          CColorShow::MapSelection
* Inputs:
*       const CRect & r: Selection rectangle in exploded view
* Result: CRect
*       The representation as coordinates relative to the bitmap
****************************************************************************/

CRect CColorShow::MapSelection(const CRect & r)
    {
     CRect nr = r;
     CSize sz = GetSize();
     CClientDC dc(this);

     dc.SetMapMode(MM_ISOTROPIC);
     dc.SetWindowExt(dx, dy); // Note: Win32 Programming p. 267 says window-before-viewport
     dc.SetViewportExt(sz.cx, sz.cy);
     dc.DPtoLP(&nr);
     return nr; 
    } // CColorShow::MapSelection

/****************************************************************************
*                             CColorShow::OnPaint
* Result: void
*       
* Effect: 
*       Draws the image. This includes the centroid box during picking, and
*       the selection box after a selection has been made. Note the use of
*       scaling to convert between these coordinate systems.
****************************************************************************/

void CColorShow::OnPaint() 
   {
    CPaintDC dc(this); // device context for painting
    if(bmp.m_hObject != NULL)
       { /* has image */
        CDC memDC;
        memDC.CreateCompatibleDC(&dc);
        memDC.SelectObject(&bmp);

        CRect r;
        GetClientRect(&r);
        CBitmap target;
        target.CreateCompatibleBitmap(&dc, r.Width(), r.Height());
        dc.SelectObject(&target);

        CSize sz = GetSize();
        dc.StretchBlt(0, 0, sz.cx, sz.cy, &memDC, 0, 0, dx, dy, SRCCOPY);

        if(!select)
           { /* show pixel */
            CPen fiducial(PS_DOT, 0, RGB(0, 0, 0));
            CArray<CPoint, CPoint&> points;
            // The lines will first be computed in logical points, then converted to
            // device points. Then they will be drawn. Each pair of entries in the
            // array are a MoveTo/LineTo pair
            
            //                                       + 0 (dx/2, 0)
            //                                       |
            //                                       |
            //                                       |
            //                                       |
            //                                       |
            //                                       | 1  (dx/2, dy/2)
            //                               10,8+---+---+9,12
            //                                   |       |
            // 4+--------------------------------+5     6+--------------------------------+7
            //                                   |       |
            //                              11,14+---+---+15,13
            //                                       | 2
            //                                       |
            //                                       |
            //                                       |
            //                                       |
            //                                       |
            //                                       + 3

            points.Add(CPoint(      dx / 2,       0     ));     // 0 +.5x
            points.Add(CPoint(        dx/2,  dy / 2     ));     // 1 +.5x
            points.Add(CPoint(      dx / 2, (dy / 2) + 1));     // 2 +.5x
            points.Add(CPoint(      dx / 2,  dy + 1     ));     // 3 +.5x
            points.Add(CPoint(           0,  dy / 2     ));     // 4 +.5y
            points.Add(CPoint(      dx / 2,  dy / 2     ));     // 5 +.5y
            points.Add(CPoint((dx / 2) + 1,  dy / 2     ));     // 6 +.5y
            points.Add(CPoint(      dx + 1,  dy / 2     ));     // 7 +.5y
            points.Add(CPoint(      dx / 2, (dy / 2)    ));     // 8
            points.Add(CPoint((dx / 2) + 1, (dy / 2)    ));     // 9
            points.Add(CPoint(      dx / 2, (dy / 2)    ));     //10
            points.Add(CPoint(      dx / 2, (dy / 2) + 1));     //11
            points.Add(CPoint((dx / 2) + 1, (dy / 2)    ));     //12
            points.Add(CPoint((dx / 2) + 1, (dy / 2) + 1));     //13
            points.Add(CPoint(      dx / 2, (dy / 2) + 1));     //14
            points.Add(CPoint((dx / 2) + 1, (dy / 2) + 1));     //15

            // We will need the 'half' to adjust the fiducial lines after we've
            // converted to device space, otherwise we can't get them to look
            // like what we want.
            CPoint half(1, 1);

            int save = dc.SaveDC();
            PrepareDC(dc);

            // Convert coordinates to device space
            dc.LPtoDP(points.GetData(), points.GetSize());
            dc.LPtoDP(&half);
            dc.RestoreDC(save);
            // The coordinates are now in device coordinates. Adjust for
            // half-coordinates for the vertical and horizontal lines
            half.x /= 2;
            half.y /= 2;
            points[0].x += half.x;
            points[1].x += half.x;
            points[2].x += half.x;
            points[3].x += half.x;
            points[4].y += half.y;
            points[5].y += half.y;
            points[6].y += half.y;
            points[7].y += half.y;

            dc.SelectObject(&fiducial);
            // For each pair of points, do a LineTo/Moveto to
            // draw each of the lines of the desired display
            for(int i = 0; i < points.GetSize(); i += 2)
               { /* draw each line */
                dc.MoveTo(points[i]);
                dc.LineTo(points[i + 1]);
               } /* draw each line */
           } /* show pixel */

        // If there is a selection, draw the outline
        if(!selection.IsRectEmpty())
           { /* has selection */
            CPen outline(PS_DOT, 0, RGB(0, 0, 0));
            
            CRect sel = selection;
            int save = dc.SaveDC();
            PrepareDC(dc);
            dc.SelectObject(&outline);
            dc.SelectStockObject(HOLLOW_BRUSH);
            dc.Rectangle(&sel);
            dc.RestoreDC(save);
           } /* has selection */
       } /* has image */
        
    // Do not call CStatic::OnPaint() for painting messages
   }

/****************************************************************************
*                         CColorShow::RecomputeImage
* Result: void
*       
* Effect: 
*       Recomputes the image. Figures out where it is on the screen
*       and captures the screen bitmap.
****************************************************************************/

void CColorShow::RecomputeImage()
    {
     CPoint pt;
     pt.x = where.x - dx / 2;
     pt.y = where.y - dy / 2;

     CDC sourceDC ;
     sourceDC.Attach(::GetWindowDC(NULL)); // get entire window

     CDC memDC;

     color = sourceDC.GetPixel(where);
     GetParent()->SendMessage(UWM_POINT, (WPARAM)MAKELONG(where.x, where.y), (LPARAM)color);
     memDC.CreateCompatibleDC(&sourceDC);

     if(bmp.m_hObject == NULL)
        bmp.CreateCompatibleBitmap(&sourceDC, dx, dy);

     int save = memDC.SaveDC();

     memDC.SelectObject(&bmp);
     memDC.BitBlt(0, 0, dx, dy, &sourceDC, pt.x, pt.y, SRCCOPY);

     memDC.RestoreDC(save);
     ::ReleaseDC(NULL, sourceDC.Detach());
     Invalidate();
    } // CColorShow::RecomputeImage

/****************************************************************************
*                             CColorShow::SetSize
* Inputs:
*       int n: Desired size to set
* Result: void
*       
* Effect: 
*       Sets the size of the capture area.
****************************************************************************/

void CColorShow::SetSize(int n)
    {
     dx = n;
     dy = n;
     if(bmp.m_hObject != NULL)
        bmp.DeleteObject();
     RecomputeImage(); 
    } // CColorShow::SetSize

/****************************************************************************
*                            CColorShow::AdjustPos
* Inputs:
*       int dx: Change in x
*       int dy: Change in y
* Result: void
*       
* Effect: 
*       Computes new x,y to "nudge" the image around.
****************************************************************************/

void CColorShow::AdjustPos(int dx, int dy)
    {
     where.x += dx;
     where.y += dy;
     RecomputeImage();
    } // CColorShow::AdjustPos

/****************************************************************************
*                              CColorShow::Copy
* Result: void
*       
* Effect: 
*       Copies the real bitmap (not the expanded image) to the clipboard
****************************************************************************/

void CColorShow::Copy()
    {
     if(bmp.m_hObject == NULL)
        return;

     CDC sourceDC;
     sourceDC.CreateCompatibleDC(NULL);
     sourceDC.SelectObject(&bmp);
     CDC targetDC;
     targetDC.CreateCompatibleDC(&sourceDC);

     CBitmap local;
     // Create a newbitmap which is the correct bitmap.
     // If there is no selection, it is the entire bitmap capture
     // If there is a selection, the selection is extracted.

     if(selection.IsRectEmpty())
        { /* local copy */
         local.CreateCompatibleBitmap(&sourceDC, dx, dy);
         targetDC.SelectObject(&local);
         targetDC.BitBlt(0, 0, dx, dy, &sourceDC, 0, 0, SRCCOPY);
        } /* local copy */
     else
        { /* subselection */
         local.CreateCompatibleBitmap(&sourceDC, selection.Width(), selection.Height());
         targetDC.SelectObject(&local);
         targetDC.BitBlt(0, 0, selection.Width(), selection.Height(), &sourceDC, selection.left, selection.top, SRCCOPY);
        } /* subselection */
     // Store it in the clipboard.
     OpenClipboard();
     ::EmptyClipboard();
     ::SetClipboardData(CF_BITMAP, local.m_hObject);
     local.Detach();
     ::CloseClipboard();
    } // CColorShow::Copy

/****************************************************************************
*                              CColorShow::Show
* Result: void
*       
* Effect: 
*       Shows a window that illustrates the source of the bitmap and
*       if there is a selection, a smaller rectangle showing the selection
*       is also shown.
****************************************************************************/

void CColorShow::Show()
    {
     CShower * shower = new CShower;
     CRect r;
     r.left = where.x - dx / 2;
     r.top = where.y - dy / 2;
     r.right = r.left + dx;
     r.bottom = r.top + dy;
     // Note that we have to register a class so we can create
     // a popup window. We can't use NULL for a classname
     // or there is an assertion error.
     LPCTSTR classname = AfxRegisterWndClass(0);

     shower->CreateEx(WS_EX_TRANSPARENT,   // extended style
                     classname,// classname
                     NULL,// caption
                     WS_POPUP | WS_VISIBLE,
                     r,
                     this,
                     0,
                     &selection);
    } // CColorShow::Show

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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


Written By
Retired
United States United States
PhD, Computer Science, Carnegie Mellon University, 1975
Certificate in Forensic Science and the Law, Duquesne University, 2008

Co-Author, [i]Win32 Programming[/i]

Comments and Discussions