public static PmsHeader GetHeader(byte[] bytes) { if (bytes.Length < 44) { throw new InvalidDataException("PMS header is too short"); } var ms = new MemoryStream(bytes); var br = new BinaryReader(ms); PmsHeader pms = new PmsHeader(); pms.signature = br.ReadUInt16(); pms.version = br.ReadUInt16(); pms.headerSize = br.ReadUInt16(); pms.colorDepth = br.ReadByte(); pms.shadowDepth = br.ReadByte(); pms.isSprite = br.ReadUInt16(); pms.paletteBank = br.ReadUInt16(); pms.word0C = br.ReadInt32(); pms.xLocation = br.ReadInt32(); pms.yLocation = br.ReadInt32(); pms.width = br.ReadInt32(); pms.height = br.ReadInt32(); pms.addressOfData = br.ReadInt32(); pms.addressOfPalette = br.ReadInt32(); pms.addressOfComment = br.ReadInt32(); int lastHeaderAddress = Math.Max(pms.headerSize, pms.addressOfPalette); int lastHeaderAddress2 = Math.Max(pms.headerSize, pms.addressOfData); lastHeaderAddress = Math.Min(lastHeaderAddress, lastHeaderAddress2); if (lastHeaderAddress > bytes.Length) { lastHeaderAddress = bytes.Length; } int extraDataSize = lastHeaderAddress - (int)ms.Position; if (extraDataSize >= 1024 * 1024) { throw new InvalidDataException("Too much extra data for PMS file"); } if (extraDataSize >= 0) { pms.extraData = br.ReadBytes(extraDataSize); } /* * if (pms.addressOfComment != 0) * { * ms.Position = pms.addressOfComment; * pms.comment = br.ReadStringNullTerminated(); * } */ return(pms); }
/* * Get palette from raw data * pal: palette to be stored * b : raw data (pointer to palette) */ internal static void GetPalette(Palette palette, PmsHeader pmsHeader, byte[] bytes) { int address = pmsHeader.addressOfPalette; int i; for (i = 0; i < 256; i++) { palette[i] = Color.FromArgb(bytes[address + i * 3 + 0], bytes[address + i * 3 + 1], bytes[address + i * 3 + 2]); } }
public PmsHeader ToPmsHeader() { var pmsHeader = new PmsHeader(); pmsHeader.addressOfData = 0x320; pmsHeader.addressOfPalette = 0x20; pmsHeader.colorDepth = 8; pmsHeader.headerSize = 0x20; pmsHeader.paletteBank = this.paletteBank; pmsHeader.height = this.height; pmsHeader.width = ((this.width + 7) / 8) * 8; pmsHeader.xLocation = this.xLocation; pmsHeader.yLocation = this.yLocation; return(pmsHeader); }
public static void SaveHeader(Stream stream, PmsHeader pms) { long startPosition = stream.Position; var bw = new BinaryWriter(stream); bw.Write((ushort)pms.signature); bw.Write((ushort)pms.version); bw.Write((ushort)pms.headerSize); bw.Write((byte)pms.colorDepth); bw.Write((byte)pms.shadowDepth); bw.Write((ushort)pms.isSprite); bw.Write((ushort)pms.paletteBank); bw.Write((int)pms.word0C); bw.Write((int)pms.xLocation); bw.Write((int)pms.yLocation); bw.Write((int)pms.width); bw.Write((int)pms.height); bw.Write((int)pms.addressOfData); bw.Write((int)pms.addressOfPalette); bw.Write((int)pms.addressOfComment); long endPosition = stream.Position; int currentLength = (int)(endPosition - startPosition); if (currentLength < pms.headerSize) { int extraDataLength = pms.headerSize - currentLength; if (pms.extraData != null && pms.extraData.Length >= extraDataLength) { stream.Write(pms.extraData, 0, extraDataLength); } else if (pms.extraData != null) { //shouldn't ever happen int lengthFromArray = pms.extraData.Length; stream.Write(pms.extraData, 0, lengthFromArray); stream.WriteZeroes(extraDataLength - lengthFromArray); } else { stream.WriteZeroes(extraDataLength); } } }
internal static ushort[] GetImageData16Bit(PmsHeader pmsHeader, byte[] bytes) { ushort[] pic = new ushort[pmsHeader.width * pmsHeader.height]; int bytePosition = pmsHeader.addressOfData; int c0, c1, pc0, pc1; int x, y, i, l, loc; int scanline = pmsHeader.width; for (y = 0; y < pmsHeader.height; y++) { for (x = 0; x < pmsHeader.width;) { loc = y * scanline + x; c0 = bytes[bytePosition++]; if (c0 <= 0xf7) { c1 = bytes[bytePosition++]; x++; pic[loc] = (ushort)(c0 | (c1 << 8)); } else if (c0 == 0xff) { l = bytes[bytePosition] + 2; x += l; bytePosition++; for (i = 0; i < l; i++) { pic[loc + i] = pic[loc + i - scanline]; } } else if (c0 == 0xfe) { l = bytes[bytePosition] + 2; x += l; bytePosition++; for (i = 0; i < l; i++) { pic[loc + i] = pic[loc + i - scanline * 2]; } } else if (c0 == 0xfd) { l = bytes[bytePosition] + 3; x += l; bytePosition++; c0 = bytes[bytePosition++]; c1 = bytes[bytePosition++]; pc0 = c0 | (c1 << 8); for (i = 0; i < l; i++) { pic[loc + i] = (ushort)pc0; } } else if (c0 == 0xfc) { l = (bytes[bytePosition] + 2) * 2; x += l; bytePosition++; c0 = bytes[bytePosition++]; c1 = bytes[bytePosition++]; pc0 = c0 | (c1 << 8); c0 = bytes[bytePosition++]; c1 = bytes[bytePosition++]; pc1 = c0 | (c1 << 8); for (i = 0; i < l; i += 2) { pic[loc + i] = (ushort)pc0; pic[loc + i + 1] = (ushort)pc1; } } else if (c0 == 0xfb) { x++; pic[loc] = pic[loc - scanline - 1]; } else if (c0 == 0xfa) { x++; pic[loc] = pic[loc - scanline + 1]; } else if (c0 == 0xf9) { l = bytes[bytePosition] + 1; x += l; bytePosition++; c0 = bytes[bytePosition++]; c1 = bytes[bytePosition++]; pc0 = ((c0 & 0xe0) << 8) + ((c0 & 0x18) << 6) + ((c0 & 0x07) << 2); pc1 = ((c1 & 0xc0) << 5) + ((c1 & 0x3c) << 3) + (c1 & 0x03); pic[loc] = (ushort)(pc0 + pc1); for (i = 1; i < l; i++) { c1 = bytes[bytePosition++]; pc1 = ((c1 & 0xc0) << 5) + ((c1 & 0x3c) << 3) + (c1 & 0x03); pic[loc + i] = (ushort)(pc0 | pc1); } } else { c0 = bytes[bytePosition++]; c1 = bytes[bytePosition++]; x++; pic[loc] = (ushort)(c0 | (c1 << 8)); } } } return(pic); }
internal static byte[] GetImageData8Bit(PmsHeader pms, byte[] bytes, int addressOfData) { byte[] pic = new byte[pms.width * pms.height]; int address = addressOfData; int c0, c1; int x, y, loc, l, i; int scanline = pms.width; for (y = 0; y < pms.height; y++) { for (x = 0; x < pms.width;) { int a0 = address; loc = y * scanline + x; if (bytes.Length >= address) { break; } c0 = bytes[address++]; if (c0 <= 0xf7) { //literal pic[loc] = (byte)c0; x++; } else if (c0 == 0xff) { //copy N+3 bytes from previous scanline l = bytes[address] + 3; x += l; address++; //if (loc - scanline > 0) memcpy(pic, loc, pic, loc - scanline, l); } else if (c0 == 0xfe) { //copy N+3 bytes from two scanlines ago l = bytes[address] + 3; x += l; address++; //if (loc - scanline * 2 > 0) memcpy(pic, loc, pic, loc - scanline * 2, l); } else if (c0 == 0xfd) { //fill with N+4 bytes of the next value l = bytes[address] + 4; x += l; address++; c0 = bytes[address++]; memset(pic, loc, (byte)c0, l); } else if (c0 == 0xfc) { //fill with alternating bytes N+3 times l = (bytes[address] + 3) * 2; x += l; address++; c0 = bytes[address++]; c1 = bytes[address++]; for (i = 0; i < l; i += 2) { pic[loc + i] = (byte)c0; pic[loc + i + 1] = (byte)c1; } } else { //copy byte pic[loc] = bytes[address++]; x++; } if (x > pms.width) { } } } return(pic); }
/* * Do extract 8bit pms image * pms: pms header information * pic: pixel to be stored * b : raw data (pointer to pixel) */ internal static byte[] GetImageData8Bit(PmsHeader pms, byte[] bytes) { return(GetImageData8Bit(pms, bytes, pms.addressOfData)); }
public static void SaveImage(Stream stream, FreeImageBitmap bitmap) { string comment = bitmap.Comment; var pmsHeader = new PmsHeader(); bool readComment = false; if (!string.IsNullOrEmpty(comment)) { if (pmsHeader.ParseComment(comment)) { readComment = true; } } if (pmsHeader.colorDepth == 0) { pmsHeader.colorDepth = 8; } bool is8Bit = !readComment || pmsHeader.colorDepth == 8; if (!readComment) { pmsHeader.version = 1; pmsHeader.headerSize = 48; } if (pmsHeader.signature == 0) { pmsHeader.signature = 0x00004d50; } if (pmsHeader.version == 2 && pmsHeader.headerSize == 0) { pmsHeader.headerSize = 64; } if (pmsHeader.headerSize < 48) { pmsHeader.headerSize = 48; } if (pmsHeader.headerSize > 64) { pmsHeader.headerSize = 64; } pmsHeader.addressOfComment = 0; pmsHeader.height = bitmap.Height; pmsHeader.width = bitmap.Width; if (is8Bit) { if (bitmap.ColorDepth > 8) { if (bitmap.ColorDepth == 32) { bitmap.ConvertColorDepth(FREE_IMAGE_COLOR_DEPTH.FICD_24_BPP); } bitmap.Quantize(FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, 256); //throw new ArgumentException("image must be 8-bit"); } pmsHeader.addressOfPalette = pmsHeader.headerSize; pmsHeader.addressOfData = pmsHeader.addressOfPalette + 768; pmsHeader.colorDepth = 8; SaveHeader(stream, pmsHeader); SavePalette(stream, bitmap.Palette); SaveImageData8Bit(stream, bitmap); } else { bool hasAlphaChannel = false; var existingAlphaChannel = bitmap.GetChannel(FREE_IMAGE_COLOR_CHANNEL.FICC_ALPHA); if (existingAlphaChannel != null) { bool allPixelsOpaque = AllPixelsOpaque(existingAlphaChannel); hasAlphaChannel = !allPixelsOpaque; } if (bitmap.ColorDepth != 32) { bitmap.ConvertColorDepth(FREE_IMAGE_COLOR_DEPTH.FICD_32_BPP); } bool usingAlphaChannel = pmsHeader.shadowDepth == 8 || (pmsHeader.shadowDepth == 0 && hasAlphaChannel); pmsHeader.addressOfPalette = 0; pmsHeader.addressOfData = pmsHeader.headerSize; pmsHeader.colorDepth = 16; ushort[] image16 = new ushort[pmsHeader.width * pmsHeader.height]; byte[] image8 = null; if (usingAlphaChannel) { image8 = new byte[pmsHeader.width * pmsHeader.height]; } int o = 0; for (int y = 0; y < pmsHeader.height; y++) { var scanline = bitmap.GetScanlineFromTop32Bit(y); if (image8 == null) { for (int x = 0; x < pmsHeader.width; x++) { unchecked { int p = scanline[x]; int b = (p >> 0) & 0xFF; int g = (p >> 8) & 0xFF; int r = (p >> 16) & 0xFF; //int a = (p >> 24) & 0xFF; b = (b * 0x1F1F + 0x8000) >> 16; g = (g * 0x3F3F + 0x8000) >> 16; r = (r * 0x1F1F + 0x8000) >> 16; p = (b << 0) | (g << 5) | (r << 11); image16[o] = (ushort)p; o++; } } } else { for (int x = 0; x < pmsHeader.width; x++) { unchecked { int p = scanline[x]; int b = (p >> 0) & 0xFF; int g = (p >> 8) & 0xFF; int r = (p >> 16) & 0xFF; int a = (p >> 24) & 0xFF; b = (b * 0x1F1F + 0x8000) >> 16; g = (g * 0x3F3F + 0x8000) >> 16; r = (r * 0x1F1F + 0x8000) >> 16; p = (b << 0) | (g << 5) | (r << 11); image16[o] = (ushort)p; image8[o] = (byte)a; o++; } } } } MemoryStream ms = new MemoryStream(); SaveImageData16Bit(ms, image16, pmsHeader.width, pmsHeader.height); int imageSize = (int)ms.Length; if (usingAlphaChannel) { pmsHeader.addressOfPalette = imageSize + pmsHeader.addressOfData; } SaveHeader(stream, pmsHeader); ms.WriteTo(stream); ms.SetLength(0); if (usingAlphaChannel) { SaveImageData8Bit(stream, image8, pmsHeader.width, pmsHeader.height); } } }