PCX — mapa de bits do ZSoft Paintbrush
Fonte: coreprime/kbot @ 0ab97bf · Editar no GitHub
O PCX é um formato de mapa de bits em cor indexada do início dos anos 90. O Total Annihilation usa-o para os retratos das unidades (
unitpics/*.pcx), os painéis da interface e algumas imagens de pré-visualização de mapas. No TA: Kingdoms, um PCX de 1×1 serve também para transportar as paletas de cada lado: o motor lê a paleta incorporada no PCX e aplica-a ao ficheiro de animação.tafcom o mesmo nome.
unitpics/armcom.pcx → PNG (paleta do TA)
Tip
Experimente por si.
kbot pcx describe unitpics/armcom.pcx # full header + what TA will do with it
kbot pcx info unitpics/armcom.pcx # one-line summary
kbot pcx convert unitpics/armcom.pcx -f png -o armcom.png
Veja a referência de kbot pcx
da linha de comandos.
A partir de Go. Use formats/pcx:
import "github.com/coreprime/kbot/formats/pcx"
f, _ := os.Open("unitpics/armcom.pcx")
defer f.Close()
r, _ := pcx.LoadFromReader(f)
fmt.Println(r.Width(), "×", r.Height(), "@", r.BitsPerPixel(), "bpp")
img, _ := r.Decode() // returns image.Image
O PCX é um formato da comunidade, não da Cavedog
A Cavedog não inventou o PCX: o formato é anterior ao Windows. Por isso, ao contrário do HPI, do GAF ou do TNT, a disposição no disco já era uma especificação aberta quando o TA saiu. Esta página cobre apenas o que interessa quando se encontra um PCX numa instalação do TA; para a especificação completa, consulte a referência técnica original da ZSoft.
O subconjunto que interessa ao TA:
- 8 bits por píxel, 1 plano: só imagens com paleta. As variantes de 24 bits, de 4 bits e de vários planos existem, mas a Cavedog não inclui nenhuma.
- Codificação RLE (
Encoding = 1). Os PCX sem compressão são tecnicamente válidos, mas pouco comuns. - Paleta VGA de 256 cores acrescentada a seguir aos dados dos píxeis,
precedida por um único byte marcador
0x0C. O TA usa sempre esta disposição; a paleta de 16 cores do cabeçalho é ignorada.
Cabeçalho (128 bytes)
typedef struct {
uint8 Manufacturer; // Always 0x0A (ZSoft signature)
uint8 Version; // 0–5 (TA images are typically version 5)
uint8 Encoding; // 1 = RLE
uint8 BitsPerPixel; // Bits per plane per pixel (8 in TA)
uint16 XMin, YMin; // Image bounds (inclusive)
uint16 XMax, YMax;
uint16 HorzDPI, VertDPI;
uint8 Palette16[48]; // Legacy 16-colour palette (unused in TA)
uint8 Reserved;
uint8 NumPlanes; // 1 for 256-colour images
uint16 BytesPerLine; // Encoded bytes per scan line per plane
uint16 PaletteInfo; // 1 = colour, 2 = grayscale
uint16 HorzScreen, VertScreen;
uint8 Filler[54]; // Pad to 128 bytes (Cavedog leaves it zeroed)
} PCXHeader;
| Campo | Valor no TA | Notas |
|---|---|---|
Manufacturer |
0x0A |
Rejeite qualquer outro valor: não é um PCX. |
Version |
5 |
O TA 3.1c só carrega a versão 5. |
BitsPerPixel × NumPlanes |
8 × 1 = 8 |
A única configuração que o TA inclui, e a única que o jogo descodifica. |
Encoding |
1 |
RLE (ver abaixo). O jogo descodifica RLE seja qual for o valor deste byte. |
BytesPerLine |
XMax − XMin + 1 |
No TA, tem de ser igual à largura. O jogo ignora-o e lê exatamente tantos bytes por linha quanto a largura; os editores de imagem que completam as linhas até um número par produzem ficheiros cujas linhas aparecem desalinhadas no jogo. |
Tamanho calculado da imagem
Width = XMax − XMin + 1
Height = YMax − YMin + 1
O jogo faz estas contas com inteiros de tamanho completo, por isso é
possível uma largura de 65 536, e recusa um ficheiro cujo máximo seja
inferior ao mínimo. Um leitor normal trata os bytes a mais em cada linha
de varrimento (BytesPerLine − Width) como preenchimento; o jogo não
(ver abaixo).
Dados de píxeis RLE
Para cada uma das Height linhas de varrimento, repita:
while (decoded < BytesPerLine) {
uint8 b = read();
if ((b & 0xC0) == 0xC0) {
int count = b & 0x3F; // 1..63 reps
uint8 value = read();
emit(value, count);
} else {
emit(b, 1); // literal
}
}
Depois, um leitor normal descarta o preenchimento final
(BytesPerLine − Width bytes). O TA 3.1c, pelo contrário, descodifica
exatamente Width píxeis por linha, de seguida a partir do cabeçalho,
por isso qualquer preenchimento passa a ser os primeiros píxeis da linha
seguinte; uma série que atravesse o fim de uma linha é cortada aí.
Important
Os bytes literais com os dois bits superiores definidos têm de ser
escapados com RLE. Um byte isolado com valor 0xC0–0xFF não é válido
como literal; tem de o emitir como uma série de contagem 1 (0xC1, 0xFF
para um único 0xFF). Os escritores que se esquecem disto produzem
ficheiros que se descodificam bem durante uma linha e depois se
dessincronizam.
Paleta incorporada (256 entradas × 3 bytes)
Depois do último byte RLE, o ficheiro termina com 769 bytes:
1 byte : marker, always 0x0C
256 × 3 : R, G, B per entry (8-bit each)
Para encontrar a paleta sem analisar o RLE, posicione-se em
fileSize - 769 e verifique o marcador. Um leitor normal, sem o
marcador, recorre à paleta de 16 cores do cabeçalho (as pré-visualizações
do kbot usam a paleta do TA). O TA 3.1c usa sempre os últimos 768
bytes como paleta, com ou sem marcador, e recusa um ficheiro com menos
de 896 bytes (cabeçalho mais paleta).
Note
Numa instalação do TA, a paleta incorporada é normalmente a paleta
padrão do TA, mas nem sempre. Os retratos específicos de missões às
vezes trazem paletas próprias, e o palettes/guipal.pcx é
propositadamente um transportador de paleta de 1×1 píxel, com uma
variante ajustada para a interface.
O que o TA 3.1c faz com um PCX
Do cabeçalho, o jogo só lê os campos de fabricante, de versão e de dimensões:
- Carrega apenas a versão 5 e recusa um ficheiro cujo
XMax/YMaxseja inferior ao mínimo ou que tenha menos de 896 bytes. - Ignora
Encoding,BitsPerPixel,NumPlaneseBytesPerLine: todos os ficheiros são descodificados por séries (run-length) como dados de 8 bits num único plano,Widthbytes por linha. Por isso, um ficheiro de 24 bits aparece como lixo e as linhas com preenchimento ficam desalinhadas. - A paleta são os últimos 768 bytes, seja o que for que venha antes.
- Os fundos são desenhados opacos: o índice 0 da paleta é um preto normal.
- Dados de píxeis que acabem antes do tempo não o fazem parar: continua a desenhar a partir do último byte que leu.
Os editores de imagem leem estes ficheiros da forma normal, por isso o
fundo de um mod pode parecer certo num editor (e na pré-visualização do
kbot) e errado no jogo. O kbot pcx describe termina com uma secção
TA 3.1c que lista todas as diferenças (o pcx.Reader.Compat do
kbot-io), a ferramenta MCP pcx_describe devolve-as como game_loads /
game_issues, e o explorador de recursos mostra-as num emblema ao lado
da pré-visualização:
TA 3.1c:
⚠ The game will load this file but draw it differently:
⚠ BytesPerLine is 4 but the width is 3: the game ignores BytesPerLine and decodes 3 bytes per row, so the rows shift
Exemplo comentado — unitpics/armcom.pcx
$ kbot pcx describe unitpics/armcom.pcx
Manufacturer: 0x0A
Version: 5
Encoding: 1 (RLE)
BitsPerPixel: 8
Planes: 1
Bounds: (0,0) - (95,95) → 96 × 96 px
BytesPerLine: 96
Palette: embedded (768 bytes after 0x0C marker)
Unique colors: 41
Isto diz tudo o que precisa para renderizar o ficheiro à mão: imagem de
96×96, 41 índices da paleta distintos em uso, paleta acrescentada no fim.
O kbot pcx convert -f png exporta-o como PNG com paleta, para que os
índices de cor sobrevivam a uma ida e volta para PCX.
TA: Kingdoms — o truque do "PCX de paleta"
Quando converte um .gaf do TA para o TA: Kingdoms, o motor precisa de
uma paleta, mas o .taf (o .gaf com outra extensão) não tem nenhuma. O
truque é colocar um PCX de 1×1 com o mesmo nome base ao lado do .taf:
ui/mywidget.taf ← animation data
ui/mywidget.pcx ← 1×1 paletted PCX carrying the 256-colour palette
O motor lê a paleta incorporada em mywidget.pcx e ignora o seu píxel.
Nas animações de unidades e de elementos, a paleta está implícita em cada
lado (Aramon, Taros, Veruna, Zhon); nas animações da interface, coloca-se
um PCX por sequência.
A armadilha mais comum aqui é usar uma paleta exportada pelo PSP, que tem
valores RGB ligeiramente diferentes dos da paleta canónica do TA e pode
fazer o TA:K falhar em algumas sequências. O kbot pal swatch despeja
qualquer paleta para um PNG, para a poder comparar com a de referência.
Armadilhas
Warning
No TA, escreva BytesPerLine igual à largura. Um leitor normal
descarta o preenchimento quando BytesPerLine > Width, mas o TA 3.1c
ignora BytesPerLine e descodifica Width bytes por linha, por isso o
preenchimento desalinha todas as linhas seguintes. O
kbot pcx describe avisa disso.
- O jogo usa os últimos 768 bytes como paleta, quer o marcador
0x0Cvenha antes deles, quer não. Os leitores normais, sem o marcador, recorrem à paleta de 16 cores do cabeçalho. - No TA, só a versão 5 carrega.
- O campo
PaletteInfodo cabeçalho não é fiável. Trate-o apenas como indicativo; assuma paletas a cores, a não ser que todas as entradas da paleta incorporada tenham R==G==B. - Os ficheiros
unitpics/*.pcxdo TA têm 96×96 por convenção, sem que o formato o imponha. De vez em quando, há mods que trazem retratos maiores ou não quadrados.
Tamanhos típicos
| Recurso | Dimensões | Tamanho do ficheiro |
|---|---|---|
unitpics/*.pcx (retrato de unidade) |
96 × 96 | 3–12 KB |
Painel da interface (bitmaps/*.pcx) |
variadas, muitas vezes 64–256 px de largura | 5–80 KB |
| Pré-visualização de mapa | 252 × 252 | 30–80 KB |
| Transportador de paleta do TA:K | 1 × 1 | ~800 bytes (cabeçalho + paleta) |
| Custo fixo do cabeçalho | sempre 128 bytes | — |
| Paleta incorporada | sempre 769 bytes (quando existe) | — |