第5章 绘图基础_5.1-5.4 GDI绘图

5.1 GDI的原理和结构

(1)提供一种特殊机制彻底隔离应用程序与不同输出设备(eg.显示器或打印机),以便支持 与设备无关的图形。

                光栅设备(如显示器、激光打印机):图像是由点构成的矩阵

                图形输出设备   

                矢量设备(如绘图仪):使用 线条来绘制图形

(2)Windows GDI允许使用逻辑坐标系统来保证程序与硬件的独立,也可以统用设备坐标系统 (单位:像素)来迎合硬件的需求。

(3)GDI总体上是一个静态显示系统,对动画的支持有限。DirectX可支持动画。

5.2 设备环境

5.2.1 获取设备环境句柄

(1)在WM_PAINT中获取的是无效区的句柄

   hDC = BeginPaint(hWnd,&ps);

      //其他代码

   EndPaint(hWnd,&ps);

(2)在非WM_PAINT中

获取整个客户区DC

获取整个窗口DC(含非客户区)

hDC = GetDC(hWnd);

//GetDC(NULL)时为屏幕DC

//其他代码

Release(hWnd,hDC);

hDC = GetWindowDC(hWnd);

//其他代码

Release(hWnd,hDC);

(3)更通用的方法(未必一定要窗口相关联,也可以是内存或打印机的DC)

  //①整个屏幕DC

  hDC =CreateDC(TEXT(“DISPLAY”),NULL,NULL,NULL);

  DeleteDC(hDC);

  //②内存DC

  hdcMem= CreateCompatibleDC(hDC);

  DeleteDC(hdcMem);

  //③获得图元文件的设备环境句柄

  hdcMeta= CreateMetaFile(pszFileName);

  hmf =CloseMetaFile(hdcMeta);

(4)只需要获取设备环境信息,而不在其上绘制:CreateIC——(Information Context)

5.2.2 获取设环境的信息

(1)设备的分辨率

  ①显示器:每毫米(或英寸)的像素总数。

  ②打印机:每毫米(或英寸)的点数,1磅≈1/72英寸。

(2)GetDeviceCaps(hdc,iIndex)

iIndex

iValue(返回值)

备注

HORZRES

以像素为单位的设备宽度

1、当hdc为整个屏幕DC时,与GetSystemMetrics获得的信息一致。

2、当hdc为打印机时,获得的是打机印显示区域的宽度和高度。

VERTRES

以像素为单位的设备高度

HORZSIZE

以毫米为单位的屏幕宽度

1、Windows98下:

HORZSIZE = 25.4×HORZRES/LOGPIXELSX.

VERTSIZE = 25.4×VERTRES/LOGPIXELSY

2、Windows NT下:为“标准显示器”的大小,即320×240,但可通过上述公 式算出这两个真实的值出来。

3、对于打印机是物理尺寸,对于显示器是逻辑尺寸。

VERTSIZE

以毫米为单位的屏幕高度

LOGPIXELSX

每英寸的水平像素数

1、对于打印机:可以用上述公式运算出来。

2、对于显示器:与Windows设置的字体有关:正常字体96dpi,大字体为120dpi

LOGPIXELSX

每英寸的垂直像素数

5.2.3 关于GetDeviceCaps的扩展学习

(1)屏幕物理分辨率与屏幕分辨率的区别

  ①物理分辨率:屏幕的最高分辨率,显示器一生产出来,就固定下来了。

  ②屏幕像素规模(HORZRES和VERTRES)与屏幕分辨率(屏幕dpi)

       A、屏幕像素规模:如1024×768

       B、屏幕dpi = 25.4×HORZRES/HORZSIZE或 25.4×VERTRES/VERTSIZE

  屏幕dpi是可改变。降低像素规模时(如1024×768改成800×600),dpi可能会变化,也可能不变。经测试,台式机和笔记本上dpi变化有所不同。

 

显示效果

说明

笔记本

LED显示器

屏幕显示区域缩窄,但像素大小不变

同时影响HORZRES和HORZSIZE, 但dpi不变

台式机

CRT显示器

屏幕显示区域不变,但像素增大

(LED某些分辨率下也会出现)

