108 lines
3.5 KiB
C#
108 lines
3.5 KiB
C#
|
using System.IO;
|
||
|
using System.Linq;
|
||
|
using System.Numerics;
|
||
|
using System.Text;
|
||
|
|
||
|
namespace KeepersCompound.TMV;
|
||
|
|
||
|
public partial class TextureLoader
|
||
|
{
|
||
|
// TODO: Support more types of PCX just in case
|
||
|
// TODO: We need to load the palette from an external file!!!
|
||
|
|
||
|
// References:
|
||
|
// - https://www.fileformat.info/format/pcx/egff.htm
|
||
|
// - http://www.fysnet.net/pcxfile.htm
|
||
|
private static Godot.ImageTexture LoadPcx(string path)
|
||
|
{
|
||
|
using MemoryStream stream = new(File.ReadAllBytes(path));
|
||
|
using BinaryReader reader = new(stream, Encoding.UTF8, false);
|
||
|
|
||
|
// Header
|
||
|
var tag = reader.ReadByte();
|
||
|
var version = reader.ReadByte();
|
||
|
var compression = reader.ReadByte();
|
||
|
var bytesPerPixel = reader.ReadByte();
|
||
|
var min = new Vector2(reader.ReadUInt16(), reader.ReadUInt16());
|
||
|
var max = new Vector2(reader.ReadUInt16(), reader.ReadUInt16());
|
||
|
var dpi = new Vector2(reader.ReadUInt16(), reader.ReadUInt16());
|
||
|
var headerPalette = reader.ReadBytes(48);
|
||
|
reader.ReadByte();
|
||
|
var numPlanes = reader.ReadByte();
|
||
|
var bytesPerRow = reader.ReadUInt16();
|
||
|
var paletteMode = reader.ReadUInt16();
|
||
|
var dpiSrc = new Vector2(reader.ReadUInt16(), reader.ReadUInt16());
|
||
|
reader.ReadBytes(54);
|
||
|
var dataOffset = stream.Position;
|
||
|
|
||
|
// Validation
|
||
|
if (tag != 0x0A)
|
||
|
{
|
||
|
throw new System.Exception("Invalid tag");
|
||
|
}
|
||
|
var validVersions = new int[] { 0, 2, 3, 4, 5 };
|
||
|
if (!validVersions.Contains(version))
|
||
|
{
|
||
|
throw new System.Exception("Unsupported version");
|
||
|
}
|
||
|
if (compression != 1)
|
||
|
{
|
||
|
throw new System.Exception("Unsupported compression mode");
|
||
|
}
|
||
|
if (bytesPerPixel != 8 || numPlanes != 1)
|
||
|
{
|
||
|
throw new System.Exception("Unsupported bpp/plane format");
|
||
|
}
|
||
|
|
||
|
var palette = LoadPcxPalette(reader);
|
||
|
|
||
|
// Read pixels
|
||
|
stream.Seek(dataOffset, SeekOrigin.Begin);
|
||
|
var width = (int)(max.X - min.X + 1);
|
||
|
var height = (int)(max.Y - min.Y + 1);
|
||
|
var image = Godot.Image.Create(width, height, false, Godot.Image.Format.Rgba8);
|
||
|
for (var y = 0; y < height; y++)
|
||
|
{
|
||
|
var x = 0;
|
||
|
while (x < bytesPerRow)
|
||
|
{
|
||
|
var b = reader.ReadByte();
|
||
|
if ((b & 0xc0) == 0xc0)
|
||
|
{
|
||
|
var runLength = b & 0x3f;
|
||
|
b = reader.ReadByte();
|
||
|
for (var i = 0; i < runLength; i++)
|
||
|
{
|
||
|
image.SetPixel(x, y, palette[b]);
|
||
|
x++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
image.SetPixel(x, y, palette[b]);
|
||
|
x++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Godot.ImageTexture.CreateFromImage(image);
|
||
|
}
|
||
|
|
||
|
private static Godot.Color[] LoadPcxPalette(BinaryReader reader)
|
||
|
{
|
||
|
var numColors = 256;
|
||
|
var bytesPerColor = 3;
|
||
|
var paletteSize = numColors * bytesPerColor;
|
||
|
reader.BaseStream.Seek(-paletteSize, SeekOrigin.End);
|
||
|
|
||
|
var palette = new Godot.Color[numColors];
|
||
|
for (var i = 0; i < numColors; i++)
|
||
|
{
|
||
|
var r = reader.ReadByte() / 255.0f;
|
||
|
var g = reader.ReadByte() / 255.0f;
|
||
|
var b = reader.ReadByte() / 255.0f;
|
||
|
palette[i] = new Godot.Color(r, g, b);
|
||
|
}
|
||
|
return palette;
|
||
|
}
|
||
|
}
|