Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/NesTiler.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2022-10-27 17:30:40 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2022-10-27 17:30:40 +0300
commit7eef9314dd24ba73bb40e058b50d4f4e7bce1035 (patch)
tree766f59cf1e86613fc1c01f097f8055a0a9f020aa
parentdfd7e9a652514fe6abf5d84e2f1736036afdcc1c (diff)
Great optimization.
-rw-r--r--NesTiler/ColorExtensions.cs20
-rw-r--r--NesTiler/ColorFinder.cs35
-rw-r--r--NesTiler/Config.cs9
-rw-r--r--NesTiler/FastBitmap.cs11
-rw-r--r--NesTiler/Palette.cs39
-rw-r--r--NesTiler/Program.cs20
6 files changed, 72 insertions, 62 deletions
diff --git a/NesTiler/ColorExtensions.cs b/NesTiler/ColorExtensions.cs
index 88f973a..70f8bc0 100644
--- a/NesTiler/ColorExtensions.cs
+++ b/NesTiler/ColorExtensions.cs
@@ -1,5 +1,6 @@
using ColorMine.ColorSpaces;
using ColorMine.ColorSpaces.Comparisons;
+using SkiaSharp;
using System.Collections.Generic;
using System.Drawing;
@@ -7,8 +8,8 @@ namespace com.clusterrr.Famicom.NesTiler
{
public record ColorPair
{
- public Color Color1;
- public Color Color2;
+ public SKColor Color1;
+ public SKColor Color2;
}
static class ColorExtensions
@@ -17,7 +18,16 @@ namespace com.clusterrr.Famicom.NesTiler
public static void ClearCache() => cache.Clear();
- public static double GetDelta(this Color color1, Color color2)
+ public static Color ToColor(this SKColor color)
+ => Color.FromArgb((int)color.ToArgb());
+
+ public static SKColor ToSKColor(this Color color)
+ => new SKColor((uint)color.ToArgb());
+
+ public static uint ToArgb(this SKColor color)
+ => (uint)((color.Alpha << 24) | (color.Red << 16) | (color.Green << 8) | color.Blue);
+
+ public static double GetDelta(this SKColor color1, SKColor color2)
{
var pair = new ColorPair()
{
@@ -26,8 +36,8 @@ namespace com.clusterrr.Famicom.NesTiler
};
if (cache.ContainsKey(pair))
return cache[pair];
- var a = new Rgb { R = color1.R, G = color1.G, B = color1.B };
- var b = new Rgb { R = color2.R, G = color2.G, B = color2.B };
+ var a = new Rgb { R = color1.Red, G = color1.Green, B = color1.Blue };
+ var b = new Rgb { R = color2.Red, G = color2.Green, B = color2.Blue };
var delta = a.Compare(b, new CieDe2000Comparison());
cache[pair] = delta;
return delta;
diff --git a/NesTiler/ColorFinder.cs b/NesTiler/ColorFinder.cs
index a6f55b0..748f844 100644
--- a/NesTiler/ColorFinder.cs
+++ b/NesTiler/ColorFinder.cs
@@ -13,28 +13,28 @@ namespace com.clusterrr.Famicom.NesTiler
{
static byte[] FORBIDDEN_COLORS = new byte[] { 0x0D, 0x0E, 0x0F, 0x1E, 0x1F, 0x2E, 0x2F, 0x3E, 0x3F };
- public readonly Dictionary<byte, Color> Colors;
- private readonly Dictionary<Color, byte> cache = new();
+ public readonly Dictionary<byte, SKColor> Colors;
+ private readonly Dictionary<SKColor, byte> cache = new();
public ColorFinder(string filename)
{
this.Colors = LoadColors(filename);
}
- private static Dictionary<byte, Color> LoadColors(string filename)
+ private static Dictionary<byte, SKColor> LoadColors(string filename)
{
Trace.WriteLine($"Loading colors from {filename}...");
if (!File.Exists(filename)) throw new FileNotFoundException($"Could not find file '{filename}'.", filename);
var data = File.ReadAllBytes(filename);
- Dictionary<byte, Color> nesColors;
+ Dictionary<byte, SKColor> nesColors;
// Detect file type
if ((Path.GetExtension(filename) == ".pal") || ((data.Length == 192 || data.Length == 1536) && data.Where(b => b >= 128).Any()))
{
// Binary file
- nesColors = new Dictionary<byte, Color>();
+ nesColors = new Dictionary<byte, SKColor>();
for (byte c = 0; c < 64; c++)
{
- var color = Color.FromArgb(data[c * 3], data[(c * 3) + 1], data[(c * 3) + 2]);
+ var color = new SKColor(data[c * 3], data[(c * 3) + 1], data[(c * 3) + 2]);
nesColors[c] = color;
}
}
@@ -63,7 +63,8 @@ namespace com.clusterrr.Famicom.NesTiler
{
try
{
- return ColorTranslator.FromHtml(kv.Value);
+ var color = ColorTranslator.FromHtml(kv.Value); ;
+ return new SKColor(color.R, color.G, color.B);
}
catch (FormatException)
{
@@ -82,13 +83,13 @@ namespace com.clusterrr.Famicom.NesTiler
/// </summary>
/// <param name="color">Input color</param>
/// <returns>Output color index</returns>
- public byte FindSimilarColorIndex(Color color)
+ public byte FindSimilarColorIndex(SKColor color)
{
if (cache.ContainsKey(color))
return cache[color];
byte result = byte.MaxValue;
double minDelta = double.MaxValue;
- Color c = Color.Transparent;
+ SKColor c = SKColors.Transparent;
foreach (var index in Colors.Keys)
{
var delta = color.GetDelta(Colors[index]);
@@ -112,9 +113,9 @@ namespace com.clusterrr.Famicom.NesTiler
/// <param name="colors">Haystack</param>
/// <param name="color">Niddle</param>
/// <returns>Output color</returns>
- public Color FindSimilarColor(IEnumerable<Color> colors, Color color)
+ public SKColor FindSimilarColor(IEnumerable<SKColor> colors, SKColor color)
{
- Color result = Color.Black;
+ SKColor result = SKColors.Black;
double minDelta = double.MaxValue;
foreach (var c in colors)
{
@@ -133,7 +134,7 @@ namespace com.clusterrr.Famicom.NesTiler
/// </summary>
/// <param name="color">Input colo</param>
/// <returns>Output color</returns>
- public Color FindSimilarColor(Color color) => Colors[FindSimilarColorIndex(color)];
+ public SKColor FindSimilarColor(SKColor color) => Colors[FindSimilarColorIndex(color)];
public void WriteColorsTable(string filename)
{
@@ -150,19 +151,17 @@ namespace com.clusterrr.Famicom.NesTiler
{
for (int x = 0; x < colorColumns; x++)
{
- Color color;
- SKColor skColor;
+ SKColor color;
SKPaint paint;
if (Colors.TryGetValue((byte)((y * colorColumns) + x), out color))
{
- skColor = new SKColor(color.R, color.G, color.B);
- paint = new SKPaint() { Color = skColor };
+ paint = new SKPaint() { Color = color };
canvas.DrawRegion(new SKRegion(new SKRectI(x * colorSize, y * colorSize, (x + 1) * colorSize, (y + 1) * colorSize)), paint);
- skColor = new SKColor((byte)(0xFF - color.R), (byte)(0xFF - color.G), (byte)(0xFF - color.B)); // invert color
+ color = new SKColor((byte)(0xFF - color.Red), (byte)(0xFF - color.Green), (byte)(0xFF - color.Blue)); // invert color
paint = new SKPaint()
{
- Color = skColor,
+ Color = color,
TextAlign = SKTextAlign.Center,
TextSize = textSize,
FilterQuality = SKFilterQuality.High,
diff --git a/NesTiler/Config.cs b/NesTiler/Config.cs
index 9916a3b..3046748 100644
--- a/NesTiler/Config.cs
+++ b/NesTiler/Config.cs
@@ -1,4 +1,5 @@
-using System;
+using SkiaSharp;
+using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
@@ -20,7 +21,7 @@ namespace com.clusterrr.Famicom.NesTiler
public string ColorsFile { get; private set; }
public Dictionary<int, string> ImageFiles { get; private set; } = new Dictionary<int, string>();
- public Color? BgColor { get; private set; } = null;
+ public SKColor? BgColor { get; private set; } = null;
public bool[] PaletteEnabled { get; private set; } = new bool[4] { true, true, true, true };
public Palette?[] FixedPalettes { get; private set; } = new Palette?[4] { null, null, null, null };
public TilesMode Mode { get; private set; } = TilesMode.Backgrounds;
@@ -125,7 +126,7 @@ namespace com.clusterrr.Famicom.NesTiler
{
try
{
- config.BgColor = ColorTranslator.FromHtml(value);
+ config.BgColor = ColorTranslator.FromHtml(value).ToSKColor();
}
catch (FormatException)
{
@@ -156,7 +157,7 @@ namespace com.clusterrr.Famicom.NesTiler
case ArgPalette.S:
case ArgPalette.L:
{
- var colors = value.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(c => ColorTranslator.FromHtml(c));
+ var colors = value.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(c => ColorTranslator.FromHtml(c).ToSKColor());
config.FixedPalettes[indexNum] = new Palette(colors);
}
i++;
diff --git a/NesTiler/FastBitmap.cs b/NesTiler/FastBitmap.cs
index 07b33e5..1015354 100644
--- a/NesTiler/FastBitmap.cs
+++ b/NesTiler/FastBitmap.cs
@@ -11,7 +11,7 @@ namespace com.clusterrr.Famicom.NesTiler
public int Width { get; }
public int Height { get; }
- private readonly Color[] colors;
+ private readonly SKColor[] colors;
private static Dictionary<string, SKBitmap> imagesCache = new Dictionary<string, SKBitmap>();
private FastBitmap(SKBitmap skBitmap, int verticalOffset = 0, int height = -1)
@@ -20,7 +20,7 @@ namespace com.clusterrr.Famicom.NesTiler
Height = height <= 0 ? skBitmap.Height - verticalOffset : height;
if (skBitmap.Height - verticalOffset - Height < 0 || Height <= 0) throw new InvalidOperationException("Invalid image height.");
var pixels = skBitmap.Pixels;
- colors = skBitmap.Pixels.Skip(verticalOffset * Width).Take(Width * Height).Select(p => Color.FromArgb(p.Alpha, p.Red, p.Green, p.Blue)).ToArray();
+ colors = skBitmap.Pixels.Skip(verticalOffset * Width).Take(Width * Height).ToArray();
}
public static FastBitmap? Decode(string filename, int verticalOffset = 0, int height = -1)
@@ -40,12 +40,12 @@ namespace com.clusterrr.Famicom.NesTiler
}
}
- public Color GetPixelColor(int x, int y)
+ public SKColor GetPixelColor(int x, int y)
{
return colors[(y * Width) + x];
}
- public void SetPixelColor(int x, int y, Color color)
+ public void SetPixelColor(int x, int y, SKColor color)
{
colors[(y * Width) + x] = color;
}
@@ -58,8 +58,7 @@ namespace com.clusterrr.Famicom.NesTiler
for (int x = 0; x < Width; x++)
{
var color = colors[(y * Width) + x];
- var skColor = new SKColor(color.R, color.G, color.B);
- skImage.SetPixel(x, y, skColor);
+ skImage.SetPixel(x, y, color);
}
}
return skImage.Encode(format, v).ToArray();
diff --git a/NesTiler/Palette.cs b/NesTiler/Palette.cs
index 5b8703f..44cb940 100644
--- a/NesTiler/Palette.cs
+++ b/NesTiler/Palette.cs
@@ -1,4 +1,5 @@
-using System;
+using SkiaSharp;
+using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
@@ -7,12 +8,12 @@ using Color = System.Drawing.Color;
namespace com.clusterrr.Famicom.NesTiler
{
- class Palette : IEquatable<Palette>, IEnumerable<Color>
+ class Palette : IEquatable<Palette>, IEnumerable<SKColor>
{
- private Color?[] colors = new Color?[3];
- private Dictionary<ColorPair, (Color color, double delta)> deltaCache = new();
+ private SKColor?[] colors = new SKColor?[3];
+ private Dictionary<ColorPair, (SKColor color, double delta)> deltaCache = new();
- public Color? this[int i]
+ public SKColor? this[int i]
{
get
{
@@ -32,9 +33,9 @@ namespace com.clusterrr.Famicom.NesTiler
// Empty palette
}
- public Palette(FastBitmap image, int leftX, int topY, int width, int height, Color bgColor)
+ public Palette(FastBitmap image, int leftX, int topY, int width, int height, SKColor bgColor)
{
- Dictionary<Color, int> colorCounter = new Dictionary<Color, int>();
+ Dictionary<SKColor, int> colorCounter = new();
for (int y = topY; y < topY + height; y++)
{
if (y < 0) continue;
@@ -54,21 +55,21 @@ namespace com.clusterrr.Famicom.NesTiler
this[i + 1] = sortedColors[i].Key;
}
- public void Add(Color color)
+ public void Add(SKColor color)
{
if (Count >= 3) throw new IndexOutOfRangeException();
this[Count + 1] = color;
deltaCache.Clear();
}
- public Palette(IEnumerable<Color> colors)
+ public Palette(IEnumerable<SKColor> colors)
{
var colorsList = colors.ToList();
for (int i = 0; i < 3; i++)
if (colorsList.Count > i) this[i + 1] = colorsList[i];
}
- public double GetTileDelta(FastBitmap image, int leftX, int topY, int width, int height, Color bgColor)
+ public double GetTileDelta(FastBitmap image, int leftX, int topY, int width, int height, SKColor bgColor)
{
double delta = 0;
for (int y = topY; y < topY + height; y++)
@@ -83,7 +84,7 @@ namespace com.clusterrr.Famicom.NesTiler
return delta;
}
- private (Color color, double delta) GetMinDelta(Color color, Color bgColor)
+ private (SKColor color, double delta) GetMinDelta(SKColor color, SKColor bgColor)
{
var pair = new ColorPair()
{
@@ -91,7 +92,7 @@ namespace com.clusterrr.Famicom.NesTiler
Color2 = bgColor
};
if (deltaCache.ContainsKey(pair)) return deltaCache[pair];
- var ac = Enumerable.Concat(colors.Where(c => c.HasValue).Select(c => c!.Value), new Color[] { bgColor });
+ var ac = Enumerable.Concat(colors.Where(c => c.HasValue).Select(c => c!.Value), new SKColor[] { bgColor });
var result = ac.OrderBy(c => c.GetDelta(color)).First();
var r = (result, result.GetDelta(color));
deltaCache[pair] = r;
@@ -105,7 +106,7 @@ namespace com.clusterrr.Famicom.NesTiler
.OrderBy(c => c!.Value.ToArgb())
.Select(c => c!.Value)
.ToArray();
- var colors2 = new Color?[] { other[1], other[2], other[3] }
+ var colors2 = new SKColor?[] { other[1], other[2], other[3] }
.Where(c => c.HasValue)
.OrderBy(c => c!.Value.ToArgb())
.Select(c => c!.Value)
@@ -116,7 +117,7 @@ namespace com.clusterrr.Famicom.NesTiler
public bool Contains(Palette other)
{
var thisColors = colors.Where(c => c.HasValue);
- var otherColors = new Color?[] { other[1], other[2], other[3] }.Where(c => c.HasValue).Select(c => c!.Value);
+ var otherColors = new SKColor?[] { other[1], other[2], other[3] }.Where(c => c.HasValue).Select(c => c!.Value);
foreach (var color in otherColors)
{
@@ -126,7 +127,7 @@ namespace com.clusterrr.Famicom.NesTiler
return true;
}
- public IEnumerator<Color> GetEnumerator()
+ public IEnumerator<SKColor> GetEnumerator()
{
return colors.Where(c => c.HasValue).Select(c => c!.Value).GetEnumerator();
}
@@ -136,13 +137,13 @@ namespace com.clusterrr.Famicom.NesTiler
return GetEnumerator();
}
- public override string ToString() => string.Join(", ", colors.Where(c => c.HasValue).Select(c => ColorTranslator.ToHtml(c!.Value)).OrderBy(c => c));
+ public override string ToString() => string.Join(", ", colors.Where(c => c.HasValue).Select(c => ColorTranslator.ToHtml(c!.Value.ToColor())).OrderBy(c => c));
public override int GetHashCode()
{
- return ((this[1]?.R ?? 0) + (this[2]?.R ?? 0) + (this[3]?.R ?? 0))
- | (((this[1]?.G ?? 0) + (this[2]?.G ?? 0) + (this[3]?.G ?? 0)) << 10)
- | (((this[1]?.B ?? 0) + (this[2]?.B ?? 0) + (this[3]?.B ?? 0)) << 20);
+ return ((this[1]?.Red ?? 0) + (this[2]?.Red ?? 0) + (this[3]?.Red ?? 0))
+ | (((this[1]?.Green ?? 0) + (this[2]?.Green ?? 0) + (this[3]?.Green ?? 0)) << 10)
+ | (((this[1]?.Blue ?? 0) + (this[2]?.Blue ?? 0) + (this[3]?.Blue ?? 0)) << 20);
}
}
}
diff --git a/NesTiler/Program.cs b/NesTiler/Program.cs
index 92c6d12..fde58db 100644
--- a/NesTiler/Program.cs
+++ b/NesTiler/Program.cs
@@ -139,7 +139,7 @@ namespace com.clusterrr.Famicom.NesTiler
for (int x = 0; x < image.Width; x++)
{
var color = image.GetPixelColor(x, y);
- if (color.A >= 0x80 || c.Mode == Config.TilesMode.Backgrounds)
+ if (color.Alpha >= 0x80 || c.Mode == Config.TilesMode.Backgrounds)
{
var similarColor = nesColors.FindSimilarColor(color);
image.SetPixelColor(x, y, similarColor);
@@ -156,7 +156,7 @@ namespace com.clusterrr.Famicom.NesTiler
List<Palette> calculatedPalettes;
var maxCalculatedPaletteCount = Enumerable.Range(0, 4)
.Select(i => c.PaletteEnabled[i] && c.FixedPalettes[i] == null).Count();
- Color bgColor;
+ SKColor bgColor;
// Detect background color
if (c.BgColor.HasValue)
{
@@ -174,7 +174,7 @@ namespace com.clusterrr.Famicom.NesTiler
{
// Autodetect most used color
Trace.Write($"Background color autodetect... ");
- Dictionary<Color, int> colorPerTileCounter = new Dictionary<Color, int>();
+ Dictionary<SKColor, int> colorPerTileCounter = new();
foreach (var imageNum in images.Keys)
{
var image = images[imageNum];
@@ -183,7 +183,7 @@ namespace com.clusterrr.Famicom.NesTiler
for (int tileX = 0; tileX < image.Width / c.TilePalWidth; tileX++)
{
// Count each color only once per tile/sprite
- var colorsInTile = new List<Color>();
+ var colorsInTile = new List<SKColor>();
for (int y = 0; y < c.TilePalHeight; y++)
{
for (int x = 0; x < c.TilePalWidth; x++)
@@ -206,7 +206,7 @@ namespace com.clusterrr.Famicom.NesTiler
// Most used colors
var candidates = colorPerTileCounter.OrderByDescending(kv => kv.Value).Select(kv => kv.Key).ToArray();
// Try to calculate palettes for every background color
- var calcResults = new Dictionary<Color, Palette[]>();
+ var calcResults = new Dictionary<SKColor, Palette[]>();
for (int i = 0; i < Math.Min(candidates.Length, MAX_BG_COLOR_AUTODETECT_ITERATIONS); i++)
{
calcResults[candidates[i]] = CalculatePalettes(images,
@@ -220,7 +220,7 @@ namespace com.clusterrr.Famicom.NesTiler
// Select background color which uses minimum palettes
var kv = calcResults.OrderBy(kv => kv.Value.Length).First();
(bgColor, calculatedPalettes) = (kv.Key, kv.Value.ToList());
- Trace.WriteLine(ColorTranslator.ToHtml(bgColor));
+ Trace.WriteLine(ColorTranslator.ToHtml(bgColor.ToColor()));
}
if (calculatedPalettes.Count > maxCalculatedPaletteCount && !c.Lossy)
@@ -254,9 +254,9 @@ namespace com.clusterrr.Famicom.NesTiler
if (palettes[i] != null)
{
- Trace.WriteLine($"Palette #{i}: {ColorTranslator.ToHtml(bgColor)}(BG) {string.Join(" ", palettes[i]!.Select(p => ColorTranslator.ToHtml(p)))}");
+ Trace.WriteLine($"Palette #{i}: {ColorTranslator.ToHtml(bgColor.ToColor())}(BG) {string.Join(" ", palettes[i]!.Select(p => ColorTranslator.ToHtml(p.ToColor())))}");
// Write CSV if required
- outPalettesCsvLines?.Add($"{i},{ColorTranslator.ToHtml(bgColor)},{string.Join(",", Enumerable.Range(1, 3).Select(c => (palettes[i]![c] != null ? ColorTranslator.ToHtml(palettes[i]![c]!.Value) : "")))}");
+ outPalettesCsvLines?.Add($"{i},{ColorTranslator.ToHtml(bgColor.ToColor())},{string.Join(",", Enumerable.Range(1, 3).Select(c => (palettes[i]![c] != null ? ColorTranslator.ToHtml(palettes[i]![c]!.Value.ToColor()) : "")))}");
}
}
}
@@ -324,7 +324,7 @@ namespace com.clusterrr.Famicom.NesTiler
var color = image.GetPixelColor((tilePalX * c.TilePalWidth) + x, cy);
var similarColor = nesColors.FindSimilarColor(Enumerable.Concat(
bestPalette,
- new Color[] { bgColor }
+ new SKColor[] { bgColor }
), color);
image.SetPixelColor(
(tilePalX * c.TilePalWidth) + x,
@@ -539,7 +539,7 @@ namespace com.clusterrr.Famicom.NesTiler
}
}
- static Palette[] CalculatePalettes(Dictionary<int, FastBitmap> images, bool[] paletteEnabled, Palette?[] fixedPalettes, Dictionary<int, int> attributeTableOffsets, int tilePalWidth, int tilePalHeight, Color bgColor)
+ static Palette[] CalculatePalettes(Dictionary<int, FastBitmap> images, bool[] paletteEnabled, Palette?[] fixedPalettes, Dictionary<int, int> attributeTableOffsets, int tilePalWidth, int tilePalHeight, SKColor bgColor)
{
var required = Enumerable.Range(0, 4).Select(i => paletteEnabled[i] && fixedPalettes[i] == null);
// Creating and counting the palettes