windows编程之GDI绘图 – 实验3-1

1.实验名称

实验3-1 GDI绘图实验


2.实验目的

  1. 理解设备环境在绘图中的作用

  2. 掌握绘图工具的创建,理解绘图工具和设备环境之间的关系

  3. 掌握绘图步骤,掌握绘图函数的使用


3.实验的步骤和方法

  1. 将实验二中的窗口代码修改,在窗口处理函数中添加绘图代码:响应WM_PAINT消息,在其中按照绘图步骤,用BeginPaint方法获取设备环境句柄,创建彩色的、具有某种样式的画笔和画刷,选入设备环境,在窗口上画椭圆、矩形等图形,这时创建并选入设备环境的画笔、画刷将作用在绘制图形上,最后删除画笔画刷、用EndPaint释放设备环境。调试运行,观察程序效果。

  2. 在第1题的基础上,修改代码,在键盘按键消息(WM_KEYDOWN)中,使用GetDC/ReleaseDC方法获取/释放设备环境,实现原来的画图操作。编译运行,把窗口拖动、最小化之后再还原窗口,看看出现什么问题,结合大课的ppt考虑原因。

  3. 用GDI绘制一个游戏界面,如下图所示。其中,左边绿色部分,是游戏控制面板,上面有两个按钮或文本框,记录玩家姓名、分数等,右边是个类似棋盘的游戏内容面板,其中分布了很多条线,构成棋盘的样子。窗口不可以最大化、不可以调整大小。注意:游戏面板中的多条线段,可以循环多次绘制;窗口的类型,可以查询MSDN中CreateWindow的style参数。


4.代码实现

#pragma comment(lib, "User32.lib")
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "Gdi32.lib")

#include <windows.h>
#include <wingdi.h>

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
  static TCHAR szAppName[] = TEXT("NueXini");
  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("Error"), szAppName, MB_ICONERROR | MB_OK);
    return 0;
  }

  hWnd = CreateWindow(
      szAppName,
      szAppName,
      WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, // 状态条 包括标题栏 最小化按钮 菜单
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      750, // 窗口大小 width
      700, // 窗口大小 height
      NULL,
      NULL,
      hInstance,
      NULL);
  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;
  RECT rect;

  HGDIOBJ hPen, hOldPen, hBrush, hOldBruch;

  switch (message)
  {
          
  case WM_KEYDOWN:
    hdc = GetDC(hwnd);
    GetClientRect(hwnd, &rect);

    TextOut(hdc, 0, 10, _text, strlen(_text));

    ReleaseDC(hwnd, hdc);
    return 0;

  case WM_SIZE:
    GetClientRect(hwnd, &rect);
    return 0;

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

    // 外边框
    hPen = CreatePen(PS_SOLID, 5, RGB(142, 202, 142));
    hOldPen = SelectObject(hdc, hPen);
    // 矩形内部
    hBrush = CreateSolidBrush(RGB(90, 150, 90));
    hOldBruch = SelectObject(hdc, hBrush);

    // 绘制绿色圆角矩形
    RoundRect(hdc, 40, 50, 200, 600, 10, 20);
    // 释放
    DeleteObject(hOldBruch);
    DeleteObject(hOldPen);

    // 外边框
    hPen = CreatePen(PS_SOLID, 3, RGB(180, 118, 119));
    hOldPen = SelectObject(hdc, hPen);
    // 矩形内部
    hBrush = CreateSolidBrush(RGB(160, 100, 100));
    hOldBruch = SelectObject(hdc, hBrush);
    // 绘制两个红色矩形在绿色矩形内部
    RoundRect(hdc, 50, 450, 190, 480, 10, 20);
    RoundRect(hdc, 50, 500, 190, 530, 10, 20);
    // 释放
    DeleteObject(hOldBruch);
    DeleteObject(hOldPen);

    // 绘制右边那个棋盘
    hPen = CreatePen(PS_SOLID, 3, RGB(255, 177, 177));
    hOldPen = SelectObject(hdc, hPen);
    hBrush = CreateSolidBrush(RGB(255, 130, 130));
    hOldBruch = SelectObject(hdc, hBrush);
    RoundRect(hdc, 210, 50, 700, 600, 10, 20);
    DeleteObject(hOldBruch);
    DeleteObject(hOldPen);

    // 绘制竖线
    hPen = GetStockObject(BLACK_PEN);
    hOldPen = SelectObject(hdc, hPen);
    for (size_t i = 1; i < 14; i++)
    {
      MoveToEx(hdc, 210 + (i * 35), 50, NULL);
      LineTo(hdc, 210 + (i * 35), 600);
    }

    // 绘制横线
    for (size_t i = 1; i < 15; i++)
    {
      MoveToEx(hdc, 210, 50 + (i * 37), NULL);
      LineTo(hdc, 700, 50 + (i * 37));
    }
    // 释放
    DeleteObject(hOldPen);

    EndPaint(hwnd, &ps);
    return 0;

  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  }
  return DefWindowProc(hwnd, message, wParam, lParam);
}


