diff --git a/src/Palette.cs b/src/Palette.cs index ebcc528..df3a41a 100644 --- a/src/Palette.cs +++ b/src/Palette.cs @@ -4,81 +4,98 @@ namespace Palettizer { public class Palette { - public Dictionary ColorToGrayscaleMap { get; } - public byte GrayscaleIndex { get; set; } - public List AlternateColorRows { get; } + public Dictionary GrayscaleToColorMap { get; } + public Color[] GrayscaleToColor { get; } + public Dictionary ColorToGrayscaleMap { get; } + public int GrayscaleCount { get; set; } + public List AlternateColorRows { get; } - public int Width { get => ColorToGrayscaleMap.Count; } - public int Height { get => AlternateColorRows.Count + 1; } + public int Width { get => 256; } + public int Height { get => AlternateColorRows.Count + 2; } public Palette() { + GrayscaleToColorMap = new Dictionary(); ColorToGrayscaleMap = new Dictionary(); - GrayscaleIndex = 0; - AlternateColorRows = new List(); + GrayscaleCount = 0; + AlternateColorRows = new List(); } - public void AddColor(Color color, byte grayscaleIndex) + public void AddColor(Color color) { - ColorToGrayscaleMap.Add(color, grayscaleIndex); - } + var average = (color.R + color.G + color.B) / 3; + if (GrayscaleToColorMap.ContainsKey((byte)average)) + { + var increment = 1; + for (var i = 0; i < 256 - GrayscaleCount; i += 1) + { + average += increment; + + if (!GrayscaleToColorMap.ContainsKey((byte)average)) + { + break; + } + + increment = ((increment > 0) ? (increment + 1) : (increment - 1)) * -1; + } + + } + + GrayscaleToColorMap.Add((byte)average, color); + ColorToGrayscaleMap.Add(color, (byte)average); + GrayscaleCount += 1; + } public void AddAlternateColorRow(Color[] colors) { - var byteArray = new byte[ColorToGrayscaleMap.Count * 4]; + var byteArray = new byte[256 * 4]; - foreach (var color in colors) + for (var i = 0; i < 256; i += 1) { - if (ColorToGrayscaleMap.ContainsKey(color)) - { - var grayscaleIndex = ColorToGrayscaleMap[color]; + byteArray[i * 4] = colors[i].R; + byteArray[i * 4 + 1] = colors[i].G; + byteArray[i * 4 + 2] = colors[i].B; + byteArray[i * 4 + 3] = colors[i].A; + } - byteArray[grayscaleIndex] = color.R; - byteArray[grayscaleIndex + 1] = color.G; - byteArray[grayscaleIndex + 2] = color.B; - byteArray[grayscaleIndex + 3] = color.A; - } - else - { - System.Console.WriteLine($"Color {color.ToString()} doesn't exist in the grayscale palette!! Bailing!!!"); - return; - } - } - - AlternateColorRows.Add(byteArray); + AlternateColorRows.Add(byteArray); } public byte[] CreateIndexedPaletteBitmap() { - var paletteBitmap = new byte[ColorToGrayscaleMap.Count * 4 * (AlternateColorRows.Count + 1)]; + var paletteBitmap = new byte[256 * (AlternateColorRows.Count + 2) * 4]; - var index = 0; - foreach (KeyValuePair kv in ColorToGrayscaleMap) + for (var i = 0; i < 256; i += 1) { - var color = kv.Key; - var grayscale = kv.Value; + paletteBitmap[i * 4] = (byte)i; + paletteBitmap[i * 4 + 1] = (byte)i; + paletteBitmap[i * 4 + 2] = (byte)i; + paletteBitmap[i * 4 + 3] = 255; + } - paletteBitmap[index] = grayscale; - paletteBitmap[index + 1] = grayscale; - paletteBitmap[index + 2] = grayscale; - paletteBitmap[index + 3] = 255; - - var alternateIndex = 1; - foreach (var alternateColorRow in AlternateColorRows) + for (var i = 0; i < 256; i += 1) + { + var color = new Color(); + if (GrayscaleToColorMap.ContainsKey((byte)i)) { - for (var j = 0; j < ColorToGrayscaleMap.Count * 4; j += 4) - { - paletteBitmap[index + (ColorToGrayscaleMap.Count * alternateIndex)] = alternateColorRow[j]; - paletteBitmap[index + 1 + (ColorToGrayscaleMap.Count * alternateIndex)] = alternateColorRow[j + 1]; - paletteBitmap[index + 2 + (ColorToGrayscaleMap.Count * alternateIndex)] = alternateColorRow[j + 2]; - paletteBitmap[index + 3 + (ColorToGrayscaleMap.Count * alternateIndex)] = alternateColorRow[j + 3]; - } + color = GrayscaleToColorMap[(byte)i]; + } + paletteBitmap[(256 + i) * 4] = color.R; + paletteBitmap[(256 + i) * 4 + 1] = color.G; + paletteBitmap[(256 + i) * 4 + 2] = color.B; + paletteBitmap[(256 + i) * 4 + 3] = color.A; + } - alternateIndex += 1; - } - - index += 4; - } + for (var i = 0; i < 256; i += 1) + { + for (var j = 0; j < AlternateColorRows.Count; j += 1) + { + paletteBitmap[(i + (256 * (j + 2))) * 4] = AlternateColorRows[j][i * 4]; + paletteBitmap[(i + (256 * (j + 2))) * 4 + 1] = AlternateColorRows[j][i * 4 + 1]; + paletteBitmap[(i + (256 * (j + 2))) * 4 + 2] = AlternateColorRows[j][i * 4 + 2]; + paletteBitmap[(i + (256 * (j + 2))) * 4 + 3] = AlternateColorRows[j][i * 4 + 3]; + } + } return paletteBitmap; } diff --git a/src/Program.cs b/src/Program.cs index 2932eb3..604cb93 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Invocation; using System.IO; @@ -114,24 +115,46 @@ namespace Palettizer { ImageResult paletteImage = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha); - for (var j = 0; j < paletteImage.Width; j += 1) + var indexToGrayscaleMap = new Dictionary(); + + // build the color index + for (var i = 0; i < paletteImage.Height; i += 1) + { + var sourceColor = new Color + { + R = paletteImage.Data[i * paletteImage.Width * 4], + G = paletteImage.Data[i * paletteImage.Width * 4 + 1], + B = paletteImage.Data[i * paletteImage.Width * 4 + 2], + A = paletteImage.Data[i * paletteImage.Width * 4 + 3] + }; + + if (palette.ColorToGrayscaleMap.ContainsKey(sourceColor)) + { + indexToGrayscaleMap.Add(i, palette.ColorToGrayscaleMap[sourceColor]); + } + } + + for (var j = 1; j < paletteImage.Width; j += 1) { - var colorPalette = new Color[paletteImage.Height]; + var colorPalette = new Color[256]; for (var i = 0; i < paletteImage.Height; i += 1) { - colorPalette[i] = new Color + if (!indexToGrayscaleMap.ContainsKey(i)) { continue; } + var grayscale = indexToGrayscaleMap[i]; + + colorPalette[grayscale] = new Color { - R = paletteImage.Data[j * 4 + i * paletteImage.Width * 4], - G = paletteImage.Data[(j * 4 + i * paletteImage.Width * 4) + 1], - B = paletteImage.Data[(j * 4 + i * paletteImage.Width * 4) + 2], - A = paletteImage.Data[(j * 4 + i * paletteImage.Width * 4) + 3] + R = paletteImage.Data[(j + i * paletteImage.Width) * 4], + G = paletteImage.Data[(j + i * paletteImage.Width) * 4 + 1], + B = paletteImage.Data[(j + i * paletteImage.Width) * 4 + 2], + A = paletteImage.Data[(j + i * paletteImage.Width) * 4 + 3] }; } palette.AddAlternateColorRow(colorPalette); } - } + } // Write final palette sprite to PNG var paletteOutputPath = Path.Combine(outputDir.FullName, textureGroup + "_Palette.png"); @@ -171,15 +194,14 @@ namespace Palettizer } else { - if (palette.GrayscaleIndex == 255) + if (palette.GrayscaleCount == 256) { console.Out.Write("Too many colors! Bailing!\n"); return grayscaleImage; } else { - palette.ColorToGrayscaleMap.Add(color, palette.GrayscaleIndex); - palette.GrayscaleIndex += 1; + palette.AddColor(color); } } }