# — 玄铁渲染库编程指南
> 对标库版本: **v1.2.7**

> 底层基于 **raylib 6.0**,专注桌面 2D 渲染。本文档提供完整 API 参考、使用教程和实战示例。

---

## 快速开始

```xuantie
引 "../lib/渲染/渲染.xt" 予 R

R.初始化窗口(800, 600, "我的应用")

当 非 R.窗口应关闭?() {
    R.开始绘图()
    R.清背景(30, 30, 50, 255)
    R.绘文字("Hello 玄铁", 100, 100, 20, 255, 255, 255, 255)
    R.结束绘图()
}

R.关闭窗口()
```

> `初始化窗口` 默认开启 **MSAA 4x** 多重采样抗锯齿:圆角矩形、斜线、旋转纹理的边缘平滑无台阶;GPU 不支持时自动降级,无需任何配置。

编译方式(Go 种子或自举编译器均支持渲染链接):

```bash
# Go 种子
.\xt_gsc.exe tie .\程序.xt

# 自举编译器
.\xtc.exe tie .\程序.xt
```

---

## 窗口管理

### 创建与关闭

| API | 说明 |
|-----|------|
| `R.初始化窗口(宽, 高, "标题")` | 创建窗口,自动设置 60 FPS 目标帧率 |
| `R.窗口应关闭?()` | 返回 `布尔`,用户点击关闭按钮或按 ESC 时为真 |
| `R.关闭窗口()` | 关闭窗口并释放 OpenGL 上下文 |
| `R.窗口就绪?()` | 窗口初始化完成返回真 |

### 属性控制

| API | 说明 |
|-----|------|
| `R.设置目标帧率(fps)` | 调整帧率上限(如 30、60、120) |
| `R.取帧率()` | 返回当前实际 FPS(整) |
| `R.取帧时间()` | 返回上帧耗时 秒(小数,double) |
| `R.取屏幕宽()` | 当前屏幕宽度(整) |
| `R.取屏幕高()` | 当前屏幕高度(整) |
| `R.设置窗口标题("新标题")` | 运行时修改窗口标题,**支持 Unicode(中文/日文等)** |

### 窗口模式(运行时可随时切换)

| API | 说明 |
|-----|------|
| `R.设窗口可调整(真/假)` | 窗口边缘与角可否拖拽拉伸;默认固定大小 |
| `R.设窗口无边框(真/假)` | 去掉/恢复标题栏与边框。无边框后没有系统拖动区和关闭按钮,需自行实现(或保留 ESC 退出) |
| `R.设窗口全屏(真/假)` | 进入/退出全屏;显式语义——目标状态与当前一致时调用无效果,不会来回翻转 |
| `R.窗口全屏?()` | 当前是否处于全屏(布尔) |
| `R.设窗口尺寸(宽, 高)` | 运行时设置窗口尺寸 |
| `R.设ESC关闭(真/假)` | 按 ESC 是否请求关窗(默认真);游戏等场景可关掉防止误触退出 |

开启可调整后,界面要跟随新尺寸需每帧取最新宽高布局:手动循环里用 `R.取屏幕宽()`/`R.取屏幕高()` 传给 `UI.刷新(...)` 即可(见 12 章 UI 指南)。

---

## 绘图帧控制

每帧必须成对调用 `开始绘图` 和 `结束绘图`,所有绘制指令在二者之间。

| API | 说明 |
|-----|------|
| `R.开始绘图()` | 开始一帧,准备画布 |
| `R.结束绘图()` | 结束一帧,交换前后缓冲区 |
| `R.清背景(r, g, b, a)` | 用纯色填充背景,各通道 0–255 |

---

## 形状绘制

全部使用 RGBA 颜色(每个通道 0–255 整数)。

| API | 参数 |
|-----|------|
| `R.绘像素(x, y, r, g, b, a)` | 单点 |
| `R.绘直线(x1, y1, x2, y2, r, g, b, a)` | 线段 |
| `R.绘矩形(x, y, 宽, 高, r, g, b, a)` | 实心矩形 |
| `R.绘矩形线(x, y, 宽, 高, r, g, b, a)` | 矩形边框 |
| `R.绘圆(cx, cy, 半径, r, g, b, a)` | 实心圆 |
| `R.绘圆线(cx, cy, 半径, r, g, b, a)` | 圆形边框 |
| `R.绘椭圆(cx, cy, 水平半径, 垂直半径, r, g, b, a)` | 实心椭圆 |
| `R.绘三角(x1, y1, x2, y2, x3, y3, r, g, b, a)` | 实心三角形 |
| `R.绘多边形(cx, cy, 边数, 半径, 旋转角, r, g, b, a)` | 正多边形 |

