94 lines
4.7 KiB
Markdown
94 lines
4.7 KiB
Markdown
# 图像模块
|
||
|
||
**脚本入口:** `eve.Image()`
|
||
|
||
解码图片文件 / 创建像素缓冲,并操作原始像素数据。
|
||
|
||
## 基本用法
|
||
|
||
```squirrel
|
||
local fs = eve.Filesystem();
|
||
local image = eve.Image();
|
||
|
||
// 从虚拟文件系统读取数据并解码(返回已绑定的 ImageData)。
|
||
local data = image.newImageData(fs.read("Textures/hero.png"));
|
||
print("size=" + data.getWidth() + "x" + data.getHeight() + " fmt=" + data.getFormat() + "\n");
|
||
|
||
// 或按 VFS 路径经统一资源缓存解码(同一路径共享一份 ImageData):
|
||
local data2 = image.newImageDataFromFile("Textures/hero.png");
|
||
|
||
// 或创建空白像素缓冲并逐像素编辑:
|
||
local canvas = image.newEmptyImageData(64, 64, "RGBA8");
|
||
canvas.setPixel(10, 10, 1.0, 0.0, 0.0, 1.0);
|
||
```
|
||
|
||
`ImageData` 是 Image / Font / Model3D 共用的脚本类:`FontData.newGlyphImageData(...)` 和
|
||
`ModelData.getEmbeddedTextureImageData(...)` 返回的对象可以直接调用下面的像素与旋转接口。
|
||
|
||
## 对象关系与调用时机
|
||
|
||
`eve.Image()` 负责创建和解码 `ImageData`:
|
||
|
||
- `newImageData(data)`:解码 `fs.read()` / `fs.newFileData()` 得到的编码数据,返回新建的 `ImageData`(脚本持有)。
|
||
- `newImageDataFromFile(path)`:从 VFS 路径经**统一资源缓存**解码——同一路径重复加载共享一份 `ImageData`,
|
||
文件变化时原地刷新(见 `docs/dev/superpowers/specs/2026-08-20-unified-resource-cache.md`)。
|
||
该实例由缓存持有,脚本不要自行释放;需要独立副本时用 `clone()`。
|
||
- `newEmptyImageData(w, h, format)`:创建全透明/黑色画布,配合 `setPixel` / `getPixelR/G/B/A` 做 CPU 像素编辑,
|
||
再用 `gfx.newTexture(imageData)` 上传为纹理。
|
||
|
||
`ImageData::rotate(radians, filter, expand)` 按**逆映射**旋转像素:遍历目标图每个像素,用逆旋转矩阵回算源坐标,
|
||
再按 `filter` 采样。
|
||
|
||
| 参数 | 含义 |
|
||
|------|------|
|
||
| `radians` | 弧度;与 `Math.rotate2*` 同号(Y 向下时视觉为顺时针,同 LÖVE) |
|
||
| `filter` | `"nearest"` / `"linear"` / `"rotsprite"`(Xenowhirl:Scale2x×3 → 偏移搜索 → 最近邻缩回) |
|
||
| `expand` | `true` 时画布扩到容纳整图 AABB;`false` 保持原尺寸(可能裁切) |
|
||
|
||
返回同格式的新 `ImageData`(调用方拥有);源范围外采样为透明黑。`rotsprite` 只挑选已有调色板颜色,不引入插值新色。
|
||
|
||
## 目标导向指南
|
||
|
||
### 加载并上传一张 PNG 为纹理
|
||
|
||
```squirrel
|
||
local image = eve.Image();
|
||
local gfx = eve.Graphics();
|
||
local tex = gfx.newTexture(image.newImageDataFromFile("Textures/hero.png"));
|
||
```
|
||
|
||
同一路径重复调用只解码一次;文件修改后由热重载在原地刷新。
|
||
|
||
### CPU 生成 / 修改像素
|
||
|
||
用 `newEmptyImageData(w, h, "RGBA8")` 建画布,`setPixel(x, y, r, g, b, a)` 写入,`getPixelR/G/B/A` 读回;
|
||
`clone()` 深拷贝,`paste(src, dx, dy, sx, sy, sw, sh)` 拷贝子区域,`rotate(...)` 旋转。注意像素坐标越界会抛异常。
|
||
|
||
模型表面绘制可用 `paintCircleUv(u, v, radiusPixels, r, g, b, a, wrapU, wrapV)`:
|
||
UV 原点按模型约定位于左下,函数会自动反转 V 到图像左上像素原点。`radiusPixels`
|
||
以像素为单位,`wrapU` / `wrapV` 适用于需要穿过 UV 缝的画笔。返回实际改变的像素数。
|
||
|
||
## 常见问题
|
||
|
||
- 把 `newImageData(data)` 与 `newEmptyImageData(w, h, format)` 混淆:前者解码编码数据,后者创建空白画布。
|
||
- 传给 `newImageData` 的不是 `fs.read()` 返回的数据对象:会得到类型转换错误。
|
||
- 修改 `newImageDataFromFile` 返回的像素:它由缓存共享,修改会影响同路径后续引用;需要独立副本先 `clone()`。
|
||
- 每帧把字形重新转纹理:应缓存 ImageData/Texture。
|
||
|
||
## API 快查
|
||
|
||
- `Image`:`getName()`、`newImageData(data)`、`newImageDataFromFile(path)`、`newEmptyImageData(width, height, format)`、`isCompressed(data)`
|
||
- `ImageData`:`getWidth()`、`getHeight()`、`getFormat()`、`getSize()`、`getPixelSize()`、`isSRGB()`、`inside(x, y)`、
|
||
`clone()`、`paste(src, dx, dy, sx, sy, sw, sh)`、`rotate(radians, filter, expand)`、
|
||
`getPixelR(x, y)`、`getPixelG(x, y)`、`getPixelB(x, y)`、`getPixelA(x, y)`、`setPixel(x, y, r, g, b, a)`、
|
||
`paintCircleUv(u, v, radiusPixels, r, g, b, a, wrapU, wrapV)`
|
||
|
||
## 使用要点
|
||
|
||
- 模块对象和它创建的资源对象应保存在全局或实体状态中,不要在每帧重复创建。
|
||
- 带 `update(dt)` 的系统应在 `eve_update` 调用;绘制方法应在 `eve_render` 调用。
|
||
- 参数约束、默认值和返回类型以对应模块头文件及 `addFunc` 绑定为准;本文 API 快查与当前源码同步生成。
|
||
|
||
**源码:** [`src/modules/image/`](../../../src/modules/image/)
|
||
**相关测试:** 在 [`test/`](../../../test/) 中搜索 `image`。
|