176 lines
6.9 KiB
C#
176 lines
6.9 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Collections.Generic;
|
|
|
|
public class ImageProcessor
|
|
{
|
|
public static void MakeCleanTransparent(string inputPath, string outputPath)
|
|
{
|
|
using var original = (Bitmap)Image.FromFile(inputPath);
|
|
int w = original.Width;
|
|
int h = original.Height;
|
|
|
|
using var bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
|
|
using (var g = Graphics.FromImage(bmp))
|
|
{
|
|
g.DrawImage(original, 0, 0, w, h);
|
|
}
|
|
|
|
var rect = new Rectangle(0, 0, w, h);
|
|
var data = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
|
|
|
int bytesPerPixel = 4;
|
|
int byteCount = data.Stride * h;
|
|
byte[] pixels = new byte[byteCount];
|
|
System.Runtime.InteropServices.Marshal.Copy(data.Scan0, pixels, 0, byteCount);
|
|
|
|
bool[,] isBg = new bool[w, h];
|
|
var queue = new Queue<Point>();
|
|
|
|
bool CheckWhite(int x, int y)
|
|
{
|
|
int idx = y * data.Stride + x * bytesPerPixel;
|
|
byte b = pixels[idx];
|
|
byte g = pixels[idx + 1];
|
|
byte r = pixels[idx + 2];
|
|
// Pure/near white or light background
|
|
return r > 215 && g > 215 && b > 215 && Math.Abs(r - g) < 25 && Math.Abs(g - b) < 25;
|
|
}
|
|
|
|
// Seed borders
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
if (CheckWhite(x, 0)) { isBg[x, 0] = true; queue.Enqueue(new Point(x, 0)); }
|
|
if (CheckWhite(x, h - 1)) { isBg[x, h - 1] = true; queue.Enqueue(new Point(x, h - 1)); }
|
|
}
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
if (CheckWhite(0, y)) { isBg[0, y] = true; queue.Enqueue(new Point(0, y)); }
|
|
if (CheckWhite(w - 1, y)) { isBg[w - 1, y] = true; queue.Enqueue(new Point(w - 1, y)); }
|
|
}
|
|
|
|
int[] dx = { 0, 0, 1, -1 };
|
|
int[] dy = { 1, -1, 0, 0 };
|
|
|
|
while (queue.Count > 0)
|
|
{
|
|
var p = queue.Dequeue();
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int nx = p.X + dx[i];
|
|
int ny = p.Y + dy[i];
|
|
if (nx >= 0 && nx < w && ny >= 0 && ny < h && !isBg[nx, ny])
|
|
{
|
|
if (CheckWhite(nx, ny))
|
|
{
|
|
isBg[nx, ny] = true;
|
|
queue.Enqueue(new Point(nx, ny));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply transparency
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
int idx = y * data.Stride + x * bytesPerPixel;
|
|
if (isBg[x, y])
|
|
{
|
|
pixels[idx + 3] = 0; // Alpha = 0
|
|
}
|
|
else
|
|
{
|
|
// Check if adjacent to background for soft anti-aliased edge
|
|
bool nearBg = false;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int nx = x + dx[i];
|
|
int ny = y + dy[i];
|
|
if (nx >= 0 && nx < w && ny >= 0 && ny < h && isBg[nx, ny])
|
|
{
|
|
nearBg = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
byte b = pixels[idx];
|
|
byte g = pixels[idx + 1];
|
|
byte r = pixels[idx + 2];
|
|
if (nearBg && r > 210 && g > 210 && b > 210)
|
|
{
|
|
double avg = (r + g + b) / 3.0;
|
|
int alpha = (int)(255.0 * (1.0 - (avg - 210.0) / 45.0));
|
|
pixels[idx + 3] = (byte)Math.Clamp(alpha, 0, 255);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
System.Runtime.InteropServices.Marshal.Copy(pixels, 0, data.Scan0, byteCount);
|
|
bmp.UnlockBits(data);
|
|
|
|
// Ensure output dir
|
|
var dir = Path.GetDirectoryName(outputPath);
|
|
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
|
|
|
|
bmp.Save(outputPath, ImageFormat.Png);
|
|
Console.WriteLine($"Exported: {outputPath}");
|
|
}
|
|
|
|
public static void Main(string[] args)
|
|
{
|
|
string brain = @"C:\Users\mvrba\.gemini\antigravity\brain\f097144f-e914-4120-8461-4bdfe0d3cdef";
|
|
string root = @"src\CiselnaVyprava\wwwroot\assets\images";
|
|
|
|
// 1. Knight (Exact same boy character)
|
|
string knightIdleSrc = Path.Combine(brain, "disney_hero_knight_1789996129818.jpg");
|
|
string knightAtkSrc = Path.Combine(brain, "knight_action_strike_1789998031553.jpg");
|
|
string knightDefSrc = Path.Combine(brain, "knight_action_shield_1789998042563.jpg");
|
|
string knightDizzySrc = Path.Combine(brain, "hero_knight_dizzy_1789997064946.jpg");
|
|
|
|
// 2. Goblin
|
|
string goblinIdleSrc = Path.Combine(brain, "disney_monster_goblin_1789996145106.jpg");
|
|
string goblinAtkSrc = Path.Combine(brain, "goblin_monster_attack_1789997081347.jpg");
|
|
string goblinHurtSrc = Path.Combine(brain, "goblin_monster_hurt_1789997090819.jpg");
|
|
|
|
// 3. Golem Boss
|
|
string golemSrc = Path.Combine(brain, "stone_golem_boss_1789997102336.jpg");
|
|
|
|
// Process Knight
|
|
for (int p = 1; p <= 4; p++)
|
|
{
|
|
string idleSrc = (p <= 2) ? knightIdleSrc : knightDizzySrc;
|
|
MakeCleanTransparent(idleSrc, Path.Combine(root, $"heroes/knight_idle_p{p}.png"));
|
|
MakeCleanTransparent(knightAtkSrc, Path.Combine(root, $"heroes/knight_attack_p{p}.png"));
|
|
MakeCleanTransparent(knightDefSrc, Path.Combine(root, $"heroes/knight_defend_p{p}.png"));
|
|
MakeCleanTransparent(knightDizzySrc, Path.Combine(root, $"heroes/knight_hurt_p{p}.png"));
|
|
}
|
|
|
|
// Process Goblin & Slime
|
|
for (int p = 1; p <= 4; p++)
|
|
{
|
|
MakeCleanTransparent(goblinIdleSrc, Path.Combine(root, $"enemies/goblin_idle_p{p}.png"));
|
|
MakeCleanTransparent(goblinAtkSrc, Path.Combine(root, $"enemies/goblin_attack_p{p}.png"));
|
|
MakeCleanTransparent(goblinHurtSrc, Path.Combine(root, $"enemies/goblin_hurt_p{p}.png"));
|
|
|
|
MakeCleanTransparent(goblinIdleSrc, Path.Combine(root, $"enemies/slime_idle_p{p}.png"));
|
|
MakeCleanTransparent(goblinAtkSrc, Path.Combine(root, $"enemies/slime_attack_p{p}.png"));
|
|
MakeCleanTransparent(goblinHurtSrc, Path.Combine(root, $"enemies/slime_hurt_p{p}.png"));
|
|
}
|
|
|
|
// Process Golem
|
|
for (int p = 1; p <= 4; p++)
|
|
{
|
|
MakeCleanTransparent(golemSrc, Path.Combine(root, $"enemies/golem_idle_p{p}.png"));
|
|
MakeCleanTransparent(golemSrc, Path.Combine(root, $"enemies/golem_attack_p{p}.png"));
|
|
MakeCleanTransparent(golemSrc, Path.Combine(root, $"enemies/golem_hurt_p{p}.png"));
|
|
}
|
|
|
|
Console.WriteLine("All assets converted to clean 32-bit transparent PNGs!");
|
|
}
|
|
}
|