颜色拾取






4.89/5 (20投票s)
2005年3月22日
3分钟阅读

88888

1571
一篇关于从桌面获取像素颜色的文章。
引言
这个程序从桌面获取像素的颜色,在一个窗口中显示它们,并有一个 4 - 1 倍的放大镜。这些颜色可以稍后复制到剪贴板。
背景
这个版本对用户更友好。选定的颜色填充五个框。相关的按钮将其颜色复制到复制窗口,格式由“选择格式”选项选择。这些按钮最初是禁用的,否则对话框窗口的背景颜色将被复制。按下“复制”将此值发送到剪贴板。
我知道,你会认为这以前已经做过了,甚至在这个网站上也有,但我展示这篇文章的原因是:我订阅了 Lockergnome - 一份 Windows 狂热者通讯。上周,他们有一个程序可以下载,叫做 TakeColor。我想看看我是否可以伪造逆向工程它。结果证明是相当具有挑战性的:TakeColor - flaxie.com。
使用代码
元素
- 一个菜单。
- 一个显示像素颜色的窗口。
- 一个缩放窗口(4 - 1 倍放大镜)。
- 显示 HEX、HTML 或 RGB。
- 鼠标坐标。
- 选定颜色列表。
- 五个框显示最后五个选定的颜色。
- 复制到剪贴板。
解释
- 菜单,没什么大不了的,使用了资源编辑器。第一个挑战是
ON_UPDATE_COMMAND_UI
不适用于非系统菜单。此函数切换菜单项。我们可以使用ToggleMenuItem(0, 1)
调用它。我手动编码了菜单项和子项,这个调用在“选项”菜单上设置一个复选标记,“始终位于顶部”。void CColorTakeDlg::ToggleMenuItem(int nMenu, int nPos) { // Onupdate command UI doesn't work in a non-system menu CMenu *pMenu = GetMenu(); CMenu *pSubMenu = pMenu->GetSubMenu(nMenu); UINT nID = pSubMenu->GetMenuItemID(nPos); UINT nState = pSubMenu->GetMenuState(nID, MF_BYCOMMAND); ASSERT(nState != 0xFFFFFFFF); if(nState & MF_CHECKED) pSubMenu->CheckMenuItem(nID, MF_UNCHECKED | MF_BYCOMMAND); else pSubMenu->CheckMenuItem(nID, MF_CHECKED | MF_BYCOMMAND); }
- 显示当前颜色。
CDC *pSDC = m_current.GetDC(); CRect sRect; m_current.GetClientRect(&sRect); pSDC->FillSolidRect(&sRect, m_colCurrent); ReleaseDC(pSDC);
- 缩放窗口。
if (bEnableZoom) { CDC *pDC = m_zoom.GetDC(); CRect m_rect; m_zoom.GetClientRect(&m_rect); InflateRect(&m_rect, -1, 0); pDC->SetStretchBltMode(COLORONCOLOR); // stretch the 20 x 20 to the 81 x 81 window, // close to 4 to 1 magnifier pDC->StretchBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(), pDesktopDC, pt.x - m_nOffset, pt.y - m_nOffset, m_nWidth, m_nWidth, SRCCOPY); pen.DeleteObject(); pen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); CPen *pOldPen = pDC->SelectObject(&pen); CPoint point; // draw a red line at the mid point of the x and y axis point.x = m_rect.Width()/2; point.y = m_rect.top; pDC->MoveTo(point); point.y = m_rect.bottom; pDC->LineTo(point); point.x = m_rect.left; point.y = m_rect.Height()/2; pDC->MoveTo(point); point.x = m_rect.right; pDC->LineTo(point); pDC->SelectObject(pOldPen); ReleaseDC(pDC); }
- 显示 HEX、HTML 或 RGB。
switch (m_nSelect) { case 0: str.Format("%02X, %02X, %02X", rVal, gVal, bVal); // Hex m_color.SetWindowText((LPCTSTR)str); break; case 1: str.Format("#%02X%02X%02X", rVal, gVal, bVal); // HTML m_color.SetWindowText((LPCTSTR)str); break; case 2: str.Format("%d, %d, %d", rVal, gVal, bVal); // RGB m_color.SetWindowText((LPCTSTR)str); break; }
- 鼠标坐标:这是一个很大的挑战,我使用
SetCapture()
从桌面获取光标。在以前的版本中,这是一个错误。void CColorTakeDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default HWND m_hwnd = ::GetDesktopWindow(); CString str; POINT pt; pt.x = pt.y = 0; // ======================================================================= // Get the mouse pointer position and display it // ======================================================================= GetCursorPos(&pt); str.Format("X: %d, Y: %d", pt.x, pt.y); m_mouse.SetWindowText((LPCTSTR)str); ... ...
- 选定颜色列表。
CString str; m_color.GetWindowText(str); m_list1.InsertString(0, str); UpdateData(FALSE);
- 五个框显示最后五个选定的颜色。
// Store the current selected color in the left most // of 5 boxes, shifting old ones right. void CColorTakeDlg::CopyColor() { m_nColor[4] = m_nColor[3]; m_nColor[3] = m_nColor[2]; m_nColor[2] = m_nColor[1]; m_nColor[1] = m_nColor[0]; m_nColor[0] = m_colCurrent; DrawColorBoxes(); }
- 复制到剪贴板。
void CColorTakeDlg::OnCopy() { // TODO: Add your control notification handler code here m_edit1.SetSel(0, -1); // Copy selected color to the clipboard m_edit1.Copy(); }
关注点
这个程序使用 CHotKeyCtrl
类将选定的颜色复制到列表和第一个框。 函数 void GetHotKey( WORD &wVirtualKeyCode, WORD &wModifiers ) const;
用于从控件获取热键。wVirtualKeyCode
是按下的键盘键(A - Z),wModifiers
是控制键(Shift、Ctrl、Alt 和 Windows 键)。 在 CommCtrl.h 中定义的修饰符标志可以是以下值的组合
#define HOTKEYF_SHIFT 0x01 #define HOTKEYF_CONTROL 0x02 #define HOTKEYF_ALT 0x04 #define HOTKEYF_EXT 0x08
然后必须使用以下方法注册热键
BOOL RegisterHotKey( HWND hWnd, // handle to window int id, // hot key identifier UINT fsModifiers, // key-modifier options UINT vk // virtual-key code );
fsModifiers
的值在 WinUser.h 中定义。
#define MOD_ALT 0x0001 #define MOD_CONTROL 0x0002 #define MOD_SHIFT 0x0004 #define MOD_WIN 0x0008
正如您所看到的,这些值并不相同,所以我拥有
void CColorTakeDlg::OnNewHotkey() { // TODO: Add your control notification handler code here UnregisterHotKey(m_hWnd, HOTKEYID); m_hotkey1.GetHotKey(wVirtualKeyCode, wModifiers); switch(wModifiers) { case HOTKEYF_SHIFT: fsModifiers = MOD_SHIFT; break; case HOTKEYF_CONTROL: fsModifiers = MOD_CONTROL; break; case HOTKEYF_ALT: fsModifiers = MOD_ALT; break; case HOTKEYF_EXT: fsModifiers = MOD_WIN; break; } RegisterHotKey(m_hWnd, HOTKEYID, fsModifiers, wVirtualKeyCode); }
这只有四种组合,我不想允许所有 16 种,所以我使用了 SetRules()
。
m_hotkey1.SetHotKey(0x43, HOTKEYF_ALT); m_hotkey1.SetRules(HKCOMB_CA | HKCOMB_SA | HKCOMB_SC | HKCOMB_SCA | HKCOMB_NONE, HOTKEYF_ALT); RegisterHotKey(m_hWnd, HOTKEYID, MOD_ALT, 0x43); wVirtualKeyCode = 0x43; wModifiers = HOTKEYF_ALT; fsModifiers = MOD_ALT;
如果你想要它很疯狂,然后将光标定位在放大镜窗口中。当您接近十字线(1 像素宽)时,您将在窗口中看到它们是 4 像素宽。如果您靠得更近,则为 16 像素宽。大约在它们变为 64 像素宽的时候,窗口会变成红色。
警告
如果在旧版本的 Windows(Win95/Win98)上使用此程序,并且缩放窗口无法正常工作,那么您就遇到了问题。 GDI 资源导致 StretchBlt(...)
返回 FALSE
:请参阅 MSDN 知识库文章。
历史
- 2005 年 3 月 19 日 - 版本 1.0。
- 2005 年 3 月 25 日 - 版本 1.1 - 新的线程方法。
- 2005 年 3 月 31 日 - 版本 1.2 - 修复了错误,添加了已选中的标记。
- 2005 年 11 月 22 日 - 版本 1.3 - 缩放的新方法。
- 2005 年 12 月 4 日 - 版本 1.4 - 最终版本,用户友好。