using System; using System.Collections.Generic; using System.IO; namespace Oni.Imaging { internal static class TgaWriter { public static void Write(Surface surface, string filePath) { surface = surface.Convert(SurfaceFormat.BGRA); var imageType = TgaImageType.TrueColor; byte[] rleData = null; if (surface.Width > 2 && surface.Height > 2) { rleData = Rle32Compress(surface.Width, surface.Height, surface.Data); if (rleData.Length > surface.Data.Length) rleData = null; else imageType = TgaImageType.RleTrueColor; } var header = TgaHeader.Create(surface.Width, surface.Height, imageType); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); using (var stream = File.Create(filePath)) using (var writer = new BinaryWriter(stream)) { header.Write(writer); if (rleData != null) writer.Write(rleData, 0, rleData.Length); else writer.Write(surface.Data, 0, surface.Data.Length); } } private static byte[] Rle32Compress(int width, int height, byte[] sourceData) { var result = new List(); for (int y = height - 1; y >= 0; y--) { int lineOffset = y * width * 4; int lastPixel = BitConverter.ToInt32(sourceData, y * width * 4); int runStart = 0; byte packetType = 64; for (int x = 1; x < width; x++) { int pixel = BitConverter.ToInt32(sourceData, x * 4 + lineOffset); if (pixel == lastPixel) { if (packetType == 0) { Rle32WritePackets(result, packetType, sourceData, runStart, x - 1, lineOffset); runStart = x - 1; } packetType = 128; } else { if (packetType == 128) { Rle32WritePackets(result, packetType, sourceData, runStart, x, lineOffset); runStart = x; } packetType = 0; } lastPixel = pixel; if (x == width - 1) { Rle32WritePackets(result, packetType, sourceData, runStart, width, lineOffset); } } } return result.ToArray(); } private static void Rle32WritePackets(List result, byte packetType, byte[] sourceData, int xStart, int xStop, int lineOffset) { int count = xStop - xStart; if (count == 0) return; int startOffset = (xStart * 4) + lineOffset; if (packetType == 128) { for (; count > 128; count -= 128) { result.Add((byte)(packetType | 127)); for (int i = 0; i < 4; i++) result.Add(sourceData[startOffset + i]); } result.Add((byte)(packetType | (count - 1))); for (int i = 0; i < 4; i++) result.Add(sourceData[startOffset + i]); } else { for (; count > 128; count -= 128) { result.Add((byte)(packetType | 127)); for (int i = 0; i < 4 * 128; i++) result.Add(sourceData[startOffset + i]); startOffset += 4 * 128; } result.Add((byte)(packetType | (count - 1))); for (int i = 0; i < 4 * count; i++) result.Add(sourceData[startOffset + i]); } } } }