下面我将为你介绍三种主流的方法,从最推荐到最基础的,并附上完整的代码示例。
使用 graphics.h 库 (简单易学,适合初学者)
graphics.h 是一个古老的、非标准的图形库,常见于早期的Turbo C/C++编译器(如 conio.h 和 dos.h 一样),虽然它已经过时,但对于学习基本图形绘制概念非常直观,很多学校的C语言课程仍在使用它。
特点:
- 优点:API简单,代码量少,容易上手。
- 缺点:平台兼容性差,只能在Windows的特定环境下编译(如
BGI库),不适合现代软件开发。
示例代码:绘制三次贝塞尔曲线
#include <graphics.h>
#include <conio.h> // 用于 _getch()
int main() {
// 初始化图形窗口,"C:\\TC\\BGI" 是你的BGI库路径,请根据实际情况修改
int gd = DETECT, gm;
initgraph(&gd, &gm, "C:\\TC\\BGI");
// 定义三次贝塞尔曲线的四个控制点
// (x1, y1) 起始点
// (x2, y2) 控制点1
// (x3, y3) 控制点2
// (x4, y4) 结束点
int x1 = 50, y1 = 200;
int x2 = 150, y2 = 50;
int x3 = 250, y3 = 50;
int x4 = 350, y4 = 200;
// 绘制控制点(用小圆圈表示)
setcolor(YELLOW);
circle(x1, y1, 5);
circle(x2, y2, 5);
circle(x3, y3, 5);
circle(x4, y4, 5);
// 绘制控制点之间的辅助线(虚线)
setcolor(WHITE);
setlinestyle(DOTTED_LINE, 0, 1);
line(x1, y1, x2, y2);
line(x2, y2, x3, y3);
line(x3, y3, x4, y4);
// 绘制实线样式的贝塞尔曲线
setcolor(GREEN);
setlinestyle(SOLID_LINE, 0, 3); // 设置线宽为3
drawbezier(x1, y1, x2, y2, x3, y3, x4, y4);
// 在窗口显示文字
outtextxy(150, 250, "Bezier Curve Example");
// 按任意键关闭图形窗口
_getch();
closegraph();
return 0;
}
如何编译和运行 (以 Dev-C++ 为例):
- 下载
graphics.h和winbgim.h库文件,并将它们放到你的编译器include目录下。 - 下载
libbgi.a库文件,并将其放到你的编译器lib目录下。 - 创建一个新的项目,将上述代码粘贴进去。
- 编译并运行。
使用 Win32 API (Windows平台原生,功能强大)
如果你是在Windows平台上进行开发,使用Win32 API是更现代、更强大的选择,它不依赖于任何第三方库(除了Windows SDK本身)。
特点:
- 优点:Windows平台原生支持,性能高,功能极其丰富,是Windows桌面程序开发的基础。
- 缺点:API相对复杂,代码量比
graphics.h多,需要理解Windows的消息机制。
示例代码:在窗口中绘制贝塞尔曲线
#include <windows.h>
// 窗口过程的函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 窗口类名
const char* CLASS_NAME = "BezierWindowClass";
// 注册窗口类
WNDCLASS wc = { };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
// 创建窗口
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
"Win32 Bezier Curve Example", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 定义贝塞尔曲线的四个控制点
POINT pts[4] = {
{50, 200}, // 起始点
{150, 50}, // 控制点1
{250, 50}, // 控制点2
{350, 200} // 结束点
};
// 绘制控制点
HPEN hPenYellow = CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
HPEN hOldPen = (HPEN)SelectObject(hdc, hPenYellow);
for (int i = 0; i < 4; i++) {
MoveToEx(hdc, pts[i].x - 5, pts[i].y, NULL);
LineTo(hdc, pts[i].x + 5, pts[i].y);
MoveToEx(hdc, pts[i].x, pts[i].y - 5, NULL);
LineTo(hdc, pts[i].x, pts[i].y + 5);
}
SelectObject(hdc, hOldPen);
DeleteObject(hPenYellow);
// 绘制辅助线
HPEN hPenDotted = CreatePen(PS_DOT, 1, RGB(200, 200, 200));
hOldPen = (HPEN)SelectObject(hdc, hPenDotted);
Polyline(hdc, pts, 4);
SelectObject(hdc, hOldPen);
DeleteObject(hPenDotted);
// 绘制贝塞尔曲线
HPEN hPenGreen = CreatePen(PS_SOLID, 3, RGB(0, 200, 0));
hOldPen = (HPEN)SelectObject(hdc, hPenGreen);
PolyBezier(hdc, pts, 4);
SelectObject(hdc, hOldPen);
DeleteObject(hPenGreen);
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
如何编译和运行:
- 将代码保存为
.c文件(bezier_win32.c)。 - 使用支持Windows SDK的编译器(如Visual Studio的
cl.exe或MinGW的gcc)进行编译。- 使用Visual Studio: 创建一个 "Windows 桌面应用程序" 项目,将代码粘贴进去即可。
- 使用MinGW/GCC: 在命令行运行
gcc bezier_win32.c -o bezier_win32.exe -mwindows,然后运行生成的.exe文件。
手动实现贝塞尔曲线算法 (最底层,适合理解原理)
如果你想真正理解贝塞尔曲线是如何工作的,可以自己动手实现它的绘制算法,贝塞尔曲线的核心是德卡斯特里奥算法。
我们将使用Windows GDI的 SetPixel 函数来逐个像素点绘制。
示例代码:手动实现并绘制
#include <windows.h>
#include <math.h>
// 窗口过程
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// 使用德卡斯特里奥算法计算t时刻的贝塞尔曲线上的点
POINT CalculateBezierPoint(float t, POINT* controlPoints, int numPoints) {
POINT* tempPoints = (POINT*)malloc(numPoints * sizeof(POINT));
for (int i = 0; i < numPoints; i++) {
tempPoints[i] = controlPoints[i];
}
for (int r = 1; r < numPoints; r++) {
for (int i = 0; i < numPoints - r; i++) {
tempPoints[i].x = (1 - t) * tempPoints[i].x + t * tempPoints[i + 1].x;
tempPoints[i].y = (1 - t) * tempPoints[i].y + t * tempPoints[i + 1].y;
}
}
POINT result = tempPoints[0];
free(tempPoints);
return result;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
const char* CLASS_NAME = "ManualBezierClass";
WNDCLASS wc = { };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, CLASS_NAME, "Manual Bezier Curve",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 定义三次贝塞尔曲线的四个控制点
POINT controlPoints[4] = {
{50, 200},
{150, 50},
{250, 50},
{350, 200}
};
// 1. 绘制控制点
SetPixel(hdc, controlPoints[0].x, controlPoints[0].y, RGB(255, 255, 0));
SetPixel(hdc, controlPoints[1].x, controlPoints[1].y, RGB(255, 255, 0));
SetPixel(hdc, controlPoints[2].x, controlPoints[2].y, RGB(255, 255, 0));
SetPixel(hdc, controlPoints[3].x, controlPoints[3].y, RGB(255, 255, 0));
// 2. 绘制辅助线
HPEN hPenDotted = CreatePen(PS_DOT, 1, RGB(200, 200, 200));
HPEN hOldPen = (HPEN)SelectObject(hdc, hPenDotted);
MoveToEx(hdc, controlPoints[0].x, controlPoints[0].y, NULL);
LineTo(hdc, controlPoints[1].x, controlPoints[1].y);
LineTo(hdc, controlPoints[2].x, controlPoints[2].y);
LineTo(hdc, controlPoints[3].x, controlPoints[3].y);
SelectObject(hdc, hOldPen);
DeleteObject(hPenDotted);
// 3. 手动绘制贝塞尔曲线
SetPixel(hdc, controlPoints[0].x, controlPoints[0].y, RGB(0, 200, 0)); // 从起点开始
float step = 0.01f; // t的步长,值越小曲线越平滑
for (float t = step; t <= 1.0f; t += step) {
POINT p = CalculateBezierPoint(t, controlPoints, 4);
SetPixel(hdc, p.x, p.y, RGB(0, 200, 0));
}
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
总结与对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
graphics.h |
极其简单,代码少,适合教学 | 过时,平台兼容性差,功能有限 | C语言初学者学习图形绘制概念 |
Win32 API |
Windows原生,功能强大,性能高,专业 | API复杂,代码量多,仅限Windows | Windows桌面应用程序开发 |
| 手动实现 | 理解算法核心,不依赖任何库 | 效率较低,代码复杂,细节多 | 学习计算机图形学,面试,或需要高度自定义时 |
推荐选择:
- 如果你是初学者,只想快速画个图看看:使用
graphics.h。 - 如果你要在Windows上开发一个正式的应用程序:使用
Win32 API。 - 如果你想深入理解贝塞尔曲线的原理:手动实现算法。