---

## 文字渲染

### 默认字体(仅 ASCII)

| API | 说明 |
|-----|------|
| `R.绘文字("文本", x, y, 字号, r, g, b, a)` | 用 raylib 内置位图字体渲染(仅 ASCII 32–126) |
| `R.量文字("文本", 字号)` | 测量文本像素宽度(整) |
| `R.绘帧率(x, y)` | 在指定位置显示 FPS 计数器 |

### 自定义字体

**支持格式**:`.ttf` / `.otf` / `.ttc`(字体集合,取容器内**第 1 个 face**,加载失败返回 0 并回退 raylib 默认字体)。注意:苹方/冬青黑体等 CFF 格式字体 stb 无法解码,`加载系统字体` 命中时会自动兜底黑体。

#### 加载模式

| API | 纹理大小 | 适用场景 |
|-----|---------|---------|
| `R.加载系统字体("微软雅黑", 字号)` | 按需(图集缓存) | **推荐**。按系统字体名取字体,不必自己带文件;Windows 走 GDI 光栅化(灰阶抗锯齿、生僻字系统回落、按需栅格化),macOS/Linux 映射到系统自带字体文件后经 raylib 载入 |
| `R.加载字体("路径.ttf", 字号)` | ~8192×4096(全 CJK) | 需要自带字体文件/跨机器字形一致的场景。注意:字号 ≥28px 保证可读清晰度 |
| `R.加载字体精准("路径.ttf", 字号, "参考文本")` | ~256×256 | 已知渲染内容,追求极致清晰轻量 |

**系统字体模式**:用系统字体名直接取字体,无需字体文件路径。

- **Windows**:GDI `GetGlyphOutlineW` 光栅化,视觉效果与记事本等系统应用同级;只栅格化实际用到的字符并缓存进动态图集,多字号各自独立缓存。字体名如 `"微软雅黑"`/`"宋体"`/`"Consolas"`。
- **macOS**:无 GDI,改为把系统字体名映射到自带字体文件后经 raylib 载入(`苹方`/`PingFang`、`黑体`/`Heiti`、`冬青黑体`/`Hiragino`、`宋体`/`Songti`、`楷体`/`Kaiti`)。名字里带 `/` 或字体扩展名时按**路径**直用,所以 `R.加载系统字体("/某处/字体.ttc", 28)` 也成立。
- **Linux**:同样支持传路径;内置常见路径候选(文泉驿、Noto CJK)。
- 名字未命中或字体文件不存在时**回退系统 CJK 默认字体**(与 Windows 的字体回落同义);一个都取不到才返回 0(绘制回退 raylib 默认字体)。返回句柄与 `绘字体文字`/`量字体文字`/`释字体` 完全通用。

> **中文要显示出来,必须先加载字体**:`绘文字()` 走 raylib 内置位图字体,只有 ASCII(32–126),中文会显示为方块。渲染层不随包带字体(UI 库自带鸿蒙字体是 UI 层的事),所以要中文就 `加载系统字体` 或 `加载字体(路径, 字号)`。

**全范围模式**内部加载 ASCII(32–126) + CJK 统一汉字基本区(U+4E00–U+9FFF, ~21K 码点)。纹理 ~32 MB。

**精准模式**从参考文本中自动解码 UTF-8 提取所有非 ASCII 码点(去重),仅加载 ASCII + 这些码点。纹理 ~128 KB,100% 不缺字。

> 建议:中文界面优先用系统字体模式(免字体文件、系统级观感);需要自带字体或跨机器字形一致时用全范围/精准模式。

#### 使用

| API | 说明 |
|-----|------|
| `R.绘字体文字(句柄, "文本", x, y, 字号, 间距, r, g, b, a)` | 用指定字体渲染(间距通常 1–2) |
| `R.量字体文字(句柄, "文本", 字号, 间距)` | 用指定字体精确测量文字宽度(布局尺寸/居中对齐用) |
| `R.释字体(句柄)` | 释放字体 GPU 资源 |

**完整示例**:

