public static Texture2D ReadFromFile(GraphicsDevice graphicsDevice, String filename) { // LoadFile var image = new ObeImage(); using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) { using (var br = new BinaryReader(fs)) { br.ReadInt32(); // OBE0 var w = br.ReadInt32(); var h = br.ReadInt32(); var s = br.ReadUInt16(); var pl = br.ReadInt32(); image.Width = w; image.Height = h; image.StartIndex = (ushort)s; image.Palatte = new uint[pl]; for (int i = 0; i < pl; i++) { image.Palatte[i] = br.ReadUInt32(); } var bl = br.ReadInt32(); image.Bytes = br.ReadBytes(bl); } } // Decompress return(Decode(graphicsDevice, image)); }
public static ObeImage Compress(GraphicsDevice device, Texture2D texture) { var colorData = new uint[texture.Width * texture.Height]; texture.GetData(colorData); var rv = new ObeImage { Width = texture.Width, Height = texture.Height }; var colorCount = new Dictionary <uint, uint>(); { for (int i = 0; i < colorData.Length; i++) { if (!colorCount.ContainsKey(colorData[i])) { colorCount.Add(colorData[i], 1); } else { var v = colorCount[colorData[i]]; v++; colorCount[colorData[i]] = v; } } } var results = colorCount.ToList(); results.Sort((a, b) => a.Value <= b.Value ? 1 : -1); var paletteDictionary = new Dictionary <uint, uint>(); var pallete = new uint[results.Count]; { var i = 0; foreach (var keyValuePair in results) { pallete[i] = keyValuePair.Key; paletteDictionary.Add(pallete[i], (uint)i); i++; } } var indexedData = new uint[texture.Width * texture.Height]; { for (int i = 0; i < colorData.Length; i++) { var c = colorData[i]; indexedData[i] = paletteDictionary[c]; } } var diffIndex = new int[texture.Width * texture.Height]; { for (int i = 1; i < diffIndex.Length; i++) { var a = (int)indexedData[i]; var b = (int)indexedData[i - 1]; diffIndex[i - 1] = a - b; } } var opcodes = new List <CompCodeStruct>(); long current = indexedData[0]; for (var i = 0; i < diffIndex.Length; i++) { // Output the opcodes var v = diffIndex[i]; current += (int)v; if (v == 0) { opcodes.Add(new CompCodeStruct(CompCodes.NoChangeX, v, 0)); continue; } if (v >= -128 && v <= 127) { opcodes.Add(new CompCodeStruct(CompCodes.ByteX, v, 0)); continue; } // Shorts are literals var l = (int)indexedData[i]; opcodes.Add(new CompCodeStruct(CompCodes.ShortX, (int)current, 0)); } { for (int opCodeIndex = 0; opCodeIndex < opcodes.Count;) { var rle = (byte)1; for (var j = opCodeIndex + 1; j < opcodes.Count && rle < 32; j++) { var nextOp = opcodes[j].CompCode; if (nextOp == opcodes[opCodeIndex].CompCode) { rle++; } else { break; } } if (opcodes[opCodeIndex].CompCode == CompCodes.ShortX) { if (rle == 1) { opcodes[opCodeIndex].CompCode = CompCodes.ShortOne; } } opcodes[opCodeIndex].Rle = rle; opCodeIndex += rle; } } var output = new byte[indexedData.Length * 4]; // Lets just make this big in case we've made the filesize bigger! var startIndex = indexedData[0]; var outputIndex = 0; { for (int opCodeIndex = 0; opCodeIndex < opcodes.Count;) { switch (opcodes[opCodeIndex].CompCode) { case CompCodes.NoChangeX: { var opNibble = ((byte)opcodes[opCodeIndex].CompCode); var rle = opcodes[opCodeIndex].Rle; var rleNibble = (byte)((rle - 1) << 3); if (rle == 0) { throw new Exception("Unexpected rle 0"); } var writeByte = (byte)(opNibble | rleNibble); output[outputIndex] = writeByte; outputIndex++; opCodeIndex += rle; // No data needs to be written as the RLE count is all we need in this case break; } case CompCodes.ByteX: { var offset = 128; // Write opcode and RLE value var opNibble = ((byte)opcodes[opCodeIndex].CompCode); var rle = (opcodes[opCodeIndex].Rle); var rleNibble = (byte)((rle - 1) << 3); var writeByte = (byte)(opNibble | rleNibble); output[outputIndex] = writeByte; outputIndex++; for (int k = 0; k < rle; k++) { // Write data 1 byte per RLE count var currentData = (byte)(opcodes[opCodeIndex].Data + offset); output[outputIndex] = currentData; outputIndex++; opCodeIndex++; } break; } case CompCodes.ShortOne: case CompCodes.ShortX: // TODO More cases to minimise shortX { // Write opcode and RLE value var opNibble = ((byte)opcodes[opCodeIndex].CompCode); var rle = opcodes[opCodeIndex].Rle; var rleNibble = (byte)((rle - 1) << 3); var writeByte = (byte)(opNibble | rleNibble); output[outputIndex] = writeByte; outputIndex++; for (int k = 0; k < rle; k++) { // Write data (2bytes) var currentData = (short)(opcodes[opCodeIndex].Data); if (opcodes[opCodeIndex].Data < 0) { throw new Exception("Unexpected value"); } // Low byte // High byte output[outputIndex] = (byte)(currentData >> 0); output[outputIndex + 1] = (byte)(currentData >> 8); outputIndex += 2; opCodeIndex++; } break; } default: { throw new Exception("Unknown comp code"); } } } } Console.WriteLine("Length = " + (outputIndex + 1) + " bytes"); //Console.WriteLine(""); // TODO Test decompression rv.Bytes = new byte[outputIndex]; for (int i = 0; i < outputIndex; i++) { rv.Bytes[i] = output[i]; } rv.Palatte = pallete; rv.StartIndex = (ushort)startIndex; return(rv); }
public static unsafe Texture2D Decode(GraphicsDevice device, ObeImage pic) { var colors = new uint[pic.Width * pic.Height]; var palette = pic.Palatte; var output = 0; var currentIndex = (int)pic.StartIndex; colors[0] = palette[currentIndex]; fixed(uint *dstp = &colors[0]) fixed(byte *bp = &pic.Bytes[0]) { var bpoint = bp; for (int input = 0; input < pic.Bytes.Count();) { var decode = (CompCodes)(pic.Bytes[input] & 0x07); switch (decode) { case CompCodes.NoChangeX: { // Write current color x times var rle = (byte)(pic.Bytes[input] >> 3) + 1; input++; var color = palette[currentIndex]; if (rle % 4 == 0) { for (var j = 0; j < rle; j += 4) { colors[output + 0] = color; colors[output + 1] = color; colors[output + 2] = color; colors[output + 3] = color; output += 4; } } else { var r = rle % 2; { for (var j = 0; j < rle - 1; j += 2) { colors[output] = color; colors[output + 1] = color; output += 2; } } if (r == 1) { colors[output] = color; output++; } } break; } case CompCodes.ByteX: { var rle = (byte)(pic.Bytes[input] >> 3) + 1; input++; for (var i = 0; i < rle; i++) { var jump = (pic.Bytes[input] - 128); currentIndex += jump; colors[output] = palette[currentIndex]; output++; input++; } break; } case CompCodes.ShortOne: { input++; var a = *((short *)(bpoint + input)); colors[output] = palette[a]; currentIndex = a; input += 2; output++; break; } case CompCodes.ShortX: { var rle = (byte)(pic.Bytes[input] >> 3) + 1; input++; var r = rle % 2; { for (var j = 0; j < rle - 1; j += 2) { var a = *((short *)(bpoint + input)); colors[output] = palette[a]; var b = *((short *)(bpoint + input + 2)); colors[output + 1] = palette[b]; currentIndex = b; output += 2; input += 4; } } if (r == 1) { currentIndex = *((short *)(bpoint + input)); colors[output] = palette[currentIndex]; output++; input += 2; } break; } default: { throw new Exception("Unexpected CompCode: " + decode + " at index " + input); } } } } var rv = new Texture2D(device, pic.Width, pic.Height); rv.SetData(colors); return(rv); }