holyya.com
2025-09-04 14:36:34 Thursday
登录
文章检索 我的文章 写文章
C++编写的绘图软件制作教程
2023-06-23 19:50:02 深夜i     --     --
C++编程 绘图软件 制作教程 图形界面 编程实现

在当今科技发展日新月异的时代,绘图软件成为了一个必不可少的工具。但是我们也很容易遇到一些熟知的问题,例如软件兼容性、软件价格较高等问题。因此,学会编写自己的绘图软件是一件非常有用的事情,而今天我们将通过C++语言为大家提供一份绘图软件的制作教程。

第一步:准备工作

在开始编写绘图软件前,需要我们先准备好相应的开发工具。首先,我们需要安装一个C++编译器,建议使用Code::Blocks或Visual Studio。其次,我们需要安装一个绘图库,例如GDI+。最后,找到一份C++入门教程。

第二步:创建工程

打开C++编译器,选择“新建工程”,然后选择“Win32应用程序”,选择“窗口应用程序”类型,输入工程名,选择存储位置完成创建。然后我们要在工程中添加一个资源文件,用于保存图像。

第三步:编写代码

在代码中,我们需要包含一系列的头文件,例如:


#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#include <gdiplus.h>

#include <CommCtrl.h> // 用于添加滚动条

然后我们需要定义一些常量和变量:


const int WIDTH = 800;

const int HEIGHT = 600;

const int MENU_HEIGHT = 25;

const int DEFAULT_PEN_WIDTH = 5; // 默认线宽度为5

HWND hWnd;

HWND hScroll, vScroll;

HDC hdc;

HDC memHdc;

PAINTSTRUCT ps;

HBITMAP hBitmap = NULL;

Gdiplus::Status status = Gdiplus::Ok;

ULONG_PTR gdiToken;

Gdiplus::Graphics* graphics;

Gdiplus::Pen* pen;

Gdiplus::Point startPoint;

Gdiplus::Point endPoint;

bool isDrawing = false;

int penWidth = DEFAULT_PEN_WIDTH;

接着,在WinMain函数中,我们需要完成应用程序的注册和创建窗口:


// 注册应用程序

WNDCLASSEX wcex;

