当前位置:C++技术网 > 精选软件 > win32实现简单易用的拾色器(取色器)的功能及代码分析

win32实现简单易用的拾色器(取色器)的功能及代码分析

更新时间:2016-01-14 15:25:56浏览次数:1+次

    拾色器,也叫作取色器、屏幕取色器,是获取屏幕中某一点的颜色值。有了颜色值,就可以将拾取的颜色应用在其他地方,如网页UI、软件的界面。

    我们自己来实现一个拾色器,通过本文的详细讲解,你也可以轻松制作一个自己的拾色器哦。下面是拾色器程序的截图:

拾色器拾取颜色后的截图

【拾色器拾取颜色后的截图】
    我们先要知道拾色器是如何工作的,然后才是实现。我们并不是为了制作拾色器而制作拾色器。我们需要知道其中的原理,然后就可以更加灵活的增加自己的需求。
    拾色器的使用方法是:在窗口客户区中按下鼠标左键,然后拖动鼠标到屏幕的任何一个地方,都可以获取鼠标光标热点下的这个点的颜色值。然后程序将这个颜色值设置为窗口客户区的背景色,这样整个客户区就显示了当前拾取的背景色,达到预览的效果。
    通常情况下,我们鼠标移出窗口外,就不在窗口的控制范围内了。而我们确实需要这样的需求,可以脱离窗口外在屏幕任何位置上取得像素的颜色。这就需要使用鼠标捕获的技术了。对于鼠标捕获的技术分析,请阅读文章《SetCapture捕捉鼠标全面深入详解》。
    我们在鼠标左键按下时捕获鼠标,并且将鼠标光标替换为十字架,表示现在鼠标处于捕获状态,可以随意移动鼠标。当鼠标左键弹起,就释放鼠标捕获,恢复光标为箭头。
    我们现在已经具备可以在全屏幕取颜色的光标了。那么如何得到屏幕的DC呢?如果没有屏幕的DC,而只有窗口的DC,不管鼠标光标移动到哪,得到的都是窗口的像素颜色。这样是无法完成获取屏幕像素点的颜色的。
    要得到屏幕DC有两种方法,代码如下:
//hScreen = CreateDC(_T("DISPLAY"),0,0,0);//方法1
hScreen = GetDC(NULL);//方法2
    两种方法得到的效果都是一样的。所谓屏幕DC,其实就是当前显示的画面的视频显示卡上的一个位图副本。我们取色就是在这个位图副本对应的坐标取得相应的像素点的值,像素点的值由颜色值构成。我们这里获取的是24位的真彩色,不讨论索引颜色。
    一样两句代码选其一即可,都是获取DC,和获取窗口DC用法一样。我们在WM_CREATE创建,并将屏幕DC句柄存储起来,全局使用。在WM_DESTROY删除或者释放。如果是第一种方法,则对应的是DeleteDC,第二种就是ReleaseDC。不要混乱了,记住:获取的东西只要释放即可,创建的东西需要删除!
    我们在窗口第一次显示时,使用WM_PAINT提示初始化颜色值和操作方法。以后除非要重画窗口,否则不会在有什么作用了。我们的实时提示和填充背景颜色,都是在鼠标移动消息中处理的。这样效率比较高。而在WM_PAINT消息中的代码保持和鼠标移动消息一样,是为了在拾取颜色之后,再调整窗口大小时,客户区还是保持一致,不会因为调整了窗口的大小后,客户区的颜色变了。
    在客户区绘制文字和填充背景,还是要获取客户区的DC。在WM_MOUSEMOVE消息的lParam参数中,携带了鼠标的客户区坐标,高字部分为y坐标,低字部分为x坐标,我们使用宏进行提取。对于LOWORD和HIWORD的使用,请参见《DWORD、WORD的高低部分提取和合成宏代码示例》。因为坐标是可以具备正负的,因为我们使用了鼠标捕获,鼠标光标可以到达客户区负数坐标的位置,即左边和上边。所以,我们要将坐标转换为short有符号类型。因为WORD宏就是unsigned short的重定义,所以它是无符号的。然而我们此时需要有符号的值,要将值转换为short类型。而且还不能是int类型。int类型的位数太多了,无法分辨出正负了。
    我们要在屏幕中获取像素值,自然要得到屏幕的坐标才是正确的。所以要将客户区坐标转换为屏幕坐标,使用ClientToScreen来完成。对于客户区坐标和屏幕坐标的互相转换,请阅读《图解客户区坐标与屏幕坐标相互转换》。
    我们将获取的颜色值,即COLORREF类型的值,使用宏提取出红绿蓝三个颜色分量,然后用来创建画刷,并使用FillRect来填充客户区。颜色的分量提取,可以参考文章《COLOR和COLORREF之间的理解与相互转换》。
    FillRect函数的使用分析,可以参考《FillRect、FrameRect与Rectangle矩形绘制函数使用对比分析
    代码里面,基本都标注了详细的注释,就不再一一介绍了。
    下面是完整的代码:
#include "windows.h"
#include <tchar.h>
TCHAR tip[100]=_T("");
TCHAR txt[100]=_T("C++技术网提示:按住鼠标左键拖动光标到窗口外可以拾取屏幕任何位置的颜色。");
TCHAR clrhex[20]=_T("");

// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static HDC hScreen;
    RECT rect;
    static COLORREF clr;
    HBRUSH hBrush;
    switch (message)
    {
        case WM_CREATE:
            {
                //使用下面两种的一种都可以获得屏幕的DC,在窗口销毁要做对应的删除或者释放
                //hScreen = CreateDC(_T("DISPLAY"),0,0,0);//方法1
                hScreen = GetDC(NULL);//方法2
            }
        case WM_PAINT:
            hdc = BeginPaint(hwnd,&ps);
            //重绘窗口显示提示文字
            GetClientRect(hwnd,&rect);//获取客户区矩形
            hBrush = CreateSolidBrush(clr);//创建画刷
            FillRect(hdc,&rect,hBrush);//使用画刷填充客户区,即显示拾取的颜色
            SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));//使用等宽字体
            SetBkMode(hdc,TRANSPARENT);//设置文字背景透明
            SetTextColor(hdc,RGB(255-GetRValue(clr),255-GetGValue(clr),255-GetBValue(clr)));//设置文字颜色
            DrawText(hdc,txt,-1,&rect,DT_WORDBREAK);//显示操作方法
            TextOut(hdc,0,50,tip,lstrlen(tip));//输出颜色提示
            EndPaint(hwnd,&ps);
            return 0;
        case WM_LBUTTONDOWN:
            SetCapture(hwnd);//捕获鼠标
            SetCursor(LoadCursor(NULL,IDC_CROSS));//显示十字架光标
            return 0;
        case WM_LBUTTONUP:
            ReleaseCapture();//释放鼠标
            SetCursor(LoadCursor(NULL,IDC_ARROW));//显示箭头光标
            return 0;
        case WM_MOUSEMOVE:
            {
                hdc = GetDC(hwnd);
                POINT ptScreen;
                //提取鼠标坐标,基于客户区坐标
                ptScreen.x = (short)LOWORD(lParam);
                ptScreen.y = (short)HIWORD(lParam);
                ClientToScreen(hwnd,&ptScreen);//转换为屏幕坐标
                clr = GetPixel(hScreen,ptScreen.x,ptScreen.y);//获取屏幕中指定坐标的颜色值
                wsprintf(clrhex,_T("%X%X%X"),GetRValue(clr),GetGValue(clr),GetBValue(clr));//格式化颜色值到字符数组
                //格式化显示的颜色提示
                wsprintf(tip,_T("颜色(%d,%d,%d),十六进制:#%s"),GetRValue(clr),GetGValue(clr),GetBValue(clr),clrhex);
                GetClientRect(hwnd,&rect);//获取客户区矩形
                hBrush = CreateSolidBrush(clr);//创建画刷
                FillRect(hdc,&rect,hBrush);//使用画刷填充客户区,即显示拾取的颜色
                SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));//使用等宽字体
                SetBkMode(hdc,TRANSPARENT);//设置文字背景透明
                SetTextColor(hdc,RGB(255-GetRValue(clr),255-GetGValue(clr),255-GetBValue(clr)));//设置文字颜色
                DrawText(hdc,txt,-1,&rect,DT_WORDBREAK);//显示操作方法
                TextOut(hdc,0,50,tip,lstrlen(tip));//输出颜色提示
                DeleteObject(SelectObject(hdc,GetStockObject(WHITE_BRUSH)));//删除画刷
                ReleaseDC(hwnd,hdc);
             }
            return 0;
        case WM_DESTROY:
            //好习惯,用完删除或者释放
            //DeleteDC(hScreen);
            ReleaseDC(hwnd,hScreen);
            PostQuitMessage(0);
            return 0;
        default:
        break;//跳出到默认处理
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
    TCHAR ClassName[] = _T("MyClass");
    TCHAR title1[] = _T("C++技术网http://www.cjjjs.com:拾色器");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WinProc;
    wndClass.lpszClassName = ClassName;
    wndClass.lpszMenuName=NULL;
    wndClass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;

    if(!RegisterClass(&wndClass))return 0;
    HWND hwnd = CreateWindow(ClassName,title1,WS_OVERLAPPEDWINDOW,10,100,550,400,NULL,NULL,hInstance,NULL);
    ShowWindow(hwnd,SW_SHOWNORMAL);

    MSG msg;
    while (GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}