只影响HORZRES,屏幕dpi变大

  ③下面以台式机CRT显示器为例,讨论屏幕dpi变化的情况下,显示的图像大小变化:

  如15.6英寸(13.5×7.6)在全屏幕模式下,当设置为1366×768像素时,dpi 为100。当设置683×384下,dpi为50。明显,屏幕上每个像素大小,第2种情况应比第1种大 1倍,因为GPU会把屏幕上的4个像素当1个像素(2×2)用。这就导致了显示的图像变大、变 模糊了。当然,如果是在笔记本显示器下测试,因dpi不变,显示出来的图像大小也就不变了。因 此当改变了屏幕像素规模时,一定要看下屏幕dpi是否变化,才能判断出显示出来的图像大小是否 有变化。

(2)逻辑dpi(=LOGPIXELSX或LOGPIXELSY),Windows下默认为96dpi。

  ①为什么要使用逻辑dpi,而不直接使用屏幕实际的分辨率

  因为早期显示器并不存储物理尺寸等信息,所以Windows无法获取显示器的真实尺寸,也 就没办法得到屏幕分辨率(dpi),这将导致windows在输出时,不知该将1英寸长度的物体,转为 多少个像素输出。所以只能采用市面上常见的显示器的分辨率,硬性规定一个默认值(如Windows 下为96dpi),如此便可以将1英寸长度转化为96个像素输出(注意,这时屏幕上看到的那段长度 不一定就是严格的1英寸长,因为96个像素被输往屏幕后,要根据屏幕上每个像素的实际大小来算 出显示的实际长度,而屏幕像素大小要通过其dpi计算得到)。现在由于EDID技术,将物理尺寸等 信息直接存进显示器,所以将物体按屏幕实际dpi(不是逻辑dpi)显示将成为一种趋势,因为这 可以让我们的显示器按照物体的真实尺寸来显示的。

  ②逻辑dpi的含义:将1英寸的长度转化为相应的像素,以这样的像素量输出。

  如:10磅字体,在逻辑96dpi下将被转化为10/72*96,即13个像素输出;在120dpi下将被 转化为16个像素输出。至于在屏幕上这96个像素(或120)显示出来的实际大小,要根据屏幕dpi 去算出来。通常,在屏幕dpi相等的情况下(如1024×768,设dpi为100),逻辑dpi为120时 显示出来的字要更大,因为13<16。这两种字体在屏幕上的实际长度分别为13/100英寸和 16/100英寸。

  ③改变屏幕dpi与改变逻辑dpi的不同。

 

实验:将96px×96px,物理尺寸为1×1英寸蓝色方块输往屏幕

A、将屏幕dpi由200dpi→100dpi

(如1600×1200→800×600,假设这两种情况都是全屏下。否则,这时windows 会调整桌面大小,而让屏幕dpi保持不变)

改变了屏幕dpi,这时意味着屏幕上每个像素变大了,所以显示出来方块会比实际的1×1 英寸大。

B、将逻辑dpi由96→120。

(在相同屏幕dpi下,如1024×768下,只调整逻辑dpi的值)

1、注意这时屏幕像素大小并没变,因为屏幕dpi没变化。

2、改变了逻辑dpi,意味着要将1英寸长度转为120个像素输出。注意到了吗,Windows偷偷地 将方块像素比增加了,从96px×96px方块调大成120px×120px方块输出。所以,显示 出来的图形也就比原来的更大了。

5.2.4 色彩ABC

  #define RGB(r,g,b) ((COLORREF)(((BYTE)(r))|

           ((WORD)((BYTE)(g))<<8))|

                               (((DWORD)(BYTE)(b))<<16)))

5.2.5 保存设备环境属性

(1)GetDC或BeginPaint返回的设备环境属性值是默认的。ReleaseDC或EndPaint后,所做的 任何改变都会丢失。

(2)如何解决?

  ①注册窗口类时wndClass.style=CS_HREDRAW | CS_VREDRAW | CS_OWNDC,这样这样基于 这个窗口类创建的窗口都有它私有的设备环境。CS_OWNDC只影响通过GETDC和BeginPaint获得的DC ,通过其他函数(如GetWindowDC)获得的设备环境并不受影响。

  ②在WM_CREATE消息初始化设备环境的属性

  case WM_CREATE:

    hdc = GetDC(hWnd);

    //初始化各个属性

    ReleaseDC(hWnd,hdc);