wcex.cbSize = sizeof (WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon (hInstance, IDI_APPLICATION);

wcex.hCursor = LoadCursor (NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

wcex.lpszMenuName = NULL;

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon (wcex.hInstance, IDI_APPLICATION);

if (!RegisterClassEx (&wcex)) {

  MessageBox (NULL, _T("注册应用程序失败"), _T("错误"), MB_ICONERROR);

  return 0;

}

// 创建窗口

hWnd = CreateWindow (szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

           CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstance, NULL);

if (!hWnd) {

  MessageBox (NULL, _T("创建窗口失败"), _T("错误"), MB_ICONERROR);

  return 0;

}

// 显示窗口

ShowWindow (hWnd, nCmdShow);

UpdateWindow (hWnd);

接下来,我们要在WinProc函数中添加所需的功能,例如:


switch (msg) {

case WM_CREATE:

  // 初始化GDI+

  Gdiplus::GdiplusStartupInput gdiplusStartupInput;

  status = Gdiplus::GdiplusStartup (&gdiToken, &gdiplusStartupInput, NULL);

  if (status != Gdiplus::Ok) {

    PostQuitMessage (1);

  }

  // 初始化滚动条,使得画布能够随鼠标移动

  hScroll = CreateWindow (L"SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE | SBS_HORZ,

              0, HEIGHT - MENU_HEIGHT, WIDTH - GetSystemMetrics (SM_CXVSCROLL), MENU_HEIGHT, hWnd,

              (HMENU) 1001, hInstance, NULL);

  vScroll = CreateWindow (L"SCROLLBAR", NULL, WS_CHILD | WS_VISIBLE | SBS_VERT,

              WIDTH - GetSystemMetrics (SM_CXVSCROLL), 0, GetSystemMetrics (SM_CXVSCROLL),

              HEIGHT - MENU_HEIGHT, hWnd, (HMENU) 1002, hInstance, NULL);

  SetScrollRange (hScroll, SB_CTL, 0, WIDTH, FALSE);

  SetScrollRange (vScroll, SB_CTL, 0, HEIGHT, FALSE);

  SetScrollPos (hScroll, SB_CTL, 0, FALSE);

  SetScrollPos (vScroll, SB_CTL, 0, FALSE);

  break;

case WM_COMMAND:

  // 处理菜单选项

  switch (LOWORD (wParam)) {

  case ID_FILE_SAVE:

    SaveBitmap ();

    break;

  case ID_COLOR_RED:

    ChangePenColor (Gdiplus::Color (255, 0, 0));

    break;

  case ID_COLOR_GREEN:

    ChangePenColor (Gdiplus::Color (0, 255, 0));

    break;

  case ID_COLOR_BLUE:

    ChangePenColor (Gdiplus::Color (0, 0, 255));

    break;

  case ID_WIDTH_1:

    ChangePenWidth (1);

    break;

  case ID_WIDTH_3:

    ChangePenWidth (3);

    break;

  case ID_WIDTH_5:

    ChangePenWidth (5);

    break;

  case ID_WIDTH_7:

    ChangePenWidth (7);

    break;

  case ID_WIDTH_9:

    ChangePenWidth (9);

    break;

  case IDM_EXIT:

    Gdiplus::GdiplusShutdown (gdiToken);

    DestroyWindow (hWnd);

    break;

  }

  break;

case WM_SIZE:

  // 调整画布大小以适应窗口

  if (graphics != NULL)

    delete graphics;

  

  if (memHdc != NULL) {

    DeleteObject (memHdc);

  }

  int w, h;

  w = LOWORD (lParam);

  h = HIWORD (lParam);

  hdc = GetDC (hWnd);

  memHdc = CreateCompatibleDC (hdc);

  hBitmap = CreateCompatibleBitmap (hdc, w, h - MENU_HEIGHT);

  SelectObject (memHdc, hBitmap);

  ReleaseDC (hWnd, hdc);

  graphics = new Gdiplus::Graphics (memHdc);

  graphics->SetSmoothingMode (Gdiplus::SmoothingMode::SmoothingModeHighQuality);

  graphics->Clear (Gdiplus::Color (255, 255, 255));

  SetScrollRange (hScroll, SB_CTL, 0, w, FALSE);

  SetScrollRange (vScroll, SB_CTL, 0, h, FALSE);

  break;

case WM_PAINT:

  // 绘制画布

  hdc = BeginPaint (hWnd, &ps);

  graphics->Flush ();

  BitBlt (hdc, 0, 0, WIDTH, HEIGHT - MENU_HEIGHT, memHdc, 0, 0, SRCCOPY);

  EndPaint (hWnd, &ps);

  break;

case WM_LBUTTONDOWN:

  // 处理鼠标按下事件

  startPoint.X = GET_X_LPARAM (lParam);

  startPoint.Y = GET_Y_LPARAM (lParam);

  isDrawing = true;

  break;

case WM_MOUSEMOVE:

  // 处理鼠标移动事件

  if (isDrawing) {

    endPoint.X = GET_X_LPARAM (lParam);

    endPoint.Y = GET_Y_LPARAM (lParam);

    graphics->DrawLine (pen, startPoint, endPoint);

    startPoint.X = endPoint.X;

    startPoint.Y = endPoint.Y;

    InvalidateRect (hWnd, NULL, FALSE);

  }

  break;

case WM_LBUTTONUP:

  // 处理鼠标松开事件

  endPoint.X = GET_X_LPARAM (lParam);

  endPoint.Y = GET_Y_LPARAM (lParam);

  graphics->DrawLine (pen, startPoint, endPoint);

  startPoint.X = 0;

  startPoint.Y = 0;

  endPoint.X = 0;

  endPoint.Y = 0;

  isDrawing = false;

  InvalidateRect (hWnd, NULL, FALSE);

  break;

case WM_VSCROLL:

  // 处理纵向滚动条事件

  switch (LOWORD (wParam)) {

  case SB_TOP:

    SetScrollPos (vScroll, SB_CTL, 0, TRUE);

    break;

  case SB_BOTTOM:

    SetScrollPos (vScroll, SB_CTL, HEIGHT - MENU_HEIGHT, TRUE);

    break;

  case SB_LINEUP:

    SetScrollPos (vScroll, SB_CTL, GetScrollPos (vScroll, SB_CTL) - 10, TRUE);

    break;

  case SB_LINEDOWN:

    SetScrollPos (vScroll, SB_CTL, GetScrollPos (vScroll, SB_CTL) + 10, TRUE);

    break;

  case SB_PAGEUP:

    SetScrollPos (vScroll, SB_CTL, GetScrollPos (vScroll, SB_CTL) - HEIGHT / 2, TRUE);

    break;

  case SB_PAGEDOWN:

    SetScrollPos (vScroll, SB_CTL, GetScrollPos (vScroll, SB_CTL) + HEIGHT / 2, TRUE);

    break;

  case SB_THUMBPOSITION:

  case SB_THUMBTRACK:

    SetScrollPos (vScroll, SB_CTL, HIWORD (wParam), TRUE);

    break;

  }

  ScrollWindowEx (hWnd, 0, GetScrollPos (vScroll, SB_CTL) - HIWORD (wParam), NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE);

  break;

case WM_HSCROLL:

  // 处理横向滚动条事件

  switch (LOWORD (wParam)) {

  case SB_LEFT:

    SetScrollPos (hScroll, SB_CTL, 0, TRUE);

    break;

  case SB_RIGHT:

    SetScrollPos (hScroll, SB_CTL, WIDTH, TRUE);

    break;

  case SB_LINELEFT:

    SetScrollPos (hScroll, SB_CTL, GetScrollPos (hScroll, SB_CTL) - 10, TRUE);

    break;

  case SB_LINERIGHT:

    SetScrollPos (hScroll, SB_CTL, GetScrollPos (hScroll, SB_CTL) + 10, TRUE);

    break;

  case SB_PAGELEFT:

    SetScrollPos (hScroll, SB_CTL, GetScrollPos (hScroll, SB_CTL) - WIDTH / 2, TRUE);

    break;

  case SB_PAGERIGHT:

    SetScrollPos (hScroll, SB_CTL, GetScrollPos (hScroll, SB_CTL) + WIDTH / 2, TRUE);

    break;

  case SB_THUMBPOSITION:

  case SB_THUMBTRACK:

    SetScrollPos (hScroll, SB_CTL, HIWORD (wParam), TRUE);

    break;

  }

  ScrollWindowEx (hWnd, GetScrollPos (hScroll, SB_CTL) - HIWORD (wParam), 0, NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE);

  break;

case WM_DESTROY:

  // 窗口销毁消息

  PostQuitMessage (0);

  break;

default:

  return DefWindowProc (hWnd, msg, wParam, lParam);

}

return 0;

最后,我们需要实现一些常用功能,例如更改画笔颜色、更改画笔线宽、保存图像等:


void SaveBitmap () {

  WCHAR filePath[MAX_PATH];

  OPENFILENAME ofn;

  ZeroMemory (&ofn, sizeof (ofn));

  ofn.lStructSize = sizeof (OPENFILENAME);

  ofn.hwndOwner = hWnd;

  ofn.lpstrFilter = L"Bitmap(*.bmp)\0*.bmp\0";

  ofn.lpstrFile = filePath;

  ofn.lpstrDefExt = L"bmp";

  ofn.nMaxFile = MAX_PATH;

  ofn.Flags = OFN_OVERWRITEPROMPT;

  if (GetSaveFileName (&ofn)) {

    CLSID clsid;

    GetEncoderClsid (L"image/bmp", &clsid);

    graphics->Flush ();

    Gdiplus::Bitmap bmp (hBitmap, NULL);

    bmp.Save (filePath, &clsid, NULL);

  }

}

void ChangePenColor (Gdiplus::Color color) {

  delete pen;

  pen = new Gdiplus::Pen (color, penWidth);

}

void ChangePenWidth (int width) {

  penWidth = width;

  delete pen;

  pen = new Gdiplus::Pen (Gdiplus::Color (0, 0, 0), penWidth);

}

BOOL GetEncoderClsid (const WCHAR* format, CLSID* pClsid) {

  UINT num = 0;

  UINT size = 0;

  Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

  Gdiplus::GetImageEncodersSize (&num, &size);

  if (size == 0) return FALSE;

  pImageCodecInfo = (Gdiplus::ImageCodecInfo*) (malloc (size));

  if (pImageCodecInfo == NULL) return FALSE;

  Gdiplus::GetImageEncoders (num, size, pImageCodecInfo);

  for (UINT i = 0; i < num; ++i) {

    if (wcscmp (pImageCodecInfo[i].MimeType, format) == 0) {

      *pClsid = pImageCodecInfo[i].Clsid;

      free (pImageCodecInfo);

      return TRUE;

    }

  }

  free (pImageCodecInfo);

  return FALSE;

}

这份代码只是一个简单的绘图软件,但是它能够满足基本的需求。如果你想要实现更多功能,可以添加更多代码逻辑。希望这个C++编写的绘图软件制作教程对你有帮助。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复