```xuantie
设 显示文本 = "玄铁3D球体旋转"

// 精准模式:参考文本确保所有目标字符被加载
设 字体 = R.加载字体精准(
    "assets/HarmonyOS_Sans_SC_Regular.ttf",
    28,
    显示文本 & "Lambertian 光照 FPS0123456789|"
)
设 字体小 = R.加载字体精准(
    "assets/HarmonyOS_Sans_SC_Regular.ttf",
    18,
    显示文本 & "Lambertian 光照 FPS0123456789|"
)

当 非 R.窗口应关闭?() {
    R.开始绘图()
    R.清背景(15, 15, 35, 255)
    R.绘字体文字(字体, 显示文本, 20, 20, 28, 2, 255, 255, 255, 255)
    R.绘字体文字(字体小, "Lambertian 光照 | 60 FPS", 20, 54, 18, 1, 200, 200, 220, 255)
    R.绘帧率(700, 20)
    R.结束绘图()
}

R.释字体(字体)
R.释字体(字体小)
```

---

## 纹理与图像

加载 PNG 等格式到 GPU 纹理,可高效绘制。

| API | 说明 |
|-----|------|
| `R.加载纹理("路径.png")` | 从文件加载纹理(**png/bmp/gif**,支持中文路径),返回整型句柄(0 = 失败) |
| `R.释纹理(句柄)` | 释放 GPU 纹理资源 |
| `R.绘纹理(句柄, x, y, tr, tg, tb, ta)` | 原尺寸绘制(t 参数为着色,255/255/255/255 = 无着色) |
| `R.绘纹理Ex(句柄, x, y, 旋转角, 缩放, tr, tg, tb, ta)` | 带旋转(度)和缩放倍率的绘制 |
| `R.量纹理宽(句柄)` / `R.量纹理高(句柄)` | 查询纹理像素宽/高 |
| `R.绘纹理区(句柄, x, y, 宽, 高, tr, tg, tb, ta)` | 整图缩放到指定 宽×高 矩形绘制(UI 图标适配栏高用) |
| `R.加载SVG纹理("路径.svg", 宽, 高)` | SVG 经内置 nanosvg 光栅化为纹理;宽/高传 0 取 SVG 固有尺寸,否则等比缩放进 宽×高 |

### 资源句柄与支持的图片格式

