65.9K
CodeProject 正在变化。 阅读更多。
Home

颜色拾取

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (20投票s)

2005年3月22日

3分钟阅读

viewsIcon

88888

downloadIcon

1571

一篇关于从桌面获取像素颜色的文章。

Sample Image

引言

这个程序从桌面获取像素的颜色,在一个窗口中显示它们,并有一个 4 - 1 倍的放大镜。这些颜色可以稍后复制到剪贴板。

背景

这个版本对用户更友好。选定的颜色填充五个框。相关的按钮将其颜色复制到复制窗口,格式由“选择格式”选项选择。这些按钮最初是禁用的,否则对话框窗口的背景颜色将被复制。按下“复制”将此值发送到剪贴板。

我知道,你会认为这以前已经做过了,甚至在这个网站上也有,但我展示这篇文章的原因是:我订阅了 Lockergnome - 一份 Windows 狂热者通讯。上周,他们有一个程序可以下载,叫做 TakeColor。我想看看我是否可以伪造逆向工程它。结果证明是相当具有挑战性的:TakeColor - flaxie.com

使用代码

元素

  1. 一个菜单。
  2. 一个显示像素颜色的窗口。
  3. 一个缩放窗口(4 - 1 倍放大镜)。
  4. 显示 HEX、HTML 或 RGB。
  5. 鼠标坐标。
  6. 选定颜色列表。
  7. 五个框显示最后五个选定的颜色。
  8. 复制到剪贴板。

解释

  1. 菜单,没什么大不了的,使用了资源编辑器。第一个挑战是 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);
    }
  2. 显示当前颜色。
        CDC *pSDC = m_current.GetDC();
        CRect sRect;
        m_current.GetClientRect(&sRect);
        pSDC->FillSolidRect(&sRect, m_colCurrent);
        ReleaseDC(pSDC);
  3. 缩放窗口。
        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);
        }
  4. 显示 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;
        }
  5. 鼠标坐标:这是一个很大的挑战,我使用 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);
        ...
        ...
  6. 选定颜色列表。
        CString str;
        m_color.GetWindowText(str);
        m_list1.InsertString(0, str);
        UpdateData(FALSE);
  7. 五个框显示最后五个选定的颜色。
    // 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();
    }
  8. 复制到剪贴板。
    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 - 最终版本,用户友好。
© . All rights reserved.