[{"data":1,"prerenderedAt":1237},["ShallowReactive",2],{"doc-\u002Fdocs\u002Fapi\u002Feduwin-canvas-api":3},{"id":4,"title":5,"body":6,"description":23,"extension":1231,"meta":1232,"navigation":254,"path":1233,"seo":1234,"stem":1235,"__hash__":1236},"content\u002Fdocs\u002Fapi\u002Feduwin-canvas-api.md","eduwin 画布与精灵 API",{"type":7,"value":8,"toc":1207},"minimark",[9,13,17,34,38,63,66,69,97,114,142,144,147,190,240,319,321,324,333,339,364,366,369,378,381,390,392,395,399,414,432,435,450,472,475,500,506,530,543,602,605,620,623,626,731,733,736,743,746,796,799,829,880,883,918,960,963,998,1001,1021,1043,1046,1071,1090,1093,1109,1119,1133,1137,1140,1170,1173,1203],[10,11,5],"h1",{"id":12},"eduwin-画布与精灵-api",[14,15,16],"h2",{"id":16},"画布",[18,19,24],"pre",{"className":20,"code":21,"language":22,"meta":23,"style":23},"language-c shiki shiki-themes github-light github-dark","HWND create_canvas(HWND parent, int x, int y, int w, int h);\n","c","",[25,26,27],"code",{"__ignoreMap":23},[28,29,32],"span",{"class":30,"line":31},"line",1,[28,33,21],{},[35,36,37],"p",{},"创建一个可绘制的画布控件，使用 GDI+ 作为后端。画布维护一个离屏位图，所有绘图操作都在位图上进行，窗口重绘时自动刷新。",[39,40,41,51],"blockquote",{},[35,42,43],{},[44,45,46,47,50],"strong",{},"重要：所有绘图操作必须在 ",[25,48,49],{},"show_window"," 之后调用！",[35,52,53,55,56,59,60,62],{},[25,54,49],{}," 会触发 ",[25,57,58],{},"WM_SIZE"," 消息，画布会销毁当前离屏位图并按实际客户区尺寸重建。如果在 ",[25,61,49],{}," 之前绘图，内容会被清空。",[64,65],"hr",{},[14,67,68],{"id":68},"绘图状态设置",[18,70,72],{"className":20,"code":71,"language":22,"meta":23,"style":23},"void canvas_clear(HWND canvas, int r, int g, int b, int a);\nvoid canvas_set_pen(HWND canvas, int r, int g, int b, int a, int width);\nvoid canvas_set_brush(HWND canvas, int r, int g, int b, int a);\nvoid canvas_set_font(HWND canvas, const WCHAR *family, int size);\n",[25,73,74,79,85,91],{"__ignoreMap":23},[28,75,76],{"class":30,"line":31},[28,77,78],{},"void canvas_clear(HWND canvas, int r, int g, int b, int a);\n",[28,80,82],{"class":30,"line":81},2,[28,83,84],{},"void canvas_set_pen(HWND canvas, int r, int g, int b, int a, int width);\n",[28,86,88],{"class":30,"line":87},3,[28,89,90],{},"void canvas_set_brush(HWND canvas, int r, int g, int b, int a);\n",[28,92,94],{"class":30,"line":93},4,[28,95,96],{},"void canvas_set_font(HWND canvas, const WCHAR *family, int size);\n",[35,98,99,102,103,102,106,109,110,113],{},[25,100,101],{},"r"," \u002F ",[25,104,105],{},"g",[25,107,108],{},"b"," 为颜色的红、绿、蓝分量，范围 0–255。",[25,111,112],{},"a"," 为透明通道（alpha），0 完全透明，255 完全不透明。",[115,116,117,124,130,136],"ul",{},[118,119,120,123],"li",{},[25,121,122],{},"canvas_clear"," 用指定颜色填充整个画布（清除原有内容）",[118,125,126,129],{},[25,127,128],{},"canvas_set_pen"," 设置轮廓画笔颜色、透明度和线宽（默认黑色 1px 不透明）",[118,131,132,135],{},[25,133,134],{},"canvas_set_brush"," 设置填充画刷颜色和透明度（默认白色不透明）",[118,137,138,141],{},[25,139,140],{},"canvas_set_font"," 设置文本字体和字号（默认 Microsoft YaHei 14）",[64,143],{},[14,145,146],{"id":146},"绘图操作",[18,148,150],{"className":20,"code":149,"language":22,"meta":23,"style":23},"void canvas_draw_line(HWND canvas, int x1, int y1, int x2, int y2);\nvoid canvas_draw_rect(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_rect(HWND canvas, int x, int y, int w, int h);\nvoid canvas_draw_ellipse(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_ellipse(HWND canvas, int x, int y, int w, int h);\nvoid canvas_draw_text(HWND canvas, const WCHAR *text, int x, int y, int r, int g, int b, int a, int size);\nvoid canvas_draw_image(HWND canvas, void *image, int x, int y, int w, int h);\n",[25,151,152,157,162,167,172,178,184],{"__ignoreMap":23},[28,153,154],{"class":30,"line":31},[28,155,156],{},"void canvas_draw_line(HWND canvas, int x1, int y1, int x2, int y2);\n",[28,158,159],{"class":30,"line":81},[28,160,161],{},"void canvas_draw_rect(HWND canvas, int x, int y, int w, int h);\n",[28,163,164],{"class":30,"line":87},[28,165,166],{},"void canvas_fill_rect(HWND canvas, int x, int y, int w, int h);\n",[28,168,169],{"class":30,"line":93},[28,170,171],{},"void canvas_draw_ellipse(HWND canvas, int x, int y, int w, int h);\n",[28,173,175],{"class":30,"line":174},5,[28,176,177],{},"void canvas_fill_ellipse(HWND canvas, int x, int y, int w, int h);\n",[28,179,181],{"class":30,"line":180},6,[28,182,183],{},"void canvas_draw_text(HWND canvas, const WCHAR *text, int x, int y, int r, int g, int b, int a, int size);\n",[28,185,187],{"class":30,"line":186},7,[28,188,189],{},"void canvas_draw_image(HWND canvas, void *image, int x, int y, int w, int h);\n",[115,191,192,204,213,230],{},[118,193,194,102,197,102,200,203],{},[25,195,196],{},"canvas_draw_line",[25,198,199],{},"canvas_draw_rect",[25,201,202],{},"canvas_draw_ellipse"," — 用当前画笔绘制轮廓",[118,205,206,102,209,212],{},[25,207,208],{},"canvas_fill_rect",[25,210,211],{},"canvas_fill_ellipse"," — 用当前画刷填充",[118,214,215,218,219,222,223,226,227,229],{},[25,216,217],{},"canvas_draw_text"," — 在指定位置绘制文本，",[25,220,221],{},"r g b a"," 为文字颜色和透明度，",[25,224,225],{},"size"," 为字号（传 0 使用 ",[25,228,140],{}," 设定的字号）",[118,231,232,235,236,239],{},[25,233,234],{},"canvas_draw_image"," — 绘制由 ",[25,237,238],{},"load_image_from_file"," 返回的图像",[18,241,243],{"className":20,"code":242,"language":22,"meta":23,"style":23},"HWND cv = create_canvas(win, 10, 10, 400, 300);\n\ncanvas_clear(cv, 255, 255, 255, 255);               \u002F\u002F 白色不透明背景\n\ncanvas_set_pen(cv, 0, 0, 255, 255, 3);              \u002F\u002F 蓝色 3px 不透明画笔\ncanvas_draw_rect(cv, 20, 20, 100, 80);              \u002F\u002F 矩形边框\n\ncanvas_set_brush(cv, 255, 200, 200, 255);            \u002F\u002F 粉色不透明画刷\ncanvas_fill_ellipse(cv, 140, 20, 100, 80);          \u002F\u002F 填充椭圆\n\ncanvas_draw_line(cv, 20, 120, 300, 120);             \u002F\u002F 直线\n\ncanvas_draw_text(cv, L\"Hello 画布\", 20, 140,         \u002F\u002F 文字\n                 0, 0, 0, 255, 20);\n",[25,244,245,250,256,261,265,270,275,279,285,291,296,302,307,313],{"__ignoreMap":23},[28,246,247],{"class":30,"line":31},[28,248,249],{},"HWND cv = create_canvas(win, 10, 10, 400, 300);\n",[28,251,252],{"class":30,"line":81},[28,253,255],{"emptyLinePlaceholder":254},true,"\n",[28,257,258],{"class":30,"line":87},[28,259,260],{},"canvas_clear(cv, 255, 255, 255, 255);               \u002F\u002F 白色不透明背景\n",[28,262,263],{"class":30,"line":93},[28,264,255],{"emptyLinePlaceholder":254},[28,266,267],{"class":30,"line":174},[28,268,269],{},"canvas_set_pen(cv, 0, 0, 255, 255, 3);              \u002F\u002F 蓝色 3px 不透明画笔\n",[28,271,272],{"class":30,"line":180},[28,273,274],{},"canvas_draw_rect(cv, 20, 20, 100, 80);              \u002F\u002F 矩形边框\n",[28,276,277],{"class":30,"line":186},[28,278,255],{"emptyLinePlaceholder":254},[28,280,282],{"class":30,"line":281},8,[28,283,284],{},"canvas_set_brush(cv, 255, 200, 200, 255);            \u002F\u002F 粉色不透明画刷\n",[28,286,288],{"class":30,"line":287},9,[28,289,290],{},"canvas_fill_ellipse(cv, 140, 20, 100, 80);          \u002F\u002F 填充椭圆\n",[28,292,294],{"class":30,"line":293},10,[28,295,255],{"emptyLinePlaceholder":254},[28,297,299],{"class":30,"line":298},11,[28,300,301],{},"canvas_draw_line(cv, 20, 120, 300, 120);             \u002F\u002F 直线\n",[28,303,305],{"class":30,"line":304},12,[28,306,255],{"emptyLinePlaceholder":254},[28,308,310],{"class":30,"line":309},13,[28,311,312],{},"canvas_draw_text(cv, L\"Hello 画布\", 20, 140,         \u002F\u002F 文字\n",[28,314,316],{"class":30,"line":315},14,[28,317,318],{},"                 0, 0, 0, 255, 20);\n",[64,320],{},[14,322,323],{"id":323},"局部清除",[18,325,327],{"className":20,"code":326,"language":22,"meta":23,"style":23},"void canvas_clear_rect(HWND canvas, int x, int y, int w, int h, int r, int g, int b, int a);\n",[25,328,329],{"__ignoreMap":23},[28,330,331],{"class":30,"line":31},[28,332,326],{},[35,334,335,336,338],{},"用颜色填充指定矩形区域（局部清除），",[25,337,112],{}," 为透明度。",[18,340,342],{"className":20,"code":341,"language":22,"meta":23,"style":23},"\u002F\u002F 在灰色区域中间挖一个透明洞\ncanvas_set_brush(canvas, 200, 200, 200, 255);\ncanvas_fill_rect(canvas, 10, 10, 200, 100);\ncanvas_clear_rect(canvas, 50, 30, 100, 40, 100, 200, 50, 255);\n",[25,343,344,349,354,359],{"__ignoreMap":23},[28,345,346],{"class":30,"line":31},[28,347,348],{},"\u002F\u002F 在灰色区域中间挖一个透明洞\n",[28,350,351],{"class":30,"line":81},[28,352,353],{},"canvas_set_brush(canvas, 200, 200, 200, 255);\n",[28,355,356],{"class":30,"line":87},[28,357,358],{},"canvas_fill_rect(canvas, 10, 10, 200, 100);\n",[28,360,361],{"class":30,"line":93},[28,362,363],{},"canvas_clear_rect(canvas, 50, 30, 100, 40, 100, 200, 50, 255);\n",[64,365],{},[14,367,368],{"id":368},"文件输出",[18,370,372],{"className":20,"code":371,"language":22,"meta":23,"style":23},"void canvas_save_image(HWND canvas, const WCHAR *filepath);\n",[25,373,374],{"__ignoreMap":23},[28,375,376],{"class":30,"line":31},[28,377,371],{},[35,379,380],{},"将画布的当前内容保存为图像文件（根据扩展名自动选择编码器，支持 png\u002Fjpg\u002Fbmp 等）。",[18,382,384],{"className":20,"code":383,"language":22,"meta":23,"style":23},"canvas_save_image(cv, L\"output.png\");   \u002F\u002F 保存到文件\n",[25,385,386],{"__ignoreMap":23},[28,387,388],{"class":30,"line":31},[28,389,383],{},[64,391],{},[14,393,394],{"id":394},"游戏开发扩展",[396,397,398],"h3",{"id":398},"帧循环",[18,400,402],{"className":20,"code":401,"language":22,"meta":23,"style":23},"void canvas_start_timer(HWND canvas, int fps, void (*callback)(void *), void *data);\nvoid canvas_stop_timer(HWND canvas);\n",[25,403,404,409],{"__ignoreMap":23},[28,405,406],{"class":30,"line":31},[28,407,408],{},"void canvas_start_timer(HWND canvas, int fps, void (*callback)(void *), void *data);\n",[28,410,411],{"class":30,"line":81},[28,412,413],{},"void canvas_stop_timer(HWND canvas);\n",[35,415,416,419,420,423,424,427,428,431],{},[25,417,418],{},"canvas_start_timer"," 启动帧循环，",[25,421,422],{},"fps"," 为每秒回调次数，",[25,425,426],{},"callback"," 每帧调用一次。",[25,429,430],{},"canvas_stop_timer"," 停止帧循环。",[396,433,434],{"id":434},"键盘输入",[18,436,438],{"className":20,"code":437,"language":22,"meta":23,"style":23},"int canvas_is_key_down(HWND canvas, int vkey);\nvoid canvas_on_key(HWND canvas, void (*down)(int), void (*up)(int));\n",[25,439,440,445],{"__ignoreMap":23},[28,441,442],{"class":30,"line":31},[28,443,444],{},"int canvas_is_key_down(HWND canvas, int vkey);\n",[28,446,447],{"class":30,"line":81},[28,448,449],{},"void canvas_on_key(HWND canvas, void (*down)(int), void (*up)(int));\n",[35,451,452,455,456,459,460,463,464,467,468,471],{},[25,453,454],{},"canvas_is_key_down"," 实时检测按键是否按住，",[25,457,458],{},"vkey"," 为虚拟键码（如 ",[25,461,462],{},"VK_LEFT","、",[25,465,466],{},"'A'","）。",[25,469,470],{},"canvas_on_key"," 注册按键按下 \u002F 释放事件回调。",[396,473,474],{"id":474},"鼠标输入",[18,476,478],{"className":20,"code":477,"language":22,"meta":23,"style":23},"void canvas_on_mouse(HWND canvas, void (*move)(int, int), void (*down)(int, int, int), void (*up)(int, int, int));\nint canvas_get_mouse_x(HWND canvas);\nint canvas_get_mouse_y(HWND canvas);\nint canvas_is_mouse_down(HWND canvas, int btn);\n",[25,479,480,485,490,495],{"__ignoreMap":23},[28,481,482],{"class":30,"line":31},[28,483,484],{},"void canvas_on_mouse(HWND canvas, void (*move)(int, int), void (*down)(int, int, int), void (*up)(int, int, int));\n",[28,486,487],{"class":30,"line":81},[28,488,489],{},"int canvas_get_mouse_x(HWND canvas);\n",[28,491,492],{"class":30,"line":87},[28,493,494],{},"int canvas_get_mouse_y(HWND canvas);\n",[28,496,497],{"class":30,"line":93},[28,498,499],{},"int canvas_is_mouse_down(HWND canvas, int btn);\n",[35,501,502,505],{},[25,503,504],{},"canvas_on_mouse"," 注册鼠标移动 \u002F 按下 \u002F 释放回调。",[115,507,508,514,524],{},[118,509,510,513],{},[25,511,512],{},"move(x, y)"," — 鼠标移动",[118,515,516,519,520,523],{},[25,517,518],{},"down(btn, x, y)"," — 鼠标按下，",[25,521,522],{},"btn"," 0=左键, 1=中键, 2=右键",[118,525,526,529],{},[25,527,528],{},"up(btn, x, y)"," — 鼠标释放",[35,531,532,535,536,539,540,542],{},[25,533,534],{},"canvas_get_mouse_x\u002Fy"," 获取鼠标当前坐标。",[25,537,538],{},"canvas_is_mouse_down"," 检测鼠标是否按住（",[25,541,522],{}," 0=左, 1=右）。",[39,544,545,567],{},[35,546,547,550,551,554,555,558,559,562,563,566],{},[44,548,549],{},"坐标注意："," 画布鼠标回调中的 ",[25,552,553],{},"x, y"," 是",[44,556,557],{},"画布客户区坐标","。如果需要与 ",[25,560,561],{},"show_context_menu"," 等需要屏幕坐标的函数配合使用，必须用 ",[25,564,565],{},"ClientToScreen"," 转换：",[18,568,570],{"className":20,"code":569,"language":22,"meta":23,"style":23},"void on_mouse_down(int btn, int x, int y)\n{\n    POINT pt = {x, y};\n    ClientToScreen(canvas, &pt);\n    show_context_menu(ctx_menu, canvas, pt.x, pt.y);\n}\n",[25,571,572,577,582,587,592,597],{"__ignoreMap":23},[28,573,574],{"class":30,"line":31},[28,575,576],{},"void on_mouse_down(int btn, int x, int y)\n",[28,578,579],{"class":30,"line":81},[28,580,581],{},"{\n",[28,583,584],{"class":30,"line":87},[28,585,586],{},"    POINT pt = {x, y};\n",[28,588,589],{"class":30,"line":93},[28,590,591],{},"    ClientToScreen(canvas, &pt);\n",[28,593,594],{"class":30,"line":174},[28,595,596],{},"    show_context_menu(ctx_menu, canvas, pt.x, pt.y);\n",[28,598,599],{"class":30,"line":180},[28,600,601],{},"}\n",[396,603,604],{"id":604},"画布尺寸",[18,606,608],{"className":20,"code":607,"language":22,"meta":23,"style":23},"int canvas_get_width(HWND canvas);\nint canvas_get_height(HWND canvas);\n",[25,609,610,615],{"__ignoreMap":23},[28,611,612],{"class":30,"line":31},[28,613,614],{},"int canvas_get_width(HWND canvas);\n",[28,616,617],{"class":30,"line":81},[28,618,619],{},"int canvas_get_height(HWND canvas);\n",[35,621,622],{},"获取画布的实际像素尺寸。",[396,624,625],{"id":625},"游戏循环示例",[18,627,629],{"className":20,"code":628,"language":22,"meta":23,"style":23},"void game_tick(void *data)\n{\n    canvas_clear(canvas, 0, 0, 0, 255);\n\n    \u002F\u002F WASD 控制方块\n    static float px = 100, py = 100;\n    if (canvas_is_key_down(canvas, 'W')) py -= 3;\n    if (canvas_is_key_down(canvas, 'S')) py += 3;\n    if (canvas_is_key_down(canvas, 'A')) px -= 3;\n    if (canvas_is_key_down(canvas, 'D')) px += 3;\n\n    canvas_set_brush(canvas, 0, 200, 0, 255);\n    canvas_fill_rect(canvas, (int)px, (int)py, 40, 40);\n\n    canvas_draw_text(canvas, L\"按 ESC 退出\", 5, 5, 128, 128, 128, 255, 14);\n}\n\n\u002F\u002F 在 main 中：\ncanvas_start_timer(canvas, 60, game_tick, NULL);\ncanvas_on_key(canvas, NULL, NULL);  \u002F\u002F 确保画布获得焦点\n",[25,630,631,636,640,645,649,654,659,664,669,674,679,683,688,693,697,703,708,713,719,725],{"__ignoreMap":23},[28,632,633],{"class":30,"line":31},[28,634,635],{},"void game_tick(void *data)\n",[28,637,638],{"class":30,"line":81},[28,639,581],{},[28,641,642],{"class":30,"line":87},[28,643,644],{},"    canvas_clear(canvas, 0, 0, 0, 255);\n",[28,646,647],{"class":30,"line":93},[28,648,255],{"emptyLinePlaceholder":254},[28,650,651],{"class":30,"line":174},[28,652,653],{},"    \u002F\u002F WASD 控制方块\n",[28,655,656],{"class":30,"line":180},[28,657,658],{},"    static float px = 100, py = 100;\n",[28,660,661],{"class":30,"line":186},[28,662,663],{},"    if (canvas_is_key_down(canvas, 'W')) py -= 3;\n",[28,665,666],{"class":30,"line":281},[28,667,668],{},"    if (canvas_is_key_down(canvas, 'S')) py += 3;\n",[28,670,671],{"class":30,"line":287},[28,672,673],{},"    if (canvas_is_key_down(canvas, 'A')) px -= 3;\n",[28,675,676],{"class":30,"line":293},[28,677,678],{},"    if (canvas_is_key_down(canvas, 'D')) px += 3;\n",[28,680,681],{"class":30,"line":298},[28,682,255],{"emptyLinePlaceholder":254},[28,684,685],{"class":30,"line":304},[28,686,687],{},"    canvas_set_brush(canvas, 0, 200, 0, 255);\n",[28,689,690],{"class":30,"line":309},[28,691,692],{},"    canvas_fill_rect(canvas, (int)px, (int)py, 40, 40);\n",[28,694,695],{"class":30,"line":315},[28,696,255],{"emptyLinePlaceholder":254},[28,698,700],{"class":30,"line":699},15,[28,701,702],{},"    canvas_draw_text(canvas, L\"按 ESC 退出\", 5, 5, 128, 128, 128, 255, 14);\n",[28,704,706],{"class":30,"line":705},16,[28,707,601],{},[28,709,711],{"class":30,"line":710},17,[28,712,255],{"emptyLinePlaceholder":254},[28,714,716],{"class":30,"line":715},18,[28,717,718],{},"\u002F\u002F 在 main 中：\n",[28,720,722],{"class":30,"line":721},19,[28,723,724],{},"canvas_start_timer(canvas, 60, game_tick, NULL);\n",[28,726,728],{"class":30,"line":727},20,[28,729,730],{},"canvas_on_key(canvas, NULL, NULL);  \u002F\u002F 确保画布获得焦点\n",[64,732],{},[14,734,735],{"id":735},"精灵系统",[35,737,738,739,742],{},"精灵（Sprite）是画布上可独立控制的图形对象。所有精灵内部都统一为位图（",[25,740,741],{},"GpBitmap","）处理——形状创建时自动渲染为位图，图片从文件加载后也是位图。后续所有操作（颜色叠加、变换等）都直接作用于位图数据。",[396,744,745],{"id":745},"形状类型常量",[18,747,749],{"className":20,"code":748,"language":22,"meta":23,"style":23},"#define SHAPE_RECT      0   \u002F* 矩形 *\u002F\n#define SHAPE_ELLIPSE   1   \u002F* 椭圆\u002F圆 *\u002F\n#define SHAPE_TRIANGLE  2   \u002F* 三角形（朝上） *\u002F\n#define SHAPE_DIAMOND   3   \u002F* 菱形 *\u002F\n#define SHAPE_PENTAGON  4   \u002F* 正五边形 *\u002F\n#define SHAPE_HEXAGON   5   \u002F* 正六边形 *\u002F\n#define SHAPE_STAR      6   \u002F* 五角星 *\u002F\n#define SHAPE_ARROW     7   \u002F* 箭头（朝右） *\u002F\n#define SHAPE_PIE       8   \u002F* 扇形（缺右上角） *\u002F\n",[25,750,751,756,761,766,771,776,781,786,791],{"__ignoreMap":23},[28,752,753],{"class":30,"line":31},[28,754,755],{},"#define SHAPE_RECT      0   \u002F* 矩形 *\u002F\n",[28,757,758],{"class":30,"line":81},[28,759,760],{},"#define SHAPE_ELLIPSE   1   \u002F* 椭圆\u002F圆 *\u002F\n",[28,762,763],{"class":30,"line":87},[28,764,765],{},"#define SHAPE_TRIANGLE  2   \u002F* 三角形（朝上） *\u002F\n",[28,767,768],{"class":30,"line":93},[28,769,770],{},"#define SHAPE_DIAMOND   3   \u002F* 菱形 *\u002F\n",[28,772,773],{"class":30,"line":174},[28,774,775],{},"#define SHAPE_PENTAGON  4   \u002F* 正五边形 *\u002F\n",[28,777,778],{"class":30,"line":180},[28,779,780],{},"#define SHAPE_HEXAGON   5   \u002F* 正六边形 *\u002F\n",[28,782,783],{"class":30,"line":186},[28,784,785],{},"#define SHAPE_STAR      6   \u002F* 五角星 *\u002F\n",[28,787,788],{"class":30,"line":281},[28,789,790],{},"#define SHAPE_ARROW     7   \u002F* 箭头（朝右） *\u002F\n",[28,792,793],{"class":30,"line":287},[28,794,795],{},"#define SHAPE_PIE       8   \u002F* 扇形（缺右上角） *\u002F\n",[396,797,798],{"id":798},"精灵创建与销毁",[18,800,802],{"className":20,"code":801,"language":22,"meta":23,"style":23},"Sprite *sprite_create_from_shape(int type, int w, int h);\nSprite *sprite_create_from_file(const WCHAR *filepath);\nSprite *sprite_create_from_image(void *image);\nSprite *sprite_clone(Sprite *s);\nvoid sprite_destroy(Sprite *s);\n",[25,803,804,809,814,819,824],{"__ignoreMap":23},[28,805,806],{"class":30,"line":31},[28,807,808],{},"Sprite *sprite_create_from_shape(int type, int w, int h);\n",[28,810,811],{"class":30,"line":81},[28,812,813],{},"Sprite *sprite_create_from_file(const WCHAR *filepath);\n",[28,815,816],{"class":30,"line":87},[28,817,818],{},"Sprite *sprite_create_from_image(void *image);\n",[28,820,821],{"class":30,"line":93},[28,822,823],{},"Sprite *sprite_clone(Sprite *s);\n",[28,825,826],{"class":30,"line":174},[28,827,828],{},"void sprite_destroy(Sprite *s);\n",[115,830,831,845,851,860,874],{},[118,832,833,836,837,840,841,844],{},[25,834,835],{},"sprite_create_from_shape(type, w, h)"," 创建指定形状和大小的精灵，",[25,838,839],{},"type"," 为上述 ",[25,842,843],{},"SHAPE_*"," 常量之一",[118,846,847,850],{},[25,848,849],{},"sprite_create_from_file"," 从图片文件创建精灵，尺寸自动匹配",[118,852,853,856,857,859],{},[25,854,855],{},"sprite_create_from_image"," 从 ",[25,858,238],{}," 返回的图像创建精灵（内部克隆副本，不直接引用外部指针）",[118,861,862,865,866,869,870,873],{},[25,863,864],{},"sprite_clone"," 克隆精灵，内部共享原始图片备份（",[25,867,868],{},"origin_image","），克隆体有独立的工作副本（",[25,871,872],{},"image","）",[118,875,876,879],{},[25,877,878],{},"sprite_destroy"," 销毁精灵并释放资源",[396,881,882],{"id":882},"精灵变换",[18,884,886],{"className":20,"code":885,"language":22,"meta":23,"style":23},"void sprite_set_pos(Sprite *s, float x, float y);\nvoid sprite_move(Sprite *s, float dx, float dy);\nvoid sprite_set_scale(Sprite *s, float sx, float sy);\nvoid sprite_set_rotation(Sprite *s, float degrees);\nvoid sprite_set_color(Sprite *s, int r, int g, int b, int a);\nvoid sprite_set_image(Sprite *s, void *image);\n",[25,887,888,893,898,903,908,913],{"__ignoreMap":23},[28,889,890],{"class":30,"line":31},[28,891,892],{},"void sprite_set_pos(Sprite *s, float x, float y);\n",[28,894,895],{"class":30,"line":81},[28,896,897],{},"void sprite_move(Sprite *s, float dx, float dy);\n",[28,899,900],{"class":30,"line":87},[28,901,902],{},"void sprite_set_scale(Sprite *s, float sx, float sy);\n",[28,904,905],{"class":30,"line":93},[28,906,907],{},"void sprite_set_rotation(Sprite *s, float degrees);\n",[28,909,910],{"class":30,"line":174},[28,911,912],{},"void sprite_set_color(Sprite *s, int r, int g, int b, int a);\n",[28,914,915],{"class":30,"line":180},[28,916,917],{},"void sprite_set_image(Sprite *s, void *image);\n",[115,919,920,926,932,938,944,954],{},[118,921,922,925],{},[25,923,924],{},"sprite_set_pos"," 设置精灵左上角位置",[118,927,928,931],{},[25,929,930],{},"sprite_move"," 相对当前位置移动",[118,933,934,937],{},[25,935,936],{},"sprite_set_scale"," 缩放精灵（1.0 为原始大小）",[118,939,940,943],{},[25,941,942],{},"sprite_set_rotation"," 旋转精灵（角度制），绕原点（默认中心）旋转",[118,945,946,949,950,953],{},[25,947,948],{},"sprite_set_color"," 设置精灵的整体颜色叠加（",[44,951,952],{},"正片叠底 Multiply 混色模式","，透明像素不受影响）",[118,955,956,959],{},[25,957,958],{},"sprite_set_image"," 更换精灵的图像（内部克隆为新副本，并更新原始图备份）",[396,961,962],{"id":962},"精灵属性读取",[18,964,966],{"className":20,"code":965,"language":22,"meta":23,"style":23},"float sprite_get_x(Sprite *s);\nfloat sprite_get_y(Sprite *s);\nfloat sprite_get_w(Sprite *s);\nfloat sprite_get_h(Sprite *s);\nfloat sprite_get_sx(Sprite *s);\nfloat sprite_get_sy(Sprite *s);\n",[25,967,968,973,978,983,988,993],{"__ignoreMap":23},[28,969,970],{"class":30,"line":31},[28,971,972],{},"float sprite_get_x(Sprite *s);\n",[28,974,975],{"class":30,"line":81},[28,976,977],{},"float sprite_get_y(Sprite *s);\n",[28,979,980],{"class":30,"line":87},[28,981,982],{},"float sprite_get_w(Sprite *s);\n",[28,984,985],{"class":30,"line":93},[28,986,987],{},"float sprite_get_h(Sprite *s);\n",[28,989,990],{"class":30,"line":174},[28,991,992],{},"float sprite_get_sx(Sprite *s);\n",[28,994,995],{"class":30,"line":180},[28,996,997],{},"float sprite_get_sy(Sprite *s);\n",[396,999,1000],{"id":1000},"精灵绘制与碰撞",[18,1002,1004],{"className":20,"code":1003,"language":22,"meta":23,"style":23},"void sprite_draw(HWND canvas, Sprite *s);\nint sprite_collide(Sprite *a, Sprite *b);\nint sprite_hit_test(Sprite *s, float px, float py);\n",[25,1005,1006,1011,1016],{"__ignoreMap":23},[28,1007,1008],{"class":30,"line":31},[28,1009,1010],{},"void sprite_draw(HWND canvas, Sprite *s);\n",[28,1012,1013],{"class":30,"line":81},[28,1014,1015],{},"int sprite_collide(Sprite *a, Sprite *b);\n",[28,1017,1018],{"class":30,"line":87},[28,1019,1020],{},"int sprite_hit_test(Sprite *s, float px, float py);\n",[35,1022,1023,1026,1027,1030,1031,1034,1035,1038,1039,1042],{},[25,1024,1025],{},"sprite_draw"," 将精灵绘制到指定画布，自动处理位置、缩放、旋转。",[25,1028,1029],{},"sprite_collide"," AABB 碰撞检测，传入两个 ",[25,1032,1033],{},"Sprite*","，返回 1 表示碰撞。",[25,1036,1037],{},"sprite_hit_test"," 检测点 ",[25,1040,1041],{},"(px, py)"," 是否在精灵矩形内（含缩放）。",[396,1044,1045],{"id":1045},"精灵事件",[18,1047,1049],{"className":20,"code":1048,"language":22,"meta":23,"style":23},"void on_sprite_click(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\nvoid on_sprite_down(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\nvoid on_sprite_up(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\nvoid on_sprite_hover(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[25,1050,1051,1056,1061,1066],{"__ignoreMap":23},[28,1052,1053],{"class":30,"line":31},[28,1054,1055],{},"void on_sprite_click(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[28,1057,1058],{"class":30,"line":81},[28,1059,1060],{},"void on_sprite_down(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[28,1062,1063],{"class":30,"line":87},[28,1064,1065],{},"void on_sprite_up(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[28,1067,1068],{"class":30,"line":93},[28,1069,1070],{},"void on_sprite_hover(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[35,1072,1073,1074,1077,1078,1081,1082,1085,1086,1089],{},"为精灵注册鼠标事件回调。",[25,1075,1076],{},"click"," 在精灵上点击时触发，",[25,1079,1080],{},"down","\u002F",[25,1083,1084],{},"up"," 在精灵上按下\u002F释放鼠标时触发，",[25,1087,1088],{},"hover"," 在鼠标悬停在精灵上时持续触发。销毁精灵时自动清理事件。",[396,1091,1092],{"id":1092},"精灵颜色叠加说明",[35,1094,1095,1097,1098,1108],{},[25,1096,948],{}," 采用",[44,1099,1100,1101,1107],{},"正片叠底（Multiply）",[44,1102,1103,1104,1106],{},"混色模式，每次调用都从原始备份图（",[25,1105,868],{},"）重新克隆工作副本再执行混色，因此","多次调用不会累积加深颜色","。",[35,1110,1111,1112,1115,1116,1118],{},"颜色参数 ",[25,1113,1114],{},"r\u002Fg\u002Fb"," 控制色调，",[25,1117,112],{}," 控制强度（0=完全透明\u002F无效果，255=完全叠加）：",[115,1120,1121,1127],{},[118,1122,1123,1126],{},[25,1124,1125],{},"sprite_set_color(s, 255, 0, 0, 120)"," 叠加红色调",[118,1128,1129,1132],{},[25,1130,1131],{},"sprite_set_color(s, 255, 255, 255, 255)"," 恢复原始颜色",[396,1134,1136],{"id":1135},"sprite-结构体字段","Sprite 结构体字段",[35,1138,1139],{},"Sprite 的公有字段可以直接读写：",[18,1141,1143],{"className":20,"code":1142,"language":22,"meta":23,"style":23},"sprite->x = 100;       \u002F* 直接设坐标 *\u002F\nsprite->y = 200;\nsprite->visible = 0;   \u002F* 隐藏 *\u002F\nsprite->ox = 0;        \u002F* 原点移到左上角 *\u002F\nsprite->oy = 0;\n",[25,1144,1145,1150,1155,1160,1165],{"__ignoreMap":23},[28,1146,1147],{"class":30,"line":31},[28,1148,1149],{},"sprite->x = 100;       \u002F* 直接设坐标 *\u002F\n",[28,1151,1152],{"class":30,"line":81},[28,1153,1154],{},"sprite->y = 200;\n",[28,1156,1157],{"class":30,"line":87},[28,1158,1159],{},"sprite->visible = 0;   \u002F* 隐藏 *\u002F\n",[28,1161,1162],{"class":30,"line":93},[28,1163,1164],{},"sprite->ox = 0;        \u002F* 原点移到左上角 *\u002F\n",[28,1166,1167],{"class":30,"line":174},[28,1168,1169],{},"sprite->oy = 0;\n",[35,1171,1172],{},"内部字段（不应直接修改）：",[115,1174,1175,1180,1189],{},[118,1176,1177,1179],{},[25,1178,872],{}," — 当前绘制用的位图（工作副本）",[118,1181,1182,1184,1185,1188],{},[25,1183,868],{}," — 原始图片备份，每次 ",[25,1186,1187],{},"set_color"," 从此重新克隆",[118,1190,1191,1194,1195,1198,1199,1202],{},[25,1192,1193],{},"is_owner"," — ",[25,1196,1197],{},"1","=销毁时释放全部资源，",[25,1200,1201],{},"0","=克隆体仅释放自己的工作副本",[1204,1205,1206],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":23,"searchDepth":81,"depth":81,"links":1208},[1209,1210,1211,1212,1213,1214,1221],{"id":16,"depth":81,"text":16},{"id":68,"depth":81,"text":68},{"id":146,"depth":81,"text":146},{"id":323,"depth":81,"text":323},{"id":368,"depth":81,"text":368},{"id":394,"depth":81,"text":394,"children":1215},[1216,1217,1218,1219,1220],{"id":398,"depth":87,"text":398},{"id":434,"depth":87,"text":434},{"id":474,"depth":87,"text":474},{"id":604,"depth":87,"text":604},{"id":625,"depth":87,"text":625},{"id":735,"depth":81,"text":735,"children":1222},[1223,1224,1225,1226,1227,1228,1229,1230],{"id":745,"depth":87,"text":745},{"id":798,"depth":87,"text":798},{"id":882,"depth":87,"text":882},{"id":962,"depth":87,"text":962},{"id":1000,"depth":87,"text":1000},{"id":1045,"depth":87,"text":1045},{"id":1092,"depth":87,"text":1092},{"id":1135,"depth":87,"text":1136},"md",{},"\u002Fdocs\u002Fapi\u002Feduwin-canvas-api",{"title":5,"description":23},"docs\u002Fapi\u002Feduwin-canvas-api","DmrYjyjIFMplPF1jyjKaG4bqal7VACvBxPAZkW0llw0",1782488679847]