5.思考题

查询MSDN,学习Polygon函数的使用,画出黄色五角星,然后尝试画五星红旗。注意观察五星红旗的样子,其中每个五角星每个角都严格相同,每个小五角星都朝向大五角星,所以在你的程序中应该有严格的计算来控制大小、位置,而不是随意设置一些值。

  • 代码实现
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "Gdi32.lib")

#include <windows.h>
#include <wingdi.h>
#include <cmath>

#define PI 3.1415926

void CalculateStarPosition(POINT *stars, size_t nCount, size_t r, size_t angle, POINT BigStar);

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
  static TCHAR szAppName[] = TEXT("NueXini");
  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("Error"), szAppName, MB_ICONERROR | MB_OK);
    return 0;
  }

  hWnd = CreateWindow(
      szAppName,
      szAppName,
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      NULL,
      NULL,
      hInstance,
      NULL);
  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;
  RECT rect;

  HGDIOBJ hPen, hOldPen, hBrush, hOldBrush;

  // 把左上角矩形宽分为15份,高分为10份
  size_t flagWidth = 450 / 15;
  size_t flagHeight = 300 / 10;
  POINT stars[5] = {0};

  // 第1颗
  POINT BigStar1 = {(LONG)flagWidth * 5, (LONG)flagHeight * 5};
  CalculateStarPosition(stars, 5, flagHeight * 3, 90, BigStar1);
  POINT star1[5] = {stars[0], stars[2], stars[4], stars[1], stars[3]};

  // 第2颗
  POINT BigStar2 = {(LONG)flagWidth * 10, (LONG)flagHeight * 2};
  CalculateStarPosition(stars, 5, flagHeight, 70, BigStar2);
  POINT star2[5] = {stars[0], stars[2], stars[4], stars[1], stars[3]};

  // 第3颗
  POINT BigStar3 = {(LONG)flagWidth * 12, (LONG)flagHeight * 4};
  CalculateStarPosition(stars, 5, flagHeight, 120, BigStar3);
  POINT star3[5] = {stars[0], stars[2], stars[4], stars[1], stars[3]};

  // 第4颗
  POINT BigStar4 = {(LONG)flagWidth * 12, (LONG)flagHeight * 7};
  CalculateStarPosition(stars, 5, flagHeight, 90, BigStar4);
  POINT star4[5] = {stars[0], stars[2], stars[4], stars[1], stars[3]};

  // 第5颗
  POINT BigStar5 = {(LONG)flagWidth * 10, (LONG)flagHeight * 9};
  CalculateStarPosition(stars, 5, flagHeight, 70, BigStar5);
  POINT star5[5] = {stars[0], stars[2], stars[4], stars[1], stars[3]};

  switch (message)
  {

  case WM_SIZE:
    GetClientRect(hwnd, &rect);
    return 0;

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

    // 红色画刷
    hBrush = CreateSolidBrush(RGB(255, 0, 0));
    hOldBrush = SelectObject(hdc, hBrush);
    // 创建3:2矩形
    Rectangle(hdc, 0, 0, 900, 600);
    DeleteObject(hOldBrush);
    // 把矩形分为四个相等的矩形
    MoveToEx(hdc, 0, 300, nullptr);
    LineTo(hdc, 900, 300);

    MoveToEx(hdc, 450, 0, nullptr);
    LineTo(hdc, 450, 600);

    // 绘制横线
    for (size_t i = 0; i < 10; i++)
    {
      MoveToEx(hdc, 0, flagHeight * i, nullptr);
      LineTo(hdc, 450, flagHeight * i);
    }
    // 绘制竖线
    for (size_t i = 0; i < 15; i++)
    {
      MoveToEx(hdc, flagWidth * i, 0, nullptr);
      LineTo(hdc, flagWidth * i, 300);
    }

    // 绘制
    hBrush = CreateSolidBrush(RGB(255, 255, 0));
    hOldBrush = SelectObject(hdc, hOldBrush);
    hPen = CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
    hOldPen = SelectObject(hdc, hPen);
    SetPolyFillMode(hdc, WINDING);

    Polygon(hdc, star1, 5);
    Polygon(hdc, star2, 5);
    Polygon(hdc, star3, 5);
    Polygon(hdc, star4, 5);
    Polygon(hdc, star5, 5);

    DeleteObject(hOldPen);
    DeleteObject(hOldBrush);

    EndPaint(hwnd, &ps);
    return 0;

  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  }
  return DefWindowProc(hwnd, message, wParam, lParam);
}

