initial commit
commit
5b8bc0c521
|
@ -0,0 +1,5 @@
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
output/
|
||||||
|
|
||||||
|
.idea
|
|
@ -0,0 +1,6 @@
|
||||||
|
[submodule "lib/StbImageSharp"]
|
||||||
|
path = lib/StbImageSharp
|
||||||
|
url = https://github.com/StbSharp/StbImageSharp.git
|
||||||
|
[submodule "lib/StbImageWriteSharp"]
|
||||||
|
path = lib/StbImageWriteSharp
|
||||||
|
url = https://github.com/StbSharp/StbImageWriteSharp.git
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/bin/Debug/net5.0/Palettizer.dll",
|
||||||
|
"args": ["project", "../SamuraiGunn2_GMS2/GMS2/SamuraiGunn2/SamuraiGunn2.yyp", "TP_LevelPalette", "pal_Forest", "pal_Cemetery", "pal_Mountain"],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/Palettizer.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "publish",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/Palettizer.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"${workspaceFolder}/Palettizer.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
|
<LangVersion>8.0</LangVersion>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||||
|
<RootNamespace>Palettizer</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);lib\**\*</DefaultItemExcludes>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Text.Json" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21308.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include=".\lib\StbImageSharp\src\StbImageSharp.csproj" />
|
||||||
|
<ProjectReference Include=".\lib\StbImageWriteSharp\src\StbImageWriteSharp.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.26124.0
|
||||||
|
MinimumVisualStudioVersion = 15.0.26124.0
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Palettizer2", "Palettizer2.csproj", "{CC59DC79-B418-411E-BA42-017422F30821}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StbImageSharp", "lib\StbImageSharp\src\StbImageSharp.csproj", "{6C5AEE4F-1549-4E86-A1C0-6115781A86F5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StbImageWriteSharp", "lib\StbImageWriteSharp\src\StbImageWriteSharp.csproj", "{7551F435-4D46-4291-821D-A8102667C90A}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{CC59DC79-B418-411E-BA42-017422F30821}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{CC59DC79-B418-411E-BA42-017422F30821}.Debug|x64.Build.0 = Debug|x64`
|
||||||
|
{CC59DC79-B418-411E-BA42-017422F30821}.Release|x64.ActiveCfg = Release|x86
|
||||||
|
{CC59DC79-B418-411E-BA42-017422F30821}.Release|x64.Build.0 = Release|x86
|
||||||
|
{6C5AEE4F-1549-4E86-A1C0-6115781A86F5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{6C5AEE4F-1549-4E86-A1C0-6115781A86F5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{6C5AEE4F-1549-4E86-A1C0-6115781A86F5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{6C5AEE4F-1549-4E86-A1C0-6115781A86F5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{7551F435-4D46-4291-821D-A8102667C90A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{7551F435-4D46-4291-821D-A8102667C90A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{7551F435-4D46-4291-821D-A8102667C90A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{7551F435-4D46-4291-821D-A8102667C90A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 9feb07693cde152ebd1cceff7e995db4d7fe6d8d
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6c999609700e5a3450c54b78d099c56918a2d1c6
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Palettizer2
|
||||||
|
{
|
||||||
|
public struct Color
|
||||||
|
{
|
||||||
|
public byte R { get; set; }
|
||||||
|
public byte G { get; set; }
|
||||||
|
public byte B { get; set; }
|
||||||
|
public byte A { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{{R: {R.ToString()}, G: {G.ToString()}, B: {B.ToString()}, A: {A.ToString()}}}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Palettizer2
|
||||||
|
{
|
||||||
|
public class Palette
|
||||||
|
{
|
||||||
|
public Dictionary<byte, Color> GrayscaleToColorMap { get; }
|
||||||
|
public Dictionary<Color, byte> ColorToGrayscaleMap { get; }
|
||||||
|
public int GrayscaleCount { get; set; }
|
||||||
|
public List<byte[]> AlternateColorRows { get; }
|
||||||
|
|
||||||
|
public int Width { get => 256; }
|
||||||
|
public int Height { get => AlternateColorRows.Count + 1; }
|
||||||
|
|
||||||
|
public Palette()
|
||||||
|
{
|
||||||
|
GrayscaleToColorMap = new Dictionary<byte, Color>();
|
||||||
|
ColorToGrayscaleMap = new Dictionary<Color, byte>();
|
||||||
|
GrayscaleCount = 0;
|
||||||
|
AlternateColorRows = new List<byte[]>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddColor(Color color)
|
||||||
|
{
|
||||||
|
var average = (color.R + color.G + color.B) / 3;
|
||||||
|
if (GrayscaleToColorMap.ContainsKey((byte)average))
|
||||||
|
{
|
||||||
|
var increment = 1;
|
||||||
|
for (var i = 0; i < 256; i += 1)
|
||||||
|
{
|
||||||
|
average = (average + increment) % 256;
|
||||||
|
|
||||||
|
if (!GrayscaleToColorMap.ContainsKey((byte)average))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
increment = ((increment > 0) ? (increment + 1) : (increment - 1)) * -1; // up by 1, down by 2, up by 3, etc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GrayscaleToColorMap.Add((byte)average, color);
|
||||||
|
ColorToGrayscaleMap.Add(color, (byte)average);
|
||||||
|
GrayscaleCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAlternateColorRow(Color[] colors)
|
||||||
|
{
|
||||||
|
var byteArray = new byte[256 * 4];
|
||||||
|
|
||||||
|
for (var i = 0; i < 256; i += 1)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlternateColorRows.Add(byteArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearAlternateColorRows()
|
||||||
|
{
|
||||||
|
AlternateColorRows.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] CreateIndexedPaletteBitmap()
|
||||||
|
{
|
||||||
|
var paletteBitmap = new byte[256 * (AlternateColorRows.Count + 1) * 4];
|
||||||
|
|
||||||
|
for (var i = 0; i < 256; i += 1)
|
||||||
|
{
|
||||||
|
var color = new Color();
|
||||||
|
color.A = 255;
|
||||||
|
|
||||||
|
if (GrayscaleToColorMap.ContainsKey((byte)i))
|
||||||
|
{
|
||||||
|
color = GrayscaleToColorMap[(byte)i];
|
||||||
|
}
|
||||||
|
|
||||||
|
paletteBitmap[i * 4] = color.R;
|
||||||
|
paletteBitmap[i * 4 + 1] = color.G;
|
||||||
|
paletteBitmap[i * 4 + 2] = color.B;
|
||||||
|
paletteBitmap[i * 4 + 3] = color.A;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 256; i += 1)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < AlternateColorRows.Count; j += 1)
|
||||||
|
{
|
||||||
|
var alpha = AlternateColorRows[j][i * 4 + 3];
|
||||||
|
if (alpha == 0)
|
||||||
|
{
|
||||||
|
// grab from top row
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4] = paletteBitmap[i * 4];
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4 + 1] = paletteBitmap[i * 4 + 1];
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4 + 2] = paletteBitmap[i * 4 + 2];
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4 + 3] = paletteBitmap[i * 4 + 3];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4] = AlternateColorRows[j][i * 4];
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4 + 1] = AlternateColorRows[j][i * 4 + 1];
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4 + 2] = AlternateColorRows[j][i * 4 + 2];
|
||||||
|
paletteBitmap[(i + (256 * (j + 1))) * 4 + 3] = alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paletteBitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
using System.CommandLine;
|
||||||
|
using System.CommandLine.Invocation;
|
||||||
|
using System.IO;
|
||||||
|
using StbImageSharp;
|
||||||
|
|
||||||
|
namespace Palettizer2
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
var palettize = new Command("palettize")
|
||||||
|
{
|
||||||
|
new Argument<DirectoryInfo>(
|
||||||
|
"directory",
|
||||||
|
"Path to a directory containing PNG files."
|
||||||
|
),
|
||||||
|
new Argument<string>(
|
||||||
|
"paletteName",
|
||||||
|
"The name of the palette sprite to output."
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
var root = new RootCommand{
|
||||||
|
palettize
|
||||||
|
};
|
||||||
|
|
||||||
|
palettize.Handler = CommandHandler.Create<DirectoryInfo, string>(HandlePalettize);
|
||||||
|
return root.Invoke(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandlePalettize(DirectoryInfo directory, string paletteName)
|
||||||
|
{
|
||||||
|
var outputDir = Directory.CreateDirectory("output");
|
||||||
|
var palette = new Palette();
|
||||||
|
var writer = new StbImageWriteSharp.ImageWriter();
|
||||||
|
|
||||||
|
// add all sprite colors to palette
|
||||||
|
foreach (var imageFile in directory.EnumerateFiles("*.png"))
|
||||||
|
{
|
||||||
|
System.Console.WriteLine(imageFile.FullName);
|
||||||
|
|
||||||
|
var spriteDir = Path.GetDirectoryName(imageFile.FullName);
|
||||||
|
|
||||||
|
ImageResult image;
|
||||||
|
byte[] grayscaleImageBytes;
|
||||||
|
using (var stream = File.OpenRead(imageFile.FullName))
|
||||||
|
{
|
||||||
|
image = ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha);
|
||||||
|
grayscaleImageBytes = AddSpriteToPalette(palette, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
var spriteWriteDir = Path.Combine(
|
||||||
|
outputDir.FullName,
|
||||||
|
Path.GetRelativePath(directory.FullName, spriteDir));
|
||||||
|
|
||||||
|
Directory.CreateDirectory(spriteWriteDir);
|
||||||
|
var spriteWritePath = Path.Combine(spriteWriteDir, imageFile.Name);
|
||||||
|
|
||||||
|
using (var stream = File.OpenWrite(spriteWritePath))
|
||||||
|
{
|
||||||
|
writer.WritePng(
|
||||||
|
grayscaleImageBytes,
|
||||||
|
image.Width,
|
||||||
|
image.Height,
|
||||||
|
StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha,
|
||||||
|
stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write palette sprite to PNG
|
||||||
|
var paletteOutputPath = Path.Combine(outputDir.FullName, paletteName + ".png");
|
||||||
|
using (var stream = File.OpenWrite(paletteOutputPath))
|
||||||
|
{
|
||||||
|
writer.WritePng(
|
||||||
|
palette.CreateIndexedPaletteBitmap(),
|
||||||
|
palette.Width,
|
||||||
|
palette.Height,
|
||||||
|
StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha,
|
||||||
|
stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] AddSpriteToPalette(Palette palette, ImageResult image)
|
||||||
|
{
|
||||||
|
var grayscaleImage = new byte[image.Width * image.Height * 4];
|
||||||
|
|
||||||
|
for (var i = 0; i < image.Width * image.Height * 4; i += 4)
|
||||||
|
{
|
||||||
|
var color = new Color
|
||||||
|
{
|
||||||
|
R = image.Data[i],
|
||||||
|
G = image.Data[i + 1],
|
||||||
|
B = image.Data[i + 2],
|
||||||
|
A = image.Data[i + 3]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (color.A == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (palette.ColorToGrayscaleMap.ContainsKey(color))
|
||||||
|
{
|
||||||
|
var grayscaleColor = palette.ColorToGrayscaleMap[color];
|
||||||
|
|
||||||
|
grayscaleImage[i] = grayscaleColor;
|
||||||
|
grayscaleImage[i + 1] = grayscaleColor;
|
||||||
|
grayscaleImage[i + 2] = grayscaleColor;
|
||||||
|
grayscaleImage[i + 3] = color.A;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (palette.GrayscaleCount == 256)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("Too many colors! Bailing!");
|
||||||
|
return grayscaleImage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
palette.AddColor(color);
|
||||||
|
|
||||||
|
var grayscaleColor = palette.ColorToGrayscaleMap[color];
|
||||||
|
|
||||||
|
grayscaleImage[i] = grayscaleColor;
|
||||||
|
grayscaleImage[i + 1] = grayscaleColor;
|
||||||
|
grayscaleImage[i + 2] = grayscaleColor;
|
||||||
|
grayscaleImage[i + 3] = color.A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grayscaleImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue