所有格式页面 ↓
图形与媒体

PAL / ALP / LHT / SHD——调色板与颜色查找表

来自 coreprime/kbot @ 0ab97bf · 在 GitHub 上编辑

横扫千军用 256 种颜色呈现了它的整个世界。.pal 文件存放这个调色板;.alp、.shd 和 .lht 文件则是据此构建的查找表,引擎用它们在不离开索引色空间的前提下对像素做混合、阴影和光照处理。

这四种文件都没有文件头、版本字段或压缩。.pal 是 1,024 字节(256 × RGBx);.alp 是 65,536 字节(256 行,每行 256 个调色板索引),.shd / .lht 各为 8,192 字节(32 行,每行 256 个调色板索引)。

TA 调色板色板(256 色,每格 24 像素)
palettes/palette.pal——由 kbot pal swatch 渲染

Tip

自己试试。

kbot pal info     palettes/palette.pal              # one-liner
kbot pal describe palettes/palette.pal              # full RGB dump
kbot pal swatch   palettes/palette.pal -o pal.png --cell 24

# Render a lookup table, coloured by the palette:
# .alp as 256×256 cells, .shd / .lht as 256×32 cells
kbot pal lookup   palettes/palette.alp --palette palettes/palette.pal --target alp.png
kbot pal lookup   palettes/palette.shd --target shd.png
# TA: Kingdoms: the palette is usually a PCX next to its tables
kbot pal lookup   palettes/aramon.alp --palette palettes/aramon.pcx --target aramon.png

# Round-trip to editor-friendly formats
kbot pal convert  palettes/palette.pal -o palette.gpl                  # GIMP
kbot pal convert  palettes/palette.pal -o palette.txt --format jasc    # JASC

见 CLI 的 kbot pal 参考。

在 Go 中调用。 使用 formats/pal:

import "github.com/coreprime/kbot/formats/pal"

p, _ := pal.LoadFromFile("palettes/palette.pal")
fmt.Printf("RGB[0] = %v (black; sprites key it per frame)\n", p.Colors[0])
for i, c := range p.Colors {
    fmt.Printf("%3d  #%02x%02x%02x\n", i, c.R, c.G, c.B)
}

磁盘布局(.pal)

typedef struct {
    uint8 R;
    uint8 G;
    uint8 B;
    uint8 A;     // Unused. Cavedog files have this as 0 throughout.
} PALEntry;

PALEntry entries[256];   // 256 × 4 = 1024 bytes

没有签名,也没有版本号。游戏按如下方式加载 palettes/<name>.pal(kbot-io 的 pal.LoadNamed 做法相同,studio 和资源浏览器在每一次 TA 渲染中都通过它加载 palette.pal:图块池、小地图、沙盒地形、地图物件、纹理和模型颜色):

  • 1,024 字节或以上: 前 1,024 字节是调色板;其余部分被忽略。
  • 为空或缺失: 调色板改从 palettes/<name>.pcx 获取——即该文件的最后 768 字节,无论前面有没有 0x0C 标记。随后 TA 3.1c 会针对这些颜色重建 .alp、.shd 和 .lht 表。
  • 1 到 1,023 字节: 不可用;游戏会越过文件末尾读满 1,024 字节。kbot 会报告错误(studio 则退回到内置的 TA 调色板)。

.pal——标准的 TA 调色板

每个条目的字节 R、G、B、A 就字面意思而言分别是红、绿、蓝、未使用。索引 0 是普通的黑色。 地形图块、小地图和背景都把它画成不透明(275 张零售版地图中有 62 张在图块中用到了它)。透明与否由每张图像各自决定,而不是由调色板决定:GAF 帧的透明色键字节(通常是 9,有时是 0)及其跳过的像素是透明的,模型纹理采样时则不使用透明色键。

索引 TA 中的惯例 说明
0 黑色 在地形、小地图和背景中不透明;只在透明色键为 0 的精灵图帧中透明。
9 “魔术粉”(Magic pink) 编辑工具在编码前用它填充 PCX/GAF 中的透明区域;运行时并不透明。
16–95 玩家颜色循环 引擎按阵营替换这些颜色,为单位重新着色。
223–254 保留 主要用于队伍颜色循环和特效。
255 纯白 常用于激光的核心。

Note

这 256 组 RGB 三元组并不是随意的。 Cavedog 手工调校了这个调色板,让每一大类材质(金属、有机物、水、天空、熔岩……)都有平滑的色阶。替换条目以塞进一种“新”颜色,通常会破坏所有取色于该色阶的单位。色阶的分组见 pal describe 的输出。


.alp / .shd / .lht——颜色查找表

这三种文件不是 RGB 数据:每个字节都是一个调色板索引。每张表为每个混合对象或每个级别各有一行 256 个条目,各列则是 256 种源颜色:

文件 大小 行数 条目 [r][c]
.alp(alpha) 65,536 256 最接近颜色 r 与 c 平均值的索引:一半对一半的混合,用于半透明绘制。表是对称的,且 [c][c] 就是 c。
.shd(阴影) 8,192 32 最接近颜色 c 乘以 r × 0.06875 后结果的索引:第 0 行是黑色,第 14–15 行让颜色几乎不变,更高的行则使颜色变亮。
.lht(光照) 8,192 32 最接近颜色 c 乘以 1 + r/30 后结果的索引,从不变(第 0 行)到大约两倍亮度。
uint8 alp[256][256];   // blend = alp[a][b]
uint8 shd[32][256];    // shaded = shd[level][c]
uint8 lht[32][256];    // lit    = lht[level][c]

