123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- using System;
- using System.Collections.Generic;
- using System.Threading.Tasks;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Linq;
-
- namespace agbidx.ImageLib
- {
- public static class BitmapProcessor
- {
- public static async Task ProcessPokemonImage(string outDirectory, string inPath, bool verbose, bool scanDirectories, bool recursive)
- {
- //Create the original bitmap
- if(Directory.Exists(inPath))
- {
- if(scanDirectories)
- {
- List<Task> subTasks = new List<Task>();
- foreach(string s in Directory.GetFiles(inPath))
- {
- subTasks.Add(ProcessPokemonImage(outDirectory, s, verbose, recursive, recursive));
- }
- foreach(string s in Directory.GetDirectories(inPath))
- {
- subTasks.Add(ProcessPokemonImage(outDirectory, s, verbose, recursive, recursive));
- }
- await Task.WhenAll(subTasks);
- }
-
- }
- else
- {
- await Task.Run(() => {
- Bitmap input = new Bitmap(inPath);
- ProcessPokemonImageFront(input, outDirectory, Path.GetFileNameWithoutExtension(inPath), verbose);
- });
- }
-
- }
-
- private static Color[] Quantize4bppColors(Bitmap bmp)
- {
- List<Color> colors = new List<Color>(16);
- BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
- int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8;
- int heightInPixels = bitmapData.Height;
- int widthInBytes = bitmapData.Width * bytesPerPixel;
- int palCount = 0;
- unsafe
- {
- byte* ptrFirstPixel = (byte*)bitmapData.Scan0;
- for (int y = 0; y < heightInPixels; y++)
- {
- byte* currentLine = ptrFirstPixel + (y * bitmapData.Stride);
- for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
- {
- int blue = currentLine[x];
- int green = currentLine[x + 1];
- int red = currentLine[x + 2];
- if (!colors.Any(c => c.R == red && c.G == green && c.B == blue))
- {
- colors.Add(Color.FromArgb(red,green,blue));
- palCount++;
- if(palCount > 16)
- throw new ArgumentException("The image has too many colors", "bmp");
- }
-
- }
- }
- }
- bmp.UnlockBits(bitmapData);
- return colors.ToArray();
- }
-
- private static Bitmap RenderBitmapFromPalette(Bitmap original, Color[] palette)
- {
- Bitmap output = new Bitmap(original.Width,original.Height, PixelFormat.Format4bppIndexed);
- BitmapData targetData = output.LockBits(new Rectangle(0, 0, original.Width,original.Height),
- ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed);
- BitmapData sourceData = original.LockBits(new Rectangle(0, 0, original.Width,original.Height),
- ImageLockMode.ReadOnly, original.PixelFormat);
-
- int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(original.PixelFormat) / 8;
- int heightInPixels = sourceData.Height;
- int widthInBytes = sourceData.Width * bytesPerPixel;
- unsafe
- {
- byte* ptrFirstPixel = (byte*)sourceData.Scan0;
- for (int y = 0; y < heightInPixels; y++)
- {
- byte* currentLine = ptrFirstPixel + (y * sourceData.Stride);
- byte* pTarget = (byte*) (targetData.Scan0 + y*targetData.Stride);
- byte prevValue = 0;
- for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
- {
- int blue = currentLine[x];
- int green = currentLine[x + 1];
- int red = currentLine[x + 2];
- if (!palette.Any(c => c.R == red && c.G == green && c.B == blue))
- throw new ArgumentException($"The color palette does not contain the required entry {red}:{green}:{blue}.","palette");
- else
- {
- byte colorIndex = (byte)Array.IndexOf(palette, Color.FromArgb(red,green,blue));
- if((x/bytesPerPixel) % 2 == 0)
- prevValue = colorIndex;
- else
- {
- *pTarget = (byte)((prevValue<<4) | (colorIndex));
- pTarget++;
- }
-
- }
- }
- }
- }
-
- output.UnlockBits(targetData);
- original.UnlockBits(sourceData);
-
- ColorPalette pal = output.Palette;
- for(int i = 0; i < ((palette.Length > 16) ? 16 : palette.Length); ++i)
- {
- pal.Entries[i] = palette[i];
- }
- output.Palette = pal;
- return output;
- }
- private static void ProcessPokemonImageFront(Bitmap inFile, string outDirectory, string name, bool verbose)
- {
- Bitmap normal = new Bitmap(128,64, PixelFormat.Format32bppRgb);
- Bitmap shiny = new Bitmap(128,64, PixelFormat.Format32bppRgb);
- using(Graphics g = Graphics.FromImage(normal))
- {
- g.DrawImage(inFile,new Rectangle(0,0,64,64),new Rectangle(0,0,64,64), GraphicsUnit.Pixel);
- g.DrawImage(inFile,new Rectangle(64,0,64,64),new Rectangle(2*64,0,64,64), GraphicsUnit.Pixel);
- }
- using(Graphics g = Graphics.FromImage(shiny))
- {
- g.DrawImage(inFile ,new Rectangle(0,0,64,64),new Rectangle(1*64,0,64,64), GraphicsUnit.Pixel);
- g.DrawImage(inFile ,new Rectangle(64,0,64,64),new Rectangle(3*64,0,64,64), GraphicsUnit.Pixel);
- }
-
- Color[] normalPalette = Quantize4bppColors(normal);
- Color[] shinyPalette = Quantize4bppColors(shiny);
-
- Bitmap normalIndexed = RenderBitmapFromPalette(normal, normalPalette);
- Bitmap shinyIndexed = RenderBitmapFromPalette(shiny, shinyPalette);
-
- Bitmap frontNormalIndexed = normalIndexed.Clone(new Rectangle(0,0,64,64), PixelFormat.Format4bppIndexed);
- Bitmap backShinyIndexed = shinyIndexed.Clone(new Rectangle(64,0,64,64), PixelFormat.Format4bppIndexed);
-
- frontNormalIndexed.Save(Path.Combine(outDirectory, name+"_frontnormal.png"),ImageFormat.Png);
- backShinyIndexed.Save(Path.Combine(outDirectory,name+"_backshiny.png"),ImageFormat.Png);
-
- if(verbose)
- {
- Console.WriteLine("Processed " + Path.Combine(outDirectory, name+"_frontnormal.png"));
- Console.WriteLine("Processed " + Path.Combine(outDirectory,name+"_backshiny.png"));
- }
- }
- }
- }
|