void CalculateStarPosition(POINT *stars, size_t nCount, size_t r, size_t angle, POINT BigStar)
{
  for (size_t i = 0; i < nCount; i++)

  {
    stars[i].x = BigStar.x + (LONG)(r * cos((angle + i * 72) * PI / 180));
    stars[i].y = BigStar.y - (LONG)(r * sin((angle + i * 72) * PI / 180));
  }
}

6.五星红旗绘制思路

五星之位置与画法如下:

(1)为便于确定五星之位置,先将旗面对分为四个相等的长方形,将左上方之长方形上下划为十等分,左右划为十五等分。

(2)大五角星的中心点,在该长方形上五下五、左五右十之处。其画法为:以此点为圆心,以三等分为半径作一圆。在此圆周上,定出五个等距离的点,其一点须位于圆之正上方。然后将此五点中各相隔的两点相联,使各成一直线。此五直线所构成之外轮廓线,即为所需之大五角星。五角星之一个角尖正向

(3)四颗小五角星的中心点,第一点在该长方形上二下八、左十右五之处,第二点在上四下六、左十二右三之处,第三点在上七下三、左十二右三之处,第四点在上九下一、左十右五之处。其画法为:以以上四点为圆心,各以一等分为半径,分别作四个圆。

在每个圆上各定出五个等距离的点,其中均须各有一点位于大五角星中心点与以上四个圆心的各联结线上。然后用构成大五角星的同样方法,构成小五角星。此四颗小五角星均各有一个角尖正对大五角星的中心点。

   // 把左上角矩形宽分为15份,高分为10份
  size_t flagWidth = 450 / 15;
  size_t flagHeight = 300 / 15;
  // 定位 大五角星圆心
  POINT BigStar = {(LONG)flagWidth * 5, (LONG)flagHeight * 5};
  size_t r = flagHeight * 3;

  // 第1个点90度
  POINT star1;
  star1.x = BigStar.x + (r * cos(90 * PI / 180));
  star1.y = BigStar.y - (r * sin(90 * PI / 180));
  // 第2个点162度
  POINT star2;
  star2.x = BigStar.x + (r * cos(162 * PI / 180));
  star2.y = BigStar.y - (r * sin(162 * PI / 180));
  // 第3个点234度
  POINT star3;
  star3.x = BigStar.x + (r * cos(234 * PI / 180));
  star3.y = BigStar.y - (r * sin(234 * PI / 180));
  // 第4个点306度
  POINT star4;
  star4.x = BigStar.x + (r * cos(306 * PI / 180));
  star4.y = BigStar.y - (r * sin(306 * PI / 180));
  // 第5个点378度
  POINT star5;
  star5.x = BigStar.x + (r * cos(378 * PI / 180));
  star5.y = BigStar.y - (r * sin(378 * PI / 180));

  POINT stars[5] = {star1, star3, star5, star2, star4};
  • 总结方法
void CalculateStarPosition(POINT *stars, size_t nCount, size_t r, size_t angle, POINT BigStar)
{
  #define PI 3.1415926
  for (size_t i = 0; i < nCount; i++)

  {
    stars[i].x = BigStar.x + (LONG)(r * cos((angle + i * 72) * PI / 180));
    stars[i].y = BigStar.y - (LONG)(r * sin((angle + i * 72) * PI / 180));
  }
}

7.不知道这个是不是BUG

明明把画刷颜色设置成了黄色,但就是不生效,直接变成了白色,不知道怎么解决这个


enjoy it ~

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