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