实际上,引擎从不以算术方式混合颜色——它查表,所以得到的像素永远是一个真实的调色板索引,可以经由现有的 8bpp 管线绘制。这就是为什么 TA 中所有的阴影看起来都是同一色阶中更暗的颜色,而不是“叠上一层 50% 的黑色”:每个阴影结果都是最接近那个数学混合值的调色板索引。

只有当表文件的大小完全正确时,游戏才会使用它;缺失的表或大小不对的表都会根据调色板重建。因此 kbot pal lookup 会拒绝任何其他大小的文件。横扫千军:王国为其 palettes/ 文件夹中几乎每个调色板都配了同样大小的表;那些调色板大多是 PCX 文件,而 kbot pal lookup --palette 既接受 .pal,也接受 .pcx。

Important

查找表不是调色板。 用 kbot pal describe 渲染 .alp 只会显示一堆无意义的 RGB 值,因为这些字节并不是 RGB。要看它们实际做什么,请使用 kbot pal lookup——传入配套的 .pal,这样每个查找格都能用它所解析到的调色板条目着色。


实例解析——palette.pal

原版 TA 调色板的前 16 个条目:

索引 十六进制 R G B 作用
0 #000000 0 0 0 黑色(在地形中不透明;某些精灵图帧的透明色键)
1 #800000 128 0 0 暗红
2 #008000 0 128 0 暗绿
3 #808000 128 128 0 橄榄色
4 #000080 0 0 128 暗蓝(水面阴影)
5 #800080 128 0 128 暗品红
6 #008080 0 128 128 暗青
7 #808080 128 128 128 中灰
8 #C0DCC0 192 220 192 淡绿
9 #5454FC 84 84 252 “魔术粉”(编辑器中的透明色)
10–15 #000000 × 6 0 0 0 保留的填充

kbot pal describe palettes/palette.pal 会打印完整的表。


实例解析——把 palette.alp 和 palette.shd 作为查找表

以调色板为颜色来源渲染这些表(kbot pal lookup … --cell 2):

通过 palette.pal 可视化的 palette.alp
palette.alp:256 × 256 格。第 a 行第 b 列显示颜色 a 与 b 的混合;
对角线重现每种颜色本身。

通过 palette.pal 可视化的 palette.shd
palette.shd:256 × 32 格。每一列是一种源颜色;
各行从黑色(第 0 行)经过颜色本身,再到更亮的色调。

解读阴影表:第 80 列(中灰)在第 0 行是黑色,沿着它所在色阶中较暗的灰色逐渐上升,在第 14、15 行左右到达自身,然后变亮。引擎从不需要对 RGB 值做乘法——它只是读取 shd[level][c]。


为编辑器转换调色板

kbot pal convert 支持几种便于编辑器使用的输出格式:

格式 扩展名 工具
二进制 TA .PAL .pal 格式本身;编辑后重新输出时有用。
GIMP Palette .gpl GIMP、Krita、Inkscape。
JASC-PAL(文本) .pal / .txt(--format jasc) Paint Shop Pro、Aseprite。
PNG 色板 .png(通过 kbot pal swatch) 用于直观参考;不能作为调色板加载。

反方向——把 .gpl 或 .pal(JASC)导回二进制 TA .pal——只要源文件有 256 个条目就可行。

Note

TA 调色板中有 13 组重复的 RGB 三元组。 如果你的编辑器在导入时对颜色去重(有些会这样做),你就会丢失条目,调色板会悄无声息地变成一个 243 条目的调色板。kbot pal info 会报告重复的数量,便于你发现差异;用 kbot pal convert -o roundtrip.pal 做一次往返即可验证。


坑点

Warning

每个条目的第 4 个字节不是 alpha。 它是一个未使用的填充字节,Cavedog 总是把它设为 0x00。有几个开源调色板工具把它当作 alpha 读取,结果拒绝渲染这个调色板(alpha 处处为 0 ⇒ 全部透明)。kbot 读取时忽略它,写入时写出 0x00。

  • 调色板索引 0 本身并不透明。 地形、小地图和背景把它画成黑色;精灵图帧只有在它自己的透明色键(或跳过游程)指明的地方才透明。kbot 的地形和小地图渲染使用不透明的调色板。
  • 没有魔数意味着校验能力很弱。 检查一个 .pal 是否合理的唯一办法,是要求它至少有 1024 字节,并且(可选地)要求每个条目的 alpha 字节都为零。
  • 大小不对的查找表会被游戏忽略,并根据调色板重建。
  • .alp / .lht / .shd 不能互换。 它们针对不同的效果调校;互换会产生明显错误的阴影或光照。
  • 横扫千军:王国使用按阵营区分、嵌在 PCX 中的调色板,而不是全局 .pal 的工作流程。载体 PCX 的技巧见 PCX。

典型大小

文件 大小 说明
palette.pal 1,024 字节 文件更长时,游戏只读取前 1,024 字节。
palette.alp 65,536 字节 256 × 256 个调色板索引。
palette.shd / palette.lht 8,192 字节 32 × 256 个调色板索引。
导出的 GIMP .gpl 约 5 KB 文本格式,比二进制大得多。
导出的 JASC .pal(文本) 约 3 KB 同上。
kbot pal swatch 生成的 PNG 1–10 KB 取决于 --cell 大小。
原版 TA 中的调色板文件总数 约 15 个 一套标准的 palette.*,外加几个备用的界面调色板。

另见

  • PCX——内嵌调色板的文件(以及 TA:K 的调色板载体约定)。
  • GAF——使用调色板但不包含调色板的动画。
  • TNT、SCT——磁盘上带调色板的图块数据。
  • 术语表——带调色板的图像(paletted image)、查找表(lookup table)。