(3)保存与恢复

  idSaved= SaveDC(hdc); //可多次save,并保存在不同的id中。

  RestoreDC(hdc,idSaved);//RestoreDC(hdc,-1)为恢复到最近一次保存的状态。

5.3 点和线的绘制

5.3.1 设定像素

  SetPixel(hdc,x,y,crColor);

  COLORREF crColor = GetPixel(hdc,x,y);

5.3.2 直线

(1)MoveTo和MoveToEx

  MoveTo返回值是DWORD型,用于表示运行函数前的当前位置(x,y)——早期 windows。而现在的坐标(x,y)都为32位的,所以该函数不能现在的windows中返回正确的坐标。

  MoveToEx返回值BOOL。MoveToEX(hdc,x,y,&pt),第三个参数为运行该函数前的当前 位置。不需要为NULL。即MoveToEX(hdc,x,y,NULL)——现在Windows中。

(2)获取当前位置:GetCurrentPosition(hdc,&pt)

(3)LineTo、PolyLineTo等带To的函数会把最后一次的终点设为当前位置。

(4)PolyLine和PolyLineTo的区别

POINT apt[5] ={100,100,200,100,200,200,100,200,100,100} //首尾点相同
int iArrayLen = sizeof(apt)/sizeof(POINT);

//用

MoveToEx和LineTo绘制
MoveToEx(hdc,apt[0].x,apt[0].y,NULL)
for(int i=1;i<iArrayLen;i++)
{
   LineTo(hdc,apt[i].x,apt[i].y);
} 

//用

PolyLine绘制,并不改变当前位置。
PolyLine(hdc,apt,iArrayLen);//最后一个参数表示要绘制的点的个数。 

//用

PolyLineTo绘制,要将当前位置作为起点,绘制完后,会改变当前位置。
MoveToEx(hdc,apt[0].x,apt[0].y,NULL);//从当前绘制为起点
PolyLine(hdc,apt+1,iArrayLen-1); //画下后面的点。

【SINWAVE程序】

