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

NesTiler.git/NesTiler.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2020-11-08 18:28:26 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2020-11-08 18:28:26 +0300
commitf8b7349e7b6de6b393c0c8c771c6fd5f3f6b749e (patch)
tree6a1e7ccece05edde0ee93825e8fe78a2dbf7e74f
parent178dc330a7cd04f6e8ad3900274311db05b7ebed (diff)
Optimization
-rw-r--r--FastBitmap.cs118
-rw-r--r--NesTiler.csproj3
-rw-r--r--Palette.cs8
-rw-r--r--Program.cs31
4 files changed, 144 insertions, 16 deletions
diff --git a/FastBitmap.cs b/FastBitmap.cs
new file mode 100644
index 0000000..74162eb
--- /dev/null
+++ b/FastBitmap.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace com.clusterrr.Famicom.NesTiler
+{
+ public class FastBitmap : IDisposable
+ {
+ readonly Bitmap bitmap;
+ BitmapData data;
+
+ public int Width { get => bitmap.Width; }
+ public int Height { get => bitmap.Height; }
+
+ public FastBitmap(Bitmap bitmap)
+ {
+ //if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
+ // throw new FormatException($"Invalid pixel format, only {PixelFormat.Format32bppArgb} is supported");
+ this.bitmap = bitmap;
+ Lock();
+ }
+
+ private void Lock()
+ {
+ data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
+ }
+
+ private void Unlock()
+ {
+ bitmap.UnlockBits(data);
+ }
+
+ public Color GetPixel(int x, int y)
+ {
+ if (x < 0 || x >= bitmap.Width)
+ throw new IndexOutOfRangeException($"X out of range: {x}");
+ if (y < 0 || y >= bitmap.Height)
+ throw new IndexOutOfRangeException($"Y out of range: {y}");
+ unsafe
+ {
+ byte* pixel = (byte*)data.Scan0 + (y * data.Stride) + x * 3;
+ return Color.FromArgb(pixel[2], pixel[1], pixel[0]);
+ }
+ }
+
+ public void SetPixel(int x, int y, Color color)
+ {
+ if (x < 0 || x >= bitmap.Width)
+ throw new IndexOutOfRangeException($"X out of range: {x}");
+ if (y < 0 || y >= bitmap.Height)
+ throw new IndexOutOfRangeException($"Y out of range: {y}");
+ unsafe
+ {
+ byte* pixel = (byte*)data.Scan0 + (y * data.Stride) + x * 3;
+ pixel[0] = color.B;
+ pixel[1] = color.G;
+ pixel[2] = color.R;
+ }
+ }
+
+ public void Save(string filename)
+ {
+ Unlock();
+ bitmap.Save(filename);
+ Lock();
+ }
+
+ public void Save(string filename, ImageFormat format)
+ {
+ Unlock();
+ bitmap.Save(filename, format);
+ Lock();
+ }
+
+ public void Save(Stream stream, ImageFormat format)
+ {
+ Unlock();
+ bitmap.Save(stream, format);
+ Lock();
+ }
+
+ public Bitmap GetBitmap()
+ {
+ Unlock();
+ var result = new Bitmap(bitmap);
+ Lock();
+ return result;
+ }
+
+ public void Dispose()
+ {
+ Lock();
+ }
+
+ public static FastBitmap FromFile(string filename)
+ {
+ var bitmap = (Bitmap)Image.FromFile(filename);
+ return new FastBitmap(bitmap);
+ }
+
+ public static FastBitmap FromStream(Stream stream)
+ {
+ var bitmap = (Bitmap)Image.FromStream(stream);
+ return new FastBitmap(bitmap);
+ }
+
+ public static FastBitmap Copy(Image image)
+ {
+ var bitmap = new Bitmap(image);
+ return new FastBitmap(bitmap);
+ }
+ }
+}
diff --git a/NesTiler.csproj b/NesTiler.csproj
index ee15668..e504000 100644
--- a/NesTiler.csproj
+++ b/NesTiler.csproj
@@ -22,6 +22,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -32,6 +33,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="ColorMine, Version=1.1.3.0, Culture=neutral, processorArchitecture=MSIL">
@@ -52,6 +54,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ColorExtensions.cs" />
+ <Compile Include="FastBitmap.cs" />
<Compile Include="Palette.cs" />
<Compile Include="Tile.cs" />
<Compile Include="Program.cs" />
diff --git a/Palette.cs b/Palette.cs
index eadae0f..1e2e7ba 100644
--- a/Palette.cs
+++ b/Palette.cs
@@ -30,7 +30,7 @@ namespace com.clusterrr.Famicom.NesTiler
// Empty palette
}
- public Palette(Image image, int leftX, int topY, int width, int height, Color bgColor)
+ public Palette(FastBitmap image, int leftX, int topY, int width, int height, Color bgColor)
{
Dictionary<Color, int> colorCounter = new Dictionary<Color, int>();
colorCounter[bgColor] = 0;
@@ -38,7 +38,7 @@ namespace com.clusterrr.Famicom.NesTiler
{
for (int x = leftX; x < leftX + width; x++)
{
- var color = ((Bitmap)image).GetPixel(x, y);
+ var color = image.GetPixel(x, y);
if (!colorCounter.ContainsKey(color)) colorCounter[color] = 0;
colorCounter[color]++;
}
@@ -66,14 +66,14 @@ namespace com.clusterrr.Famicom.NesTiler
if (colorsList.Count >= 3) this[3] = colorsList[2];
}
- public double GetTileDelta(Image image, int leftX, int topY, int width, int height, Color bgColor)
+ public double GetTileDelta(FastBitmap image, int leftX, int topY, int width, int height, Color bgColor)
{
double delta = 0;
for (int y = topY; y < topY + height; y++)
{
for (int x = leftX; x < leftX + width; x++)
{
- var color = ((Bitmap)image).GetPixel(x, y);
+ var color = image.GetPixel(x, y);
delta += GetMinDelta(color, bgColor).delta;
}
}
diff --git a/Program.cs b/Program.cs
index cea5ef5..f1e5ba1 100644
--- a/Program.cs
+++ b/Program.cs
@@ -33,8 +33,8 @@ namespace com.clusterrr.Famicom.NesTiler
var mode = TilesMode.Backgrounds;
int tilePalWidth = 16;
int tilePalHeight = 16;
- var imagesOriginal = new Dictionary<int, Image>();
- var imagesRecolored = new Dictionary<int, Image>();
+ var imagesOriginal = new Dictionary<int, FastBitmap>();
+ var imagesRecolored = new Dictionary<int, FastBitmap>();
var palleteIndexes = new Dictionary<int, byte[,]>();
var patternTableStartOffsets = new Dictionary<int, int>();
var patternTables = new Dictionary<int, Dictionary<int, Tile>>();
@@ -183,7 +183,7 @@ namespace com.clusterrr.Famicom.NesTiler
foreach (var image in imageFiles)
{
Console.WriteLine($"Loading {Path.GetFileName(image.Value)}...");
- imagesOriginal[image.Key] = Image.FromFile(image.Value);
+ imagesOriginal[image.Key] = FastBitmap.FromFile(image.Value);
if ((imagesOriginal[image.Key].Width % tilePalWidth != 0) || (imagesOriginal[image.Key].Height % tilePalHeight != 0))
throw new InvalidDataException("Invalid image size");
}
@@ -198,7 +198,7 @@ namespace com.clusterrr.Famicom.NesTiler
foreach (var imageNum in imagesOriginal.Keys)
{
Console.WriteLine($"Adjusting colors for {imageFiles[imageNum]}...");
- var image = new Bitmap(imagesOriginal[imageNum]);
+ var image = new FastBitmap(imagesOriginal[imageNum].GetBitmap());
imagesRecolored[imageNum] = image;
// Приводим все цвета к NES палитре
for (int y = 0; y < image.Height; y++)
@@ -234,7 +234,7 @@ namespace com.clusterrr.Famicom.NesTiler
{
for (int x = 0; x < tilePalWidth; x++)
{
- var color = ((Bitmap)image).GetPixel(tileX * tilePalWidth + x, tileY * tilePalHeight + y);
+ var color = image.GetPixel(tileX * tilePalWidth + x, tileY * tilePalHeight + y);
if (!colorsInTile.Contains(color))
colorsInTile.Add(color);
}
@@ -298,11 +298,12 @@ namespace com.clusterrr.Famicom.NesTiler
// Убираем те, что больше не используются
paletteCounter = paletteCounter.Where(kv => kv.Value > 0).ToDictionary(kv => kv.Key, kv => kv.Value);
-
// Снова сортируем палитры по популярности
sortedKeys = paletteCounter.OrderByDescending(kv => kv.Value).Select(kv => kv.Key).ToArray();
- // Если есть свободные места в топовых палитрах, добиваем их менее топовыми
+
+ // Список топовых палитр
var top4 = sortedKeys.Take(4).ToList();
+ // Если есть свободные места в топовых палитрах, добиваем их менее топовыми
foreach (var t in top4)
{
if (t.Count < 3)
@@ -325,6 +326,8 @@ namespace com.clusterrr.Famicom.NesTiler
// Убираем те, что больше не используются
paletteCounter = paletteCounter.Where(kv => kv.Value > 0).ToDictionary(kv => kv.Key, kv => kv.Value);
+ // Снова сортируем палитры по популярности
+ sortedKeys = paletteCounter.OrderByDescending(kv => kv.Value).Select(kv => kv.Key).ToArray();
// Снова группируем палитры. Некоторые из них могут содержать все цвета других
foreach (var palette2 in sortedKeys)
@@ -337,9 +340,14 @@ namespace com.clusterrr.Famicom.NesTiler
}
}
+
// Убираем те, что больше не используются
paletteCounter = paletteCounter.Where(kv => kv.Value > 0).ToDictionary(kv => kv.Key, kv => kv.Value);
+ // Снова сортируем палитры по популярности
+ sortedKeys = paletteCounter.OrderByDescending(kv => kv.Value).Select(kv => kv.Key).ToArray();
+ // Обновляем список топовых
+ top4 = sortedKeys.Take(4).ToList();
// Выбираем итоговые палитры
var palettes = new Palette[4] { null, null, null, null };
for (var i = 0; i < palettes.Length; i++)
@@ -394,7 +402,7 @@ namespace com.clusterrr.Famicom.NesTiler
{
Console.WriteLine($"Mapping palettes for #{imageNum} {imageFiles[imageNum]}...");
var image = imagesOriginal[imageNum];
- var imageRecolored = new Bitmap(image);
+ var imageRecolored = new FastBitmap(image.GetBitmap());
imagesRecolored[imageNum] = imageRecolored;
// Перебираем все тайлы 16*16 или спрайты 8*8 (8*16)
palleteIndexes[imageNum] = new byte[image.Width / tilePalWidth, image.Height / tilePalHeight];
@@ -403,7 +411,6 @@ namespace com.clusterrr.Famicom.NesTiler
for (int tileX = 0; tileX < image.Width / tilePalWidth; tileX++)
{
double minDelta = double.MaxValue;
- Palette bestPalette = null;
byte bestPaletteIndex = 0;
// Пробуем каждую палитру
for (byte paletteIndex = 0; paletteIndex < palettes.Length; paletteIndex++)
@@ -416,10 +423,10 @@ namespace com.clusterrr.Famicom.NesTiler
if (delta < minDelta)
{
minDelta = delta;
- bestPalette = palettes[paletteIndex];
bestPaletteIndex = paletteIndex;
}
}
+ Palette bestPalette = palettes[bestPaletteIndex];
palleteIndexes[imageNum][tileX, tileY] = bestPaletteIndex;
// В итоге применяем эту палитру к тайлу
@@ -427,7 +434,7 @@ namespace com.clusterrr.Famicom.NesTiler
{
for (int x = 0; x < tilePalWidth; x++)
{
- var color = ((Bitmap)image).GetPixel(tileX * tilePalWidth + x, tileY * tilePalHeight + y);
+ var color = image.GetPixel(tileX * tilePalWidth + x, tileY * tilePalHeight + y);
var similarColor = findSimilarColor(Enumerable.Concat(bestPalette, new Color[] { bgColor.Value }), color);
imageRecolored.SetPixel(tileX * tilePalWidth + x, tileY * tilePalHeight + y, similarColor);
}
@@ -465,7 +472,7 @@ namespace com.clusterrr.Famicom.NesTiler
for (int y = 0; y < 8; y++) // И применяем к каждому пикселю
for (int x = 0; x < 8; x++)
{
- var color = ((Bitmap)image).GetPixel(tileX * 8 + x, (tileY + tileYS) * 8 + y);
+ var color = image.GetPixel(tileX * 8 + x, (tileY + tileYS) * 8 + y);
var palette = palettes[palleteIndexes[imageNum][tileX / (tilePalWidth / 8), (tileY + tileYS) / (tilePalHeight / 8)]];
byte colorIndex = 0;
if (color != bgColor)