protected override void Run(Workbench workbench, ILogger logger) { int maxTilesPerRow = FindNamedParameter("--colors-per-row").Values[0].ToInt32(); if (maxTilesPerRow < 0) { logger?.Log("Invalid colors per row.", LogLevel.Error); return; } int tw = 8; int th = 8; int maxColors = workbench.PaletteSet.Max(pe => pe.Palette.Count); int palCount = workbench.PaletteSet.Count; if (maxTilesPerRow == 0) { maxTilesPerRow = maxColors; } int w = maxTilesPerRow; int h = workbench.PaletteSet.Sum(pe => (pe.Palette.Count + maxTilesPerRow - 1) / maxTilesPerRow); if (w <= 0 || h <= 0) { logger?.Log("Cannot render empty palette set.", LogLevel.Error); return; } int count = 0; ColorFormat fmt = ColorFormat.BGRA8888; workbench.ClearBitmap(w * tw, h * th); workbench.Graphics.CompositingMode = CompositingMode.SourceCopy; using (SolidBrush brush = new SolidBrush(Color.Black)) { int p = 0; int c = 0; for (int j = 0; j < h; j++) { Palette pal = workbench.PaletteSet[p].Palette; for (int i = 0; i < w; i++) { // Draw transparent if we ran out of colors. if (c >= pal.Count) { brush.Color = Color.Transparent; } else { brush.Color = Color.FromArgb(fmt.Convert(pal[c++], pal.Format)); } workbench.Graphics.FillRectangle(brush, i * tw, j * th, tw, th); count++; } // Go to next palette. if (c >= pal.Count) { p++; c = 0; } } } workbench.Graphics.Flush(); logger?.Log("Rendered " + w + "x" + h + " palette set containing " + count + " colors.", LogLevel.Information); }
private static int DeduplicatePalette(Palette palette, Random rng, ILogger logger) { // Create sequential color format to remove alpha channel and padding. ColorFormat seqFmt = ColorFormat.SequentialBGRA( palette.Format.RedBits, palette.Format.GreenBits, palette.Format.BlueBits, 0 ); int n = seqFmt.MaxValue; if (palette.Count > n) { logger?.Log("Cannot deduplicate palette; palette size exceeds color space.", LogLevel.Error); return(0); } // Set of distinct colors with lookup O(1). // Use Dictionary instead of HashSet to pre-allocate capacity. Dictionary <int, int> colToIdxDict = new Dictionary <int, int>(palette.Count); // Put colors in the dictionary and count dupes. List <int> dupes = new List <int>(); for (int i = 0; i < palette.Count; i++) { int c = seqFmt.Convert(palette[i], palette.Format); if (!colToIdxDict.ContainsKey(c)) { colToIdxDict[c] = i; } else { dupes.Add(i); } } // Sort colors ascending. List <int> colSet = colToIdxDict.Keys.ToList(); colSet.Sort(); // For each dupe, add a new color. foreach (int dupe in dupes) { int?candidate = RandomIntByBinarySearch(colSet, rng, n); if (candidate is int c) { colSet.Add(c); colSet.Sort(); palette[dupe] = palette.Format.Convert(c, seqFmt); } else { logger?.Log("Failed to deduplicate palette .", LogLevel.Error); return(0); } } return(dupes.Count); }