- **句柄是"槽位编号"(标记整数),`0` 表示失败**:文件缺失、解码失败、槽位耗尽都返回 `0`,用 `!= 0` 即可判成败。纹理解码失败**不会**再返回"非 0 但尺寸为 0"的空纹理。
- **支持的图片格式**:`png` / `bmp` / `gif` 可用;**`jpg` 与 `tga` 当前不可用**——实测不同来源的基线 JPEG、灰度 JPEG 一律报 `IMAGE: Data format not supported`,TGA 同样失败;需要这些格式请先转 PNG。根因是随包 raylib 静态库未编译 JPG/TGA 解码器(见 issue #44)。

### 千分定点绘制(亚像素)

`绘矩形精`/`绘矩形线精`/`绘圆角矩形精`/`绘圆角矩形线精`/`绘纹理区精`/`绘字体文字精` —— 参数与对应普通版相同,仅坐标/尺寸改传 **×1000 定点整数**(`绘字体文字精` 另多一个"缩放千"参数,1000 = 原倍):

- 桥内转 float 由 GPU 亚像素渲染:微交互/动画的小行程移动逐帧平滑,无整数像素台阶
- 文字坐标仍建议像素对齐(半像素位置会被重采样成虚边);框体几何无需对齐
- 文字缩放走字形图集 GPU 拉伸(双线性),不换字号重光栅化,字形不变形

### 圆角渐变纹理生成器

`R.生成渐变纹理(宽, 高, r1,g1,b1,a1, r2,g2,b2,a2, 方向, 圆角, 羽化)` —— 逐像素生成双色线性渐变纹理:

- `方向`:0 = 横向,1 = 竖向
- `圆角`:像素圆角(0 = 直角);边缘由 SDF 覆盖度计算,天然抗锯齿
- `羽化`:边缘过渡像素数(1 = 仅抗锯齿;>1 产生向外柔边)
- 两色取同一色即"圆角羽化纯色块"
- 桥内带 8 槽参数缓存:每帧同参调用直接命中,不重复生成;缓存满轮转淘汰

UI 库的 `底色2`/`渐变向`/`羽化` 选项即由它驱动。纹理句柄与 `绘纹理区` 等绘制 API 通用,但**不要对缓存句柄调 `释纹理`**(缓存轮转时由桥自动释放)。

---

## 键盘与鼠标输入

### 键盘

| API | 说明 |
|-----|------|
| `R.键按下?(键码)` | 按键持续按下 |
| `R.键刚按下?(键码)` | 按键本帧刚按下(边沿触发,不会重复触发) |
| `R.取按键()` | 返回最后按下的键码(整),无按键返回 0 |

**键码常量**:

| 常量 | 对应按键 |
|------|---------|
| `R.键右` `R.键左` `R.键下` `R.键上` | 方向键 |
| `R.键空格` `R.键ESC` `R.键回车` `R.键退格` | 常用键 |
| `R.键A`–`R.键F`、`R.键W` `R.键S` `R.键D` `R.键R` | 字母(其余需用键码整数值) |
| `R.键左Shift` `R.键右Shift` `R.键Ctrl` | 修饰键 |

### 鼠标

| API | 说明 |
|-----|------|
| `R.鼠标按下?(按钮)` | 鼠标按钮持续按下 |
| `R.鼠标刚按下?(按钮)` | 鼠标按钮本帧刚按下 |
| `R.鼠X()` | 当前鼠标 X 坐标(整) |
| `R.鼠Y()` | 当前鼠标 Y 坐标(整) |
| `R.取鼠标滚轮()` | 滚轮增量(整,正=向上,负=向下) |

**鼠标常量**:`R.鼠左键` `R.鼠右键` `R.鼠中键`

---

## 音频

**句柄规则同纹理**:`加载音效`/`加载音乐流` 失败(文件缺失、解码失败、槽位耗尽)一律返回 `0`,用 `!= 0` 判断成败。句柄是槽位编号(标记整数)——**不要当指针用**,也不要拿 `-1` 之类哨兵值去比。早期的裸指针句柄会让 ARC 误判"堆损坏"并直接退出(see issue #44),现已统一。

### 音频设备

| API | 说明 |
|-----|------|
| `R.初始化音频()` | 初始化音频设备(使用任何音频 API 前调用) |
| `R.关闭音频()` | 关闭音频设备 |

### 短音效

整段加载到内存,适合短促音效(< 5 秒)。

| API | 说明 |
|-----|------|
| `R.加载音效("路径.wav")` | 加载 WAV,返回整型句柄 |
| `R.播音效(句柄)` | 播放一次 |
| `R.释音效(句柄)` | 释放内存 |

### 长音乐流

流式解码,适合背景音乐。

| API | 说明 |
|-----|------|
| `R.加载音乐流("路径.mp3")` | 打开流,返回整型句柄 |
| `R.播音乐流(句柄)` | 开始播放 |
| `R.更新音乐流(句柄)` | 每帧调用一次,解码下一段数据 |
| `R.停音乐流(句柄)` | 停止播放 |
| `R.释音乐流(句柄)` | 关闭流并释放 |

---

## 工具函数

| API | 说明 |
|-----|------|
| `R.取随机数(最小值, 最大值)` | 返回 [min, max] 内随机整数 |
| `R.取运行时间()` | 程序启动至今的秒数(小数,double) |
| `R.暂停(秒数)` | 暂停指定秒数(小数,double)(注:语言关键字是 `等待`,故封装名用 `暂停`) |
| `R.截图("文件名.png")` | 当前帧缓冲导出 PNG。**必须在一帧 `R.结束绘图()` 之后调用**(截的是已呈现的帧;结束绘图之前调用会拿到未完成的背景缓冲)。相对路径按当前工作目录解析,也可写绝对路径 |

---

## 颜色常量

未提供 RGBA 参数时可直接使用:

`R.白` `R.黑` `R.红` `R.绿` `R.蓝` `R.黄` `R.青` `R.紫` `R.橙` `R.灰` `R.深灰` `R.浅灰` `R.透明`

---

## 故障排查

| 现象 | 原因 | 解决 |
|------|------|------|
| 中文显示 `???` | 码点不在加载范围内 | 用 `加载字体精准` 并在参考文本中包含所有目标字符 |
| 字体加载但全部 `???` | 字体不含所需字形,或为 CFF 格式(如苹方) | 换含中文的 `.ttf`;系统字体用 `加载系统字体`(CFF 自动兜底黑体) |
| 字体模糊 | 全范围模式下字号太小 | 字号提升到 28px 以上,或切换到精准模式 |
| `invalid redefinition` | 自举编译器旧版本 | 更新到 v0.18.6+ |
| 链接 `XT_InitWindow` 未定义 | 自举编译器未启用渲染链接 | 确保 `render/渲染桥.c` 和 `libraylib.a` 存在 |
| 窗口关闭按钮无效 | 自举编译器旧版本 | 更新到 v0.18.6+ |