eduwin 画布与精灵 API
画布
HWND create_canvas(HWND parent, int x, int y, int w, int h);
创建一个可绘制的画布控件,使用 GDI+ 作为后端。画布维护一个离屏位图,所有绘图操作都在位图上进行,窗口重绘时自动刷新。
重要:所有绘图操作必须在
show_window之后调用!
show_window会触发WM_SIZE消息,画布会销毁当前离屏位图并按实际客户区尺寸重建。如果在show_window之前绘图,内容会被清空。
绘图状态设置
void canvas_clear(HWND canvas, int r, int g, int b, int a);
void canvas_set_pen(HWND canvas, int r, int g, int b, int a, int width);
void canvas_set_brush(HWND canvas, int r, int g, int b, int a);
void canvas_set_font(HWND canvas, const WCHAR *family, int size);
r / g / b 为颜色的红、绿、蓝分量,范围 0–255。a 为透明通道(alpha),0 完全透明,255 完全不透明。
canvas_clear用指定颜色填充整个画布(清除原有内容)canvas_set_pen设置轮廓画笔颜色、透明度和线宽(默认黑色 1px 不透明)canvas_set_brush设置填充画刷颜色和透明度(默认白色不透明)canvas_set_font设置文本字体和字号(默认 Microsoft YaHei 14)
绘图操作
void canvas_draw_line(HWND canvas, int x1, int y1, int x2, int y2);
void canvas_draw_rect(HWND canvas, int x, int y, int w, int h);
void canvas_fill_rect(HWND canvas, int x, int y, int w, int h);
void canvas_draw_ellipse(HWND canvas, int x, int y, int w, int h);
void canvas_fill_ellipse(HWND canvas, int x, int y, int w, int h);
void canvas_draw_text(HWND canvas, const WCHAR *text, int x, int y, int r, int g, int b, int a, int size);
void canvas_draw_image(HWND canvas, void *image, int x, int y, int w, int h);
canvas_draw_line/canvas_draw_rect/canvas_draw_ellipse— 用当前画笔绘制轮廓canvas_fill_rect/canvas_fill_ellipse— 用当前画刷填充canvas_draw_text— 在指定位置绘制文本,r g b a为文字颜色和透明度,size为字号(传 0 使用canvas_set_font设定的字号)canvas_draw_image— 绘制由load_image_from_file返回的图像
HWND cv = create_canvas(win, 10, 10, 400, 300);
canvas_clear(cv, 255, 255, 255, 255); // 白色不透明背景
canvas_set_pen(cv, 0, 0, 255, 255, 3); // 蓝色 3px 不透明画笔
canvas_draw_rect(cv, 20, 20, 100, 80); // 矩形边框
canvas_set_brush(cv, 255, 200, 200, 255); // 粉色不透明画刷
canvas_fill_ellipse(cv, 140, 20, 100, 80); // 填充椭圆
canvas_draw_line(cv, 20, 120, 300, 120); // 直线
canvas_draw_text(cv, L"Hello 画布", 20, 140, // 文字
0, 0, 0, 255, 20);
局部清除
void canvas_clear_rect(HWND canvas, int x, int y, int w, int h, int r, int g, int b, int a);
用颜色填充指定矩形区域(局部清除),a 为透明度。
// 在灰色区域中间挖一个透明洞
canvas_set_brush(canvas, 200, 200, 200, 255);
canvas_fill_rect(canvas, 10, 10, 200, 100);
canvas_clear_rect(canvas, 50, 30, 100, 40, 100, 200, 50, 255);
文件输出
void canvas_save_image(HWND canvas, const WCHAR *filepath);
将画布的当前内容保存为图像文件(根据扩展名自动选择编码器,支持 png/jpg/bmp 等)。
canvas_save_image(cv, L"output.png"); // 保存到文件
游戏开发扩展
帧循环
void canvas_start_timer(HWND canvas, int fps, void (*callback)(void *), void *data);
void canvas_stop_timer(HWND canvas);
canvas_start_timer 启动帧循环,fps 为每秒回调次数,callback 每帧调用一次。canvas_stop_timer 停止帧循环。
键盘输入
int canvas_is_key_down(HWND canvas, int vkey);
void canvas_on_key(HWND canvas, void (*down)(int), void (*up)(int));
canvas_is_key_down 实时检测按键是否按住,vkey 为虚拟键码(如 VK_LEFT、'A')。canvas_on_key 注册按键按下 / 释放事件回调。
鼠标输入
void canvas_on_mouse(HWND canvas, void (*move)(int, int), void (*down)(int, int, int), void (*up)(int, int, int));
int canvas_get_mouse_x(HWND canvas);
int canvas_get_mouse_y(HWND canvas);
int canvas_is_mouse_down(HWND canvas, int btn);
canvas_on_mouse 注册鼠标移动 / 按下 / 释放回调。
move(x, y)— 鼠标移动down(btn, x, y)— 鼠标按下,btn0=左键, 1=中键, 2=右键up(btn, x, y)— 鼠标释放
canvas_get_mouse_x/y 获取鼠标当前坐标。canvas_is_mouse_down 检测鼠标是否按住(btn 0=左, 1=右)。
坐标注意: 画布鼠标回调中的
x, y是画布客户区坐标。如果需要与show_context_menu等需要屏幕坐标的函数配合使用,必须用ClientToScreen转换:void on_mouse_down(int btn, int x, int y) { POINT pt = {x, y}; ClientToScreen(canvas, &pt); show_context_menu(ctx_menu, canvas, pt.x, pt.y); }
画布尺寸
int canvas_get_width(HWND canvas);
int canvas_get_height(HWND canvas);
获取画布的实际像素尺寸。
游戏循环示例
void game_tick(void *data)
{
canvas_clear(canvas, 0, 0, 0, 255);
// WASD 控制方块
static float px = 100, py = 100;
if (canvas_is_key_down(canvas, 'W')) py -= 3;
if (canvas_is_key_down(canvas, 'S')) py += 3;
if (canvas_is_key_down(canvas, 'A')) px -= 3;
if (canvas_is_key_down(canvas, 'D')) px += 3;
canvas_set_brush(canvas, 0, 200, 0, 255);
canvas_fill_rect(canvas, (int)px, (int)py, 40, 40);
canvas_draw_text(canvas, L"按 ESC 退出", 5, 5, 128, 128, 128, 255, 14);
}
// 在 main 中:
canvas_start_timer(canvas, 60, game_tick, NULL);
canvas_on_key(canvas, NULL, NULL); // 确保画布获得焦点
精灵系统
精灵(Sprite)是画布上可独立控制的图形对象。所有精灵内部都统一为位图(GpBitmap)处理——形状创建时自动渲染为位图,图片从文件加载后也是位图。后续所有操作(颜色叠加、变换等)都直接作用于位图数据。
形状类型常量
#define SHAPE_RECT 0 /* 矩形 */
#define SHAPE_ELLIPSE 1 /* 椭圆/圆 */
#define SHAPE_TRIANGLE 2 /* 三角形(朝上) */
#define SHAPE_DIAMOND 3 /* 菱形 */
#define SHAPE_PENTAGON 4 /* 正五边形 */
#define SHAPE_HEXAGON 5 /* 正六边形 */
#define SHAPE_STAR 6 /* 五角星 */
#define SHAPE_ARROW 7 /* 箭头(朝右) */
#define SHAPE_PIE 8 /* 扇形(缺右上角) */
精灵创建与销毁
Sprite *sprite_create_from_shape(int type, int w, int h);
Sprite *sprite_create_from_file(const WCHAR *filepath);
Sprite *sprite_create_from_image(void *image);
Sprite *sprite_clone(Sprite *s);
void sprite_destroy(Sprite *s);
sprite_create_from_shape(type, w, h)创建指定形状和大小的精灵,type为上述SHAPE_*常量之一sprite_create_from_file从图片文件创建精灵,尺寸自动匹配sprite_create_from_image从load_image_from_file返回的图像创建精灵(内部克隆副本,不直接引用外部指针)sprite_clone克隆精灵,内部共享原始图片备份(origin_image),克隆体有独立的工作副本(image)sprite_destroy销毁精灵并释放资源
精灵变换
void sprite_set_pos(Sprite *s, float x, float y);
void sprite_move(Sprite *s, float dx, float dy);
void sprite_set_scale(Sprite *s, float sx, float sy);
void sprite_set_rotation(Sprite *s, float degrees);
void sprite_set_color(Sprite *s, int r, int g, int b, int a);
void sprite_set_image(Sprite *s, void *image);
sprite_set_pos设置精灵左上角位置sprite_move相对当前位置移动sprite_set_scale缩放精灵(1.0 为原始大小)sprite_set_rotation旋转精灵(角度制),绕原点(默认中心)旋转sprite_set_color设置精灵的整体颜色叠加(正片叠底 Multiply 混色模式,透明像素不受影响)sprite_set_image更换精灵的图像(内部克隆为新副本,并更新原始图备份)
精灵属性读取
float sprite_get_x(Sprite *s);
float sprite_get_y(Sprite *s);
float sprite_get_w(Sprite *s);
float sprite_get_h(Sprite *s);
float sprite_get_sx(Sprite *s);
float sprite_get_sy(Sprite *s);
精灵绘制与碰撞
void sprite_draw(HWND canvas, Sprite *s);
int sprite_collide(Sprite *a, Sprite *b);
int sprite_hit_test(Sprite *s, float px, float py);
sprite_draw 将精灵绘制到指定画布,自动处理位置、缩放、旋转。sprite_collide AABB 碰撞检测,传入两个 Sprite*,返回 1 表示碰撞。sprite_hit_test 检测点 (px, py) 是否在精灵矩形内(含缩放)。
精灵事件
void on_sprite_click(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);
void on_sprite_down(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);
void on_sprite_up(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);
void on_sprite_hover(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);
为精灵注册鼠标事件回调。click 在精灵上点击时触发,down/up 在精灵上按下/释放鼠标时触发,hover 在鼠标悬停在精灵上时持续触发。销毁精灵时自动清理事件。
精灵颜色叠加说明
sprite_set_color 采用正片叠底(Multiply)混色模式,每次调用都从原始备份图(origin_image)重新克隆工作副本再执行混色,因此多次调用不会累积加深颜色。
颜色参数 r/g/b 控制色调,a 控制强度(0=完全透明/无效果,255=完全叠加):
sprite_set_color(s, 255, 0, 0, 120)叠加红色调sprite_set_color(s, 255, 255, 255, 255)恢复原始颜色
Sprite 结构体字段
Sprite 的公有字段可以直接读写:
sprite->x = 100; /* 直接设坐标 */
sprite->y = 200;
sprite->visible = 0; /* 隐藏 */
sprite->ox = 0; /* 原点移到左上角 */
sprite->oy = 0;
内部字段(不应直接修改):
image— 当前绘制用的位图(工作副本)origin_image— 原始图片备份,每次set_color从此重新克隆is_owner—1=销毁时释放全部资源,0=克隆体仅释放自己的工作副本