mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 20:39:01 +00:00
877 lines
33 KiB
C#
877 lines
33 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
|
|
namespace Orvid.Graphics.ImageFormats
|
|
{
|
|
public class PcxImage : ImageFormat
|
|
{
|
|
public override void Save(Image i, Stream dest)
|
|
{
|
|
PcxInternals.Save(i, dest);
|
|
}
|
|
|
|
public override Image Load(Stream s)
|
|
{
|
|
return PcxInternals.Load(s);
|
|
}
|
|
|
|
|
|
// Please note, everything below this
|
|
// point was originally from a plugin
|
|
// for Paint.Net, the plugin is available here:
|
|
// http://forums.getpaint.net/index.php?/topic/2135-pcx-plug-in/
|
|
//
|
|
//
|
|
// The source has been modified for use in this library.
|
|
//
|
|
// This disclaimer was last
|
|
// modified on August 9, 2011.
|
|
|
|
|
|
#region Internals
|
|
private static class PcxInternals
|
|
{
|
|
|
|
#region REALLLLYYY Doesn't Work :P
|
|
//private class pcx_t
|
|
//{
|
|
// public char manufacturer;
|
|
// public char version;
|
|
// public char encoding;
|
|
// public char bits_per_pixel;
|
|
// public short xmin, ymin, xmax, ymax;
|
|
// public short hres, vres;
|
|
// public int[] palette = new int[48];
|
|
// public char reserved;
|
|
// public char color_planes;
|
|
// public short bytes_per_line;
|
|
// public short palette_type;
|
|
// public byte[] filler = new byte[58];
|
|
// public byte[] data;
|
|
|
|
// public pcx_t(byte[] raw)
|
|
// {
|
|
// manufacturer = (char)raw[0];
|
|
// version = (char)raw[1];
|
|
// encoding = (char)raw[2];
|
|
// bits_per_pixel = (char)raw[3];
|
|
// xmin = (short)((raw[4] + (raw[5] << 8)) & 0xff);
|
|
// ymin = (short)((raw[6] + (raw[7] << 8)) & 0xff);
|
|
// xmax = (short)((raw[8] + (raw[9] << 8)) & 0xff);
|
|
// ymax = (short)((raw[10] + (raw[11] << 8)) & 0xff);
|
|
// hres = (short)((raw[12] + (raw[13] << 8)) & 0xff);
|
|
// vres = (short)((raw[14] + (raw[15] << 8)) & 0xff);
|
|
// for (int i = 0; i < 48; i++)
|
|
// palette[i] = (raw[16 + i] & 0xff);
|
|
// reserved = (char)raw[64];
|
|
// color_planes = (char)raw[65];
|
|
// bytes_per_line = (short)((raw[66] + (raw[67] << 8)) & 0xff);
|
|
// palette_type = (short)((raw[68] + (raw[69] << 8)) & 0xff);
|
|
// for (int i = 0; i < 58; i++)
|
|
// filler[i] = raw[70 + i];
|
|
// data = new byte[raw.Length - 128];
|
|
// for (int i = 0; i < raw.Length - 128; i++)
|
|
// data[i] = raw[128 + i];
|
|
// }
|
|
//}
|
|
|
|
//private static byte[] imageData;
|
|
//private static int imageWidth, imageHeight;
|
|
//public static Image Load(Stream s)
|
|
//{
|
|
// int[] palette, pic, ot, pix;
|
|
// int k, x, y, len = 0, dataByte, runLength;
|
|
// byte[] raw;
|
|
// pcx_t pcx;
|
|
|
|
// raw = new byte[s.Length];
|
|
// s.Read(raw, 0, (int)s.Length);
|
|
|
|
|
|
// pcx = new pcx_t(raw);
|
|
// raw = pcx.data;
|
|
|
|
// if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.xmax >= 640 || pcx.ymax >= 480)
|
|
// {
|
|
// throw new Exception("Bad Pcx Data!");
|
|
// }
|
|
|
|
// palette = new int[768];
|
|
// for (int i = 0; i < 768; i++)
|
|
// {
|
|
// if ((uint)(len - 128 - 768 + i) < pcx.data.Length)
|
|
// {
|
|
// palette[i] = pcx.data[len - 128 - 768 + i] & 0xff;
|
|
// }
|
|
// }
|
|
|
|
// imageWidth = pcx.xmax + 1;
|
|
// imageHeight = pcx.ymax + 1;
|
|
|
|
// ot = new int[(pcx.ymax + 1) * (pcx.xmax + 1)];
|
|
// pic = ot;
|
|
// pix = ot;
|
|
// int pixcount = 0;
|
|
// int rawcount = 0;
|
|
|
|
// for (y = 0; y <= pcx.ymax; y++, pixcount += pcx.xmax + 1)
|
|
// {
|
|
// for (x = 0; x <= pcx.xmax; )
|
|
// {
|
|
// dataByte = raw[rawcount++];
|
|
|
|
// if ((dataByte & 0xC0) == 0xC0)
|
|
// {
|
|
// runLength = dataByte & 0x3F;
|
|
// dataByte = raw[rawcount++];
|
|
// }
|
|
// else
|
|
// runLength = 1;
|
|
|
|
// while (runLength-- > 0)
|
|
// pix[pixcount + x++] = dataByte & 0xff;
|
|
// }
|
|
// }
|
|
|
|
// Image im = new Image(imageWidth, imageHeight);
|
|
// imageData = new byte[imageWidth * imageHeight * 4];
|
|
|
|
// //convert to rgb format
|
|
// for (k = 0; k < (imageWidth * imageHeight); k++)
|
|
// {
|
|
// imageData[k * 4] = (byte)palette[pic[k] * 3];
|
|
// imageData[k * 4 + 1] = (byte)palette[pic[k] * 3 + 1];
|
|
// imageData[k * 4 + 2] = (byte)palette[pic[k] * 3 + 2];
|
|
// imageData[k * 4 + 3] = 0xff;
|
|
// }
|
|
// uint ndx = 0;
|
|
// for (uint yloc = 0; yloc < imageHeight; yloc++)
|
|
// {
|
|
// for (uint xloc = 0; xloc < imageWidth; xloc++)
|
|
// {
|
|
// im.SetPixel(xloc, yloc, new Pixel(imageData[ndx], imageData[ndx + 1], imageData[ndx + 2], imageData[ndx + 3]));
|
|
// ndx += 4;
|
|
// }
|
|
// }
|
|
|
|
// return im;
|
|
//}
|
|
#endregion
|
|
|
|
|
|
#region Load
|
|
public static Image Load(Stream input)
|
|
{
|
|
PcxPalette palette = null;
|
|
uint[] numArray = null;
|
|
Image im;
|
|
PcxHeader header = new PcxHeader(input);
|
|
|
|
#region Checks
|
|
if (header.id != PcxId.ZSoftPCX)
|
|
{
|
|
throw new FormatException("Not a PCX file.");
|
|
}
|
|
if (((header.version != PcxVersion.Version3_0) && (header.version != PcxVersion.Version2_8_Palette)) && ((header.version != PcxVersion.Version2_8_DefaultPalette) && (header.version != PcxVersion.Version2_5)))
|
|
{
|
|
throw new FormatException("Unsupported PCX version: " + header.version.ToString());
|
|
}
|
|
if (((header.bitsPerPixel != 1) && (header.bitsPerPixel != 2)) && ((header.bitsPerPixel != 4) && (header.bitsPerPixel != 8)))
|
|
{
|
|
throw new FormatException("Unsupported PCX bits per pixel: " + header.bitsPerPixel.ToString() + " bits per pixel");
|
|
}
|
|
int width = (header.xMax - header.xMin) + 1;
|
|
int height = (header.yMax - header.yMin) + 1;
|
|
if (((width < 0) || (height < 0)) || ((width > 0xffff) || (height > 0xffff)))
|
|
{
|
|
throw new FormatException("Invalid image dimensions: (" + header.xMin.ToString() + "," + header.yMin.ToString() + ")-(" + header.xMax.ToString() + "," + header.yMax.ToString() + ")");
|
|
}
|
|
int num3 = (header.bytesPerLine * 8) / header.bitsPerPixel;
|
|
int BitDepth = header.bitsPerPixel * header.nPlanes;
|
|
if ((((BitDepth != 1) && (BitDepth != 2)) && ((BitDepth != 4) && (BitDepth != 8))) && (BitDepth != 0x18))
|
|
{
|
|
throw new FormatException("Unsupported PCX bit depth: " + BitDepth.ToString());
|
|
}
|
|
#endregion
|
|
|
|
#region Load Palette
|
|
while (true)
|
|
{
|
|
if (BitDepth == 1)
|
|
{
|
|
palette = PcxPalette.FromEgaPalette(PcxPalette.MONO_PALETTE);
|
|
break;
|
|
}
|
|
if (BitDepth >= 8)
|
|
{
|
|
if (BitDepth == 8)
|
|
{
|
|
long position = input.Position;
|
|
input.Seek(-769L, SeekOrigin.End);
|
|
if (input.ReadByte() != 12)
|
|
{
|
|
throw new FormatException("PCX palette marker not present in file");
|
|
}
|
|
palette = new PcxPalette(input, 0x100);
|
|
input.Seek(position, SeekOrigin.Begin);
|
|
}
|
|
else
|
|
{
|
|
palette = new PcxPalette(0x100);
|
|
}
|
|
break;
|
|
}
|
|
switch (header.version)
|
|
{
|
|
case PcxVersion.Version2_5:
|
|
case PcxVersion.Version2_8_DefaultPalette:
|
|
if (BitDepth == 2)
|
|
{
|
|
numArray = PcxPalette.CGA_PALETTE;
|
|
palette = PcxPalette.FromEgaPalette(numArray);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
numArray = new uint[0];
|
|
palette = PcxPalette.FromColorMap(header.colorMap);
|
|
break;
|
|
}
|
|
if (numArray == null)
|
|
{
|
|
numArray = PcxPalette.EGA_PALETTE;
|
|
}
|
|
break;
|
|
}
|
|
#endregion
|
|
|
|
im = new Image(width, height);
|
|
uint[] array = new uint[width];
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
PcxByteReader byteReader = (header.encoding == PcxEncoding.RunLengthEncoded) ? ((PcxByteReader)new PcxRleByteReader(input)) : ((PcxByteReader)new PcxRawByteReader(input));
|
|
PcxIndexReader indxReader = new PcxIndexReader(byteReader, header.bitsPerPixel);
|
|
for (int j = 0; j < header.nPlanes; j++)
|
|
{
|
|
for (int m = 0; m < num3; m++)
|
|
{
|
|
uint num10 = indxReader.ReadIndex();
|
|
if (m < width)
|
|
{
|
|
array[m] |= num10 << (j * header.bitsPerPixel);
|
|
}
|
|
}
|
|
}
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
Pixel bgra;
|
|
uint TempC = array[x];
|
|
if (BitDepth == 24)
|
|
{
|
|
byte r = (byte)(TempC & 0xff);
|
|
byte g = (byte)((TempC >> 8) & 0xff);
|
|
byte b = (byte)((TempC >> 16) & 0xff);
|
|
bgra = new Pixel(r, g, b, 255);
|
|
}
|
|
else
|
|
{
|
|
bgra = palette[TempC];
|
|
}
|
|
im.SetPixel((uint)x, (uint)y, bgra);
|
|
}
|
|
}
|
|
return im;
|
|
}
|
|
#endregion
|
|
|
|
#region Save
|
|
public static void Save(Image bitmap, Stream output)
|
|
{
|
|
//if ((bitmap.Palette.Entries.Length == 0) || (bitmap.Palette.Entries.Length > 0x100))
|
|
//{
|
|
// throw new FormatException("Unsupported palette size");
|
|
//}
|
|
//PcxPalette palette = new PcxPalette(bitmap.Palette);
|
|
//PcxHeader header = new PcxHeader
|
|
//{
|
|
// version = PcxVersion.Version3_0,
|
|
// encoding = token.RleCompress ? PcxEncoding.RunLengthEncoded : PcxEncoding.None,
|
|
// bitsPerPixel = (palette.Size == 0x10) ? ((byte)4) : ((palette.Size == 0x100) ? ((byte)8) : ((byte)0)),
|
|
// xMin = 0,
|
|
// yMin = 0,
|
|
// xMax = (ushort)(bitmap.Width - 1),
|
|
// yMax = (ushort)(bitmap.Height - 1),
|
|
// hDpi = (ushort)bitmap.HorizontalResolution,
|
|
// vDpi = (ushort)bitmap.VerticalResolution,
|
|
// nPlanes = 1,
|
|
// bytesPerLine = (ushort)(bitmap.Width + (((bitmap.Width % 2) == 1) ? 1 : 0)),
|
|
// paletteInfo = PcxPaletteType.Indexed
|
|
//};
|
|
//if (palette.Size == 0x10)
|
|
//{
|
|
// header.colorMap = palette.ToColorMap();
|
|
//}
|
|
//header.Write(output);
|
|
//int num = (header.bytesPerLine * 8) / header.bitsPerPixel;
|
|
// for (int i = 0; i < bitmap.Height; i++)
|
|
// {
|
|
// byte[] destination = new byte[bitmap.Width];
|
|
// byte* numPtr = (byte*)(bitmapdata.Scan0.ToPointer() + (i * bitmapdata.Stride));
|
|
// Marshal.Copy(new IntPtr((void*)numPtr), destination, 0, bitmap.Width);
|
|
// PcxByteWriter writer = (header.encoding == PcxEncoding.RunLengthEncoded) ? ((PcxByteWriter)new PcxRleByteWriter(output)) : ((PcxByteWriter)new PcxRawByteWriter(output));
|
|
// PcxIndexWriter writer2 = new PcxIndexWriter(writer, header.bitsPerPixel);
|
|
// for (int j = 0; j < num; j++)
|
|
// {
|
|
// if (j < bitmap.Width)
|
|
// {
|
|
// writer2.WriteIndex(destination[j]);
|
|
// }
|
|
// else
|
|
// {
|
|
// writer2.WriteIndex(0);
|
|
// }
|
|
// }
|
|
// writer2.Flush();
|
|
// writer.Flush();
|
|
// }
|
|
//if (palette.Size == 0x100)
|
|
//{
|
|
// output.WriteByte(12);
|
|
// palette.Write(output);
|
|
//}
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region PcxByteReader
|
|
private abstract class PcxByteReader
|
|
{
|
|
protected PcxByteReader()
|
|
{
|
|
}
|
|
|
|
public abstract byte ReadByte();
|
|
}
|
|
#endregion
|
|
|
|
#region PcxByteWriter
|
|
private abstract class PcxByteWriter
|
|
{
|
|
protected PcxByteWriter()
|
|
{
|
|
}
|
|
|
|
public abstract void Flush();
|
|
public abstract void WriteByte(byte value);
|
|
}
|
|
#endregion
|
|
|
|
#region PcxHeader
|
|
private class PcxHeader
|
|
{
|
|
public byte bitsPerPixel;
|
|
public ushort bytesPerLine;
|
|
public byte[] colorMap;
|
|
public PcxEncoding encoding;
|
|
public byte[] filler;
|
|
public ushort hDpi;
|
|
public PcxId id;
|
|
public byte nPlanes;
|
|
public PcxPaletteType paletteInfo;
|
|
public byte reserved;
|
|
public ushort vDpi;
|
|
public PcxVersion version;
|
|
public ushort xMax;
|
|
public ushort xMin;
|
|
public ushort yMax;
|
|
public ushort yMin;
|
|
|
|
public PcxHeader()
|
|
{
|
|
this.id = PcxId.ZSoftPCX;
|
|
this.version = PcxVersion.Version3_0;
|
|
this.encoding = PcxEncoding.RunLengthEncoded;
|
|
this.colorMap = new byte[0x30];
|
|
this.filler = new byte[0x3a];
|
|
}
|
|
|
|
public PcxHeader(Stream input)
|
|
{
|
|
this.id = PcxId.ZSoftPCX;
|
|
this.version = PcxVersion.Version3_0;
|
|
this.encoding = PcxEncoding.RunLengthEncoded;
|
|
this.colorMap = new byte[0x30];
|
|
this.filler = new byte[0x3a];
|
|
this.id = (PcxId)this.ReadByte(input);
|
|
this.version = (PcxVersion)this.ReadByte(input);
|
|
this.encoding = (PcxEncoding)this.ReadByte(input);
|
|
this.bitsPerPixel = this.ReadByte(input);
|
|
this.xMin = this.ReadUInt16(input);
|
|
this.yMin = this.ReadUInt16(input);
|
|
this.xMax = this.ReadUInt16(input);
|
|
this.yMax = this.ReadUInt16(input);
|
|
this.hDpi = this.ReadUInt16(input);
|
|
this.vDpi = this.ReadUInt16(input);
|
|
for (int i = 0; i < this.colorMap.Length; i++)
|
|
{
|
|
this.colorMap[i] = this.ReadByte(input);
|
|
}
|
|
this.reserved = this.ReadByte(input);
|
|
this.nPlanes = this.ReadByte(input);
|
|
this.bytesPerLine = this.ReadUInt16(input);
|
|
this.paletteInfo = (PcxPaletteType)((byte)this.ReadUInt16(input));
|
|
for (int j = 0; j < this.filler.Length; j++)
|
|
{
|
|
this.filler[j] = this.ReadByte(input);
|
|
}
|
|
}
|
|
|
|
private byte ReadByte(Stream input)
|
|
{
|
|
int num = input.ReadByte();
|
|
if (num == -1)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
return (byte)num;
|
|
}
|
|
|
|
private ushort ReadUInt16(Stream input)
|
|
{
|
|
int num = BitConverter.ToUInt16(new byte[] { (byte)input.ReadByte(), (byte)input.ReadByte() }, 0);
|
|
if (num == -1)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
return (ushort)num;
|
|
}
|
|
|
|
public void Write(Stream output)
|
|
{
|
|
output.WriteByte((byte)this.id);
|
|
output.WriteByte((byte)this.version);
|
|
output.WriteByte((byte)this.encoding);
|
|
output.WriteByte(this.bitsPerPixel);
|
|
output.Write(BitConverter.GetBytes((ushort)this.xMin), 0, 2);
|
|
output.Write(BitConverter.GetBytes((ushort)this.yMin), 0, 2);
|
|
output.Write(BitConverter.GetBytes((ushort)this.xMax), 0, 2);
|
|
output.Write(BitConverter.GetBytes((ushort)this.yMax), 0, 2);
|
|
output.Write(BitConverter.GetBytes((ushort)this.hDpi), 0, 2);
|
|
output.Write(BitConverter.GetBytes((ushort)this.vDpi), 0, 2);
|
|
for (int i = 0; i < this.colorMap.Length; i++)
|
|
{
|
|
output.WriteByte(this.colorMap[i]);
|
|
}
|
|
output.WriteByte(this.reserved);
|
|
output.WriteByte(this.nPlanes);
|
|
output.Write(BitConverter.GetBytes((ushort)this.bytesPerLine), 0, 2);
|
|
output.Write(BitConverter.GetBytes((ushort)1), 0, 2);
|
|
for (int j = 0; j < this.filler.Length; j++)
|
|
{
|
|
output.WriteByte(this.filler[j]);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxIndexReader
|
|
private class PcxIndexReader
|
|
{
|
|
private uint m_bitMask;
|
|
private uint m_bitsPerPixel;
|
|
private uint m_bitsRemaining;
|
|
private uint m_byteRead;
|
|
private PcxByteReader m_reader;
|
|
|
|
public PcxIndexReader(PcxByteReader reader, uint bitsPerPixel)
|
|
{
|
|
if (((bitsPerPixel != 1) && (bitsPerPixel != 2)) && ((bitsPerPixel != 4) && (bitsPerPixel != 8)))
|
|
{
|
|
throw new ArgumentException("bitsPerPixel must be 1, 2, 4 or 8", "bitsPerPixel");
|
|
}
|
|
this.m_reader = reader;
|
|
this.m_bitsPerPixel = bitsPerPixel;
|
|
this.m_bitMask = 1;
|
|
for (uint i = this.m_bitsPerPixel; i > 0; i--)
|
|
{
|
|
this.m_bitMask = this.m_bitMask << 1;
|
|
}
|
|
this.m_bitMask--;
|
|
}
|
|
|
|
public uint ReadIndex()
|
|
{
|
|
if (this.m_bitsRemaining == 0)
|
|
{
|
|
this.m_byteRead = this.m_reader.ReadByte();
|
|
this.m_bitsRemaining = 8;
|
|
}
|
|
uint num = (this.m_byteRead >> (int)(8 - this.m_bitsPerPixel)) & this.m_bitMask;
|
|
this.m_byteRead = this.m_byteRead << (int)this.m_bitsPerPixel;
|
|
this.m_bitsRemaining -= this.m_bitsPerPixel;
|
|
return num;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxIndexWriter
|
|
private class PcxIndexWriter
|
|
{
|
|
private uint m_bitMask;
|
|
private uint m_bitsPerPixel;
|
|
private uint m_bitsUsed;
|
|
private uint m_byteAccumulated;
|
|
private PcxByteWriter m_writer;
|
|
|
|
public PcxIndexWriter(PcxByteWriter writer, uint bitsPerPixel)
|
|
{
|
|
if (((bitsPerPixel != 1) && (bitsPerPixel != 2)) && ((bitsPerPixel != 4) && (bitsPerPixel != 8)))
|
|
{
|
|
throw new ArgumentException("bitsPerPixel must be 1, 2, 4 or 8", "bitsPerPixel");
|
|
}
|
|
this.m_writer = writer;
|
|
this.m_bitsPerPixel = bitsPerPixel;
|
|
this.m_bitMask = 1;
|
|
for (uint i = this.m_bitsPerPixel; i > 0; i--)
|
|
{
|
|
this.m_bitMask = this.m_bitMask << 1;
|
|
}
|
|
this.m_bitMask--;
|
|
}
|
|
|
|
public void Flush()
|
|
{
|
|
if (this.m_bitsUsed > 0)
|
|
{
|
|
this.m_writer.WriteByte((byte)this.m_byteAccumulated);
|
|
this.m_bitsUsed = 0;
|
|
}
|
|
}
|
|
|
|
public void WriteIndex(uint index)
|
|
{
|
|
this.m_byteAccumulated = (this.m_byteAccumulated << (int)this.m_bitsPerPixel) | (index & this.m_bitMask);
|
|
this.m_bitsUsed += this.m_bitsPerPixel;
|
|
if (this.m_bitsUsed == 8)
|
|
{
|
|
this.Flush();
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxPalette
|
|
private class PcxPalette
|
|
{
|
|
public static readonly uint[] CGA_PALETTE = new uint[]
|
|
{
|
|
0, 0xaaaa, 0xaa00aa, 0xaaaaaa,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0
|
|
};
|
|
public static readonly uint[] EGA_PALETTE = new uint[]
|
|
{
|
|
0, 0xa8, 0xa800, 0xa8a8,
|
|
0xa80000, 0xa800a8, 0xa85400, 0xa8a8a8,
|
|
0x545454, 0x5454fe, 0x54fe54, 0x54fefe,
|
|
0xfe5454, 0xfe54fe, 0xfefe54, 0xfefefe
|
|
};
|
|
private Pixel[] m_palette;
|
|
public static readonly uint[] MONO_PALETTE;
|
|
|
|
static PcxPalette()
|
|
{
|
|
uint[] numArray = new uint[0x10];
|
|
numArray[1] = 0xffffff;
|
|
MONO_PALETTE = numArray;
|
|
}
|
|
|
|
public PcxPalette(uint size)
|
|
{
|
|
if ((size != 0x10) && (size != 0x100))
|
|
{
|
|
throw new FormatException("Unsupported palette size");
|
|
}
|
|
this.m_palette = new Pixel[size];
|
|
}
|
|
|
|
public PcxPalette(Stream input, int size)
|
|
{
|
|
if ((size != 0x10) && (size != 0x100))
|
|
{
|
|
throw new FormatException("Unsupported palette size");
|
|
}
|
|
this.m_palette = new Pixel[size];
|
|
for (int i = 0; i < this.m_palette.Length; i++)
|
|
{
|
|
int num2 = input.ReadByte();
|
|
if (num2 == -1)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
int num3 = input.ReadByte();
|
|
if (num3 == -1)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
int num4 = input.ReadByte();
|
|
if (num4 == -1)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
this.m_palette[i] = new Pixel((byte)num2, (byte)num3, (byte)num4, 0xff);
|
|
}
|
|
}
|
|
|
|
public static PcxPalette FromColorMap(byte[] colorMap)
|
|
{
|
|
if (colorMap == null)
|
|
{
|
|
throw new ArgumentNullException("colorMap");
|
|
}
|
|
if (colorMap.Length != 48)
|
|
{
|
|
throw new FormatException("Trying to read an unsupported palette size from a header ColorMap");
|
|
}
|
|
PcxPalette palette = new PcxPalette(16);
|
|
uint num = 0;
|
|
for (uint i = 0; i < 16; i++)
|
|
{
|
|
byte r = colorMap[num];
|
|
num++;
|
|
byte g = colorMap[num];
|
|
num++;
|
|
byte b = colorMap[num];
|
|
num++;
|
|
palette[i] = new Pixel(r, g, b, 255);
|
|
}
|
|
return palette;
|
|
}
|
|
|
|
public static PcxPalette FromEgaPalette(uint[] egaPalette)
|
|
{
|
|
if (egaPalette == null)
|
|
{
|
|
throw new ArgumentNullException("egaPalette");
|
|
}
|
|
if (egaPalette.Length != 0x10)
|
|
{
|
|
throw new FormatException("Trying to read an unsupported palette size from a header ColorMap");
|
|
}
|
|
PcxPalette palette = new PcxPalette(0x10);
|
|
for (uint i = 0; i < 0x10; i++)
|
|
{
|
|
byte r = (byte)((egaPalette[i] >> 16) & 0xff);
|
|
byte g = (byte)((egaPalette[i] >> 8) & 0xff);
|
|
byte b = (byte)(egaPalette[i] & 0xff);
|
|
palette[i] = new Pixel(r, g, b, 255);
|
|
}
|
|
return palette;
|
|
}
|
|
|
|
public byte[] ToColorMap()
|
|
{
|
|
if (this.m_palette.Length != 0x10)
|
|
{
|
|
throw new FormatException("Trying to write an unsupported palette size to a header ColorMap");
|
|
}
|
|
byte[] buffer = new byte[0x30];
|
|
uint num = 0;
|
|
for (int i = 0; i < 0x10; i++)
|
|
{
|
|
Pixel bgra = this.m_palette[i];
|
|
buffer[num++] = bgra.R;
|
|
buffer[num++] = bgra.G;
|
|
buffer[num++] = bgra.B;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
public void Write(Stream output)
|
|
{
|
|
for (int i = 0; i < this.m_palette.Length; i++)
|
|
{
|
|
Pixel bgra = this.m_palette[i];
|
|
output.WriteByte(bgra.R);
|
|
output.WriteByte(bgra.G);
|
|
output.WriteByte(bgra.B);
|
|
}
|
|
}
|
|
|
|
public Pixel this[uint index]
|
|
{
|
|
get
|
|
{
|
|
return this.m_palette[index];
|
|
}
|
|
set
|
|
{
|
|
this.m_palette[index] = value;
|
|
}
|
|
}
|
|
|
|
public uint Size
|
|
{
|
|
get
|
|
{
|
|
return (uint)this.m_palette.Length;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxRawByteReader
|
|
private class PcxRawByteReader : PcxByteReader
|
|
{
|
|
private Stream m_stream;
|
|
|
|
public PcxRawByteReader(Stream stream)
|
|
{
|
|
this.m_stream = stream;
|
|
}
|
|
|
|
public override byte ReadByte()
|
|
{
|
|
return (byte)this.m_stream.ReadByte();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxRawByteWriter
|
|
private class PcxRawByteWriter : PcxByteWriter
|
|
{
|
|
private Stream m_stream;
|
|
|
|
public PcxRawByteWriter(Stream stream)
|
|
{
|
|
this.m_stream = stream;
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
}
|
|
|
|
public override void WriteByte(byte value)
|
|
{
|
|
this.m_stream.WriteByte(value);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxRleByteReader
|
|
private class PcxRleByteReader : PcxByteReader
|
|
{
|
|
private uint m_count;
|
|
private byte m_rleValue;
|
|
private Stream m_stream;
|
|
|
|
public PcxRleByteReader(Stream input)
|
|
{
|
|
this.m_stream = input;
|
|
}
|
|
|
|
public override byte ReadByte()
|
|
{
|
|
if (this.m_count > 0)
|
|
{
|
|
this.m_count--;
|
|
return this.m_rleValue;
|
|
}
|
|
byte num = (byte)this.m_stream.ReadByte();
|
|
if ((num & 192) == 192)
|
|
{
|
|
this.m_count = (uint)(num & 63);
|
|
this.m_rleValue = (byte)this.m_stream.ReadByte();
|
|
this.m_count--;
|
|
return this.m_rleValue;
|
|
}
|
|
return num;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxRleByteWriter
|
|
private class PcxRleByteWriter : PcxByteWriter
|
|
{
|
|
private uint m_count;
|
|
private byte m_lastValue;
|
|
private Stream m_stream;
|
|
|
|
public PcxRleByteWriter(Stream output)
|
|
{
|
|
this.m_stream = output;
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
if (this.m_count != 0)
|
|
{
|
|
if ((this.m_count > 1) || ((this.m_count == 1) && ((this.m_lastValue & 0xc0) == 0xc0)))
|
|
{
|
|
this.m_stream.WriteByte((byte)(0xc0 | this.m_count));
|
|
this.m_stream.WriteByte(this.m_lastValue);
|
|
this.m_count = 0;
|
|
}
|
|
else
|
|
{
|
|
this.m_stream.WriteByte(this.m_lastValue);
|
|
this.m_count = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void WriteByte(byte value)
|
|
{
|
|
if (((this.m_count == 0) || (this.m_count == 0x3f)) || (value != this.m_lastValue))
|
|
{
|
|
this.Flush();
|
|
this.m_lastValue = value;
|
|
this.m_count = 1;
|
|
}
|
|
else
|
|
{
|
|
this.m_count++;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region PcxVersion
|
|
private enum PcxVersion : byte
|
|
{
|
|
Version2_5 = 0,
|
|
Version2_8_DefaultPalette = 3,
|
|
Version2_8_Palette = 2,
|
|
Version3_0 = 5
|
|
}
|
|
#endregion
|
|
|
|
#region PcxPaletteType
|
|
private enum PcxPaletteType : byte
|
|
{
|
|
Grayscale = 2,
|
|
Indexed = 1
|
|
}
|
|
#endregion
|
|
|
|
#region PcxEncoding
|
|
private enum PcxEncoding : byte
|
|
{
|
|
None = 0,
|
|
RunLengthEncoded = 1
|
|
}
|
|
#endregion
|
|
|
|
#region PcxId
|
|
private enum PcxId : byte
|
|
{
|
|
ZSoftPCX = 10
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
#endregion
|
|
}
|
|
}
|