using System; namespace Oni.Imaging { internal static class Dxt1 { public static Surface Decompress(Surface src, SurfaceFormat dstFormat) { var dst = new Surface(src.Width, src.Height, dstFormat); var colors = new Color[4]; int srcOffset = 0; for (int y = 0; y < dst.Height; y += 4) { for (int x = 0; x < dst.Width; x += 4) { colors[0] = Color.ReadBgr565(src.Data, srcOffset); srcOffset += 2; colors[1] = Color.ReadBgr565(src.Data, srcOffset); srcOffset += 2; if (colors[0].ToBgr565() > colors[1].ToBgr565()) { colors[2] = Color.Lerp(colors[0], colors[1], 1.0f / 3.0f); colors[3] = Color.Lerp(colors[0], colors[1], 2.0f / 3.0f); } else { colors[2] = Color.Lerp(colors[0], colors[1], 0.5f); colors[3] = Color.Transparent; } for (int y2 = 0; y2 < 4; y2++) { int packedLookup = src.Data[srcOffset++]; for (int x2 = 0; x2 < 4; x2++) { dst[x + x2, y + y2] = colors[packedLookup & 3]; packedLookup >>= 2; } } } } return dst; } public static Surface Compress(Surface src) { var dst = new Surface(Utils.Align4(src.Width), Utils.Align4(src.Height), SurfaceFormat.DXT1); var block = new Vector3[16]; var colors = new Vector3[4]; var lookup = new int[16]; int dstOffset = 0; int height = dst.Height; int width = dst.Width; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int by = 0; by < 4; by++) { for (int bx = 0; bx < 4; bx++) block[by * 4 + bx] = src[x + bx, y + by].ToVector3(); } CompressBlock(block, lookup, colors); Color.WriteBgr565(new Color(colors[0]), dst.Data, dstOffset); dstOffset += 2; Color.WriteBgr565(new Color(colors[1]), dst.Data, dstOffset); dstOffset += 2; for (int by = 0; by < 4; by++) { int packedLookup = 0; for (int bx = 3; bx >= 0; bx--) packedLookup = (packedLookup << 2) | lookup[by * 4 + bx]; dst.Data[dstOffset++] = (byte)packedLookup; } } } return dst; } private static void CompressBlock(Vector3[] block, int[] lookup, Vector3[] colors) { colors[0] = block[0]; colors[1] = block[0]; for (int i = 1; i < block.Length; i++) { colors[0] = Vector3.Min(colors[0], block[i]); colors[1] = Vector3.Max(colors[1], block[i]); } int maxColor; if (new Color(colors[0]).ToBgr565() > new Color(colors[1]).ToBgr565()) { colors[2] = Vector3.Lerp(colors[0], colors[1], 1.0f / 3.0f); colors[3] = Vector3.Lerp(colors[0], colors[1], 2.0f / 3.0f); maxColor = 4; } else { colors[2] = Vector3.Lerp(colors[0], colors[1], 0.5f); maxColor = 3; } for (int i = 0; i < block.Length; i++) { lookup[i] = LookupNearest(colors, block[i], maxColor); } } private static int LookupNearest(Vector3[] colors, Vector3 pixel, int maxColor) { int index = 0; float ds = Vector3.DistanceSquared(pixel, colors[0]); for (int i = 1; i < maxColor; i++) { float newDs = Vector3.DistanceSquared(pixel, colors[i]); if (newDs < ds) { ds = newDs; index = i; } } return index; } } }