/*------------------------------------------------------------
SINEWAVE.C -- Sine Wave Using Polyline
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include <math.h>

#define NUM 1000
#define TWOPI (2*3.14159)

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI 

WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("SinWave");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, 

IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, 

IDC_ARROW);
    wndclass.hbrBackground = 

(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("SinWave"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 

0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 

msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    static int cxClient, cyClient;
    POINT  apt[NUM];
    switch 

(message)
    {
    case 

WM_CREATE:

        return 0;
    case 

WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case 

WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        //

画x轴
        MoveToEx(hdc, 0, cyClient / 2, NULL);
        LineTo(hdc, cxClient, cyClient / 2);

        //

计算坐标
        for (int i = 0; i < NUM; i++)
        {
            apt[i].x = i * cxClient / NUM;
            apt[i].y = (int)(cyClient / 

2 * (1 - 

sin(TWOPI * i / NUM)));
        }
        //

画正弦曲线
        Polyline(hdc, apt, NUM); //调用一次且在设备驱动程序层面上,比调用1000次LineTo要快很多



        EndPaint(hwnd, &ps);
        return 0;

    case 

WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return 

DefWindowProc(hwnd, message, wParam, lParam);
}

 5.3.3 边框绘制函数

(1)边界偏差(off-by-one):Windows在“边框”内绘图,即只画右坐标与底坐 标之前的点。即不包含右坐标与底坐标的点。

第5章 绘图基础_5.1-5.4 GDI绘图

(2)图解边框绘图函数 

 第5章 绘图基础_5.1-5.4 GDI绘图

【LineDraw】

/*------------------------------------------------------------
LINEDRAW.C -- Line-Draw Demonstration Program
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI 

WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("LineDemo");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, 

IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, 

IDC_ARROW);
    wndclass.hbrBackground = 

(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("LineDraw Demo"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 

0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 

msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    static int cxClient, cyClient;
    switch 

(message)
    {
    case 

WM_CREATE:

        return 0;
    case 

WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case 

WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        //
        Rectangle(hdc, cxClient / 8, 

cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8);

        //

画对角线
        MoveToEx(hdc, 0, 0, NULL);
        LineTo(hdc, cxClient, cyClient);
        MoveToEx(hdc, cxClient, 0, NULL);
        LineTo(hdc, 0, cyClient);
        Ellipse(hdc, cxClient / 8, 

cyClient / 8, 7 * cxClient / 8, 7 * cyClient / 8);
        RoundRect(hdc, cxClient / 4, 

cyClient / 4, 3 * cxClient / 4, 3 * cyClient / 4, 

cxClient / 4, cyClient / 4);

        EndPaint(hwnd, &ps);
        return 0;

    case 

WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return 

DefWindowProc(hwnd, message, wParam, lParam);
}

5.3.4 贝塞尔样条曲线

(1)PolyBezier(hdc,apt,iCount)

   ①apt是个POINT结构的数组,前四点分别表示曲线起点、第一个控制点、 第二个控点、第二个点。随后的每一条贝塞尔样条曲线则只需要给出三个点,因为前一条贝塞尔 样条曲线的终点就是后一条的起点

②iCount=3*曲线的条数+1;

(2)PolyBezierTo函数把当前位置作为第一个起点,所以后面每画一条曲线只需再给3个点。 当函数返回时,把曲线终点设置为当前位置。

【Bezier】

/*------------------------------------------------------------
LINEDRAW.C -- Bezier Splines Demo
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI 

WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Bezier");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, 

IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, 

IDC_ARROW);
    wndclass.hbrBackground = 

(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("Bezier"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 

0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 

msg.wParam;
}
void 

DrawBezier(HDC hdc, POINT apt[])
{
    //画贝

塞尔曲线
    PolyBezier(hdc, apt, 4);
    //画控

制线
    MoveToEx(hdc, apt[0].x, apt[0].y, NULL);
    LineTo(hdc, apt[1].x, apt[1].y);
    MoveToEx(hdc, apt[3].x, apt[3].y, NULL);
    LineTo(hdc, apt[2].x, apt[2].y);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    static int cxClient, cyClient;
    static POINT apt[4];
    switch 

(message)
    {
    case 

WM_CREATE:

        return 0;
    case 

WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        apt[0].x = cxClient / 4;
        apt[0].y = cyClient / 2;
        apt[1].x = cxClient / 2;
        apt[1].y = cyClient / 4;
        apt[2].x = cxClient / 2;
        apt[2].y = 3 * cyClient / 4;
        apt[3].x = 3 * cxClient / 4;
        apt[3].y = cyClient / 2;
        return 0;
    case WM_MOUSEMOVE:     //鼠标移动时
    case  WM_LBUTTONDOWN:  //左键按下时
    case WM_RBUTTONDOWN:   //右键按下时
        if (wParam & MK_LBUTTON || wParam 

&MK_RBUTTON)
        {
            hdc = GetDC(hwnd);
            SelectObject(hdc, GetStockObject(WHITE_PEN));
            DrawBezier(hdc, apt); //用白色画刷重绘一遍,即擦除旧的曲线
            if (wParam & MK_LBUTTON)  //左键改变第1个控制点


            {
                apt[1].x = LOWORD(lParam);
                apt[1].y = HIWORD(lParam);
            }
            if (wParam & MK_RBUTTON)  

//右键改变第2个控

制点
            {
                apt[2].x = LOWORD(lParam);
                apt[2].y = HIWORD(lParam);
            }
            SelectObject(hdc, GetStockObject(BLACK_PEN));
            DrawBezier(hdc, apt);

            ReleaseDC(hwnd, hdc);
        }
        return 0;
    case 

WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        DrawBezier(hdc, apt);
        EndPaint(hwnd, &ps);
        return 0;

    case 

WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return 

DefWindowProc(hwnd, message, wParam, lParam);
}

5.3.5 使用现有画笔(备用画笔——画笔宽度总 是为1)

(1)获取画笔句柄——3种默认画笔(WHITE_PEN、BLACK_PEN、NULL_PEN),其中 NULL_PEN表示不绘任何图形。比如,画矩形时只要填充区域,不要边框时,可用NULL_PEN。

  HPEN hPen= GetStockObject(WHITE_PEN);

(2)选入设备环境:

  HPEN hOldPen = SelectObject(hdc, (HGDIOBJ)hPen);

  HPEN hOldPen = SelectObject(hdc, GetStockObject(WHITE_PEN));

5.3.6 创建、选择和删除画笔

(1)使用GDI对象的规则

   ①当GDI对象被选入一个有效的设备环境时,不要删除它

②最终应删除你所创建的所有GDI对象

③不要删除备用库里的对象

HPEN hPen = CreatePen(PS_DASH, 1, RGB(255, 0, 0));//创建画笔

HPEN hOldPen = SelectObject(hdc, (HGDIOBJ)hPen); //选入画笔

//画图代码

SelectObject(hdc, (HGDIOBJ)hOldPen);   //恢复旧画笔

DeleteObject((HGDIOBJ)hPen); //删除画笔,上一行不能省略。

(2)hPen = CreatePen(iPenStyle,iWidth,crColor);

iPenStyle参数

iWidth

crColor

 

 第5章 绘图基础_5.1-5.4 GDI绘图

①当iWidth =0时,Windows会重设为1个像素

②对于PS_SOLID、PS_NULL、PS_INSIDEFRAME,iWidth表示画笔的宽度

③如果是虚线或点线,当iWidth>1时,会用实心画笔代替。

①设备画笔颜色,是COLORREF值。

②PS_INSIDEFRAME唯一能使用混合色的画笔样式,但要iWidth>1。

(3)hPen = CreatePenIndirect(&logpen);

LOGPEN结构(LOGPEN logpen)

lopnStyle

画笔样式

lopnWidth

画笔宽度,是一个POINT结构,x字段画笔的宽度,y字段被忽略

lopnColor

画笔颜色,COLORREF值。

(4)删除未被保存句柄的设备环境中的画笔——不能删除被选入设备环境句柄的

画笔!

  ① 方法1://选入默认画笔,返回的是设备中的画笔,再删除。

    DeleteObject(SelectObject(hdc,GetStockObject(BLACK_PEN)));

  ②方法2:

    hPen = SelectObject(hdc,CreatePen(PS_DASH),0,RGB(255,0,0));//自定义画笔

    DeleteObject(SelectObject(hdc,hPen));//返回自定义画笔句柄,并删除。

(5)获取画笔

  ①hPen =GetCurrentObject(hdc,OBJ_PEN); //获得当前设备环境中的画笔句柄

  ②GetObject(hPen,sizeof(LOGPEN),(LPVOID)&logpen);//画笔属性保存logpen中。

 

5.3.7 填充空隙——点式画笔或虚线画笔之间空

隙的颜色

空隙颜色由设备环境的背景模式和背景颜色决定(注意不是窗口的背景颜色

背景模式

OPAQUE(不透明)

TRANSPARENT(透明)

SetBkMode(hdc,TRANSPARENT);

int iMode = GetBkMode (hdc);

背景颜色

COLORREF值

SetBkColor(hdc,crColor);

COLORREF crColor = GetBkColor(hdc);

5.3.8 绘图模式(默认R2_COPYPEN)

(1)线条最终显示的颜色由画笔颜色及目标区域表面的颜色共同决定。

(2)光栅操作(rasteroperation,ROP):画笔的像素颜色与目标表面像素按位布尔运算。因

画线只涉及两种像素颜色(画笔与目标),也被称为二元光栅操作(ROP2)。

(3)16种不同的ROP2运算:如R2_COPYPEN、R2_MASKNOTPEN、R2_BLACK等。

(4)获取当前绘制模式和设置绘图模式:

  ①iDrawMode =GetROP2(hdc);

  ②SetROP2(hdc,iDrawMode);

5.4 绘制填充

5.4.1 画刷

(1)备用库画刷:6种(WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、

BLACK_BRUSH和NULL_BRUSH)。

(2)使用画刷

    HBRUSHhBrush = GetStockObject(GRAY_BRUSH); //获取画刷

    SelectObject(hdc,hBrush);  //选入设备环境中

(3)应用举例——画矩形

不要边框线,只要填充

SelectObject(hdc,GetStockObject(NULL_PEN));

只绘边框,不要填充内部

SelectObject(hdc,GetStockObject(NULL_BRUSH));

5.4.2 Polygon函数和多边形填充模式

(1)Polygon和PolyPolygon函数

  ① Polygon(hdc,apt,iCount);

apt

POINT结构的数组,表示各个顶点

若最后一个点与第一个点不同,Windows会自动加1条连接这两个点的线,以形成闭合区域

iCount

顶点的个数

 

  ②PolyPolygon(hdc, ,aiCounts,iPolyCount);

apt

POINT结构的数组,表示所有的顶点

aiCounts

是个数组,每个元素表示1个多边形的顶点数。

iPolyCount

多边形的个数

  ③PolyPolygon的功能等价代码

for(int i=0,iAccum =0;i<iPolycount;i++)

{

     Polygon(hdc,apt+iAccum,aiCounts[i]); //第i个多边形;

     iAccum +=aiCounts[i];  //绘完第i个多边形后,顶点索引

后移;

}

(2)图解多边形的填充模式:ALTERNATE(交替)和WINDING(螺旋)

  SetPolyFillMode(hdc,iMode);

第5章 绘图基础_5.1-5.4 GDI绘图

第5章 绘图基础_5.1-5.4 GDI绘图

 【ALTWIND程序】
1、特征图形与填充效果图
第5章 绘图基础_5.1-5.4 GDI绘图

效果图

第5章 绘图基础_5.1-5.4 GDI绘图2、代码实现
/*------------------------------------------------------------
ALTWIND.C -- Alternate and Winding Fill Modes
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI 

WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("AltWind");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, 

IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, 

IDC_ARROW);
    wndclass.hbrBackground = 

(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("Alternate And Winding Fill Modes"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 

0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 

msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    static int cxClient, cyClient;
    static POINT aptFigure[10] = { 10, 70, 50, 70, 50, 10, 90, 10, 90, 50,
        30, 50, 30, 90, 70, 90, 70, 30, 10, 30 }; //特征图形
    POINT apt[10];
    switch 

(message)
    {
    case 

WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case 

WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        SelectObject(hdc, GetStockObject(GRAY_BRUSH));
        //

左幅图ALTERNATE模式填充
        for (int i = 0; i < 10; i++)
        {
            apt[i].x = cxClient * aptFigure[i].x / 200;
            apt[i].y = cyClient * aptFigure[i].y / 100; //按比例放大,因为apt[i].y /cylient = aptFigure[i].y / 100;
        }
        SetPolyFillMode(hdc, ALTERNATE);
        Polygon(hdc, apt, 10);
        //

右幅图ALTERNATE模式填充
        for (int i = 0; i < 10; i++)
        {
            apt[i].x += cxClient / 2;
        }
        SetPolyFillMode(hdc, WINDING);
        Polygon(hdc, apt, 10);
        EndPaint(hwnd, &ps);
        return 0;

    case 

WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return 

DefWindowProc(hwnd, message, wParam, lParam);
}

5.4.3 用画刷(8×8的小位图)填充内部

(1)创建逻辑画刷hBrush =CreateSolidBrush(crColor); //也可能是一个抖动位图

(2)阴影线标记的画刷hBrush = CreateHatchBrush(iHatchStyle,crColor);

iHatchStyle表示阴影线标记的外观

第5章 绘图基础_5.1-5.4 GDI绘图

说明:1、阴影线颜色由crColor指定。

      2、阴影线之间的空隙由背景模式和背影颜色决定,由画笔 类似。

(3)用自己的位图画刷:CreatePatternBrush和CreateDIBPatternBrushPt。

(4)包含其他4个函数功能的(更强大):hBrush = CreateBrushIndirect (&logbrush);

LOGBRUSH结构体(三个字段,lbStyle 字段的值决定Windows如何解释其他两个字段)

lbStyle(UINT)

lbColor(COLORREF)

lbHatch(LONG)

BS_SOLID

画刷的颜色

被忽略

BS_HOLLOW

被忽略

被忽略

BS_HATCHED

阴影线的颜色

阴影线画刷的样式

BS_PATTERN

被忽略

位图的句柄

BS_DIBPATTERNPT

被忽略

指向DIB的指针

(5)获取画刷信息GetObject(hBrush,sizeof(LOGBRUSH), (LPVOID)&logbrush); 

原文链接: https://www.cnblogs.com/5iedu/p/4621235.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    第5章 绘图基础_5.1-5.4 GDI绘图

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/394843

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午3:55
下一篇 2023年4月3日 下午3:56

相关推荐