Skip to content

Commit

Permalink
Merge pull request #782 from kenjiuno/remove-gdiplus
Browse files Browse the repository at this point in the history
Support OpenKh.Command.ImgTool runs on Linux
  • Loading branch information
shananas authored Dec 22, 2023
2 parents ca18d8a + 16794b2 commit cc6fa93
Show file tree
Hide file tree
Showing 25 changed files with 884 additions and 308 deletions.
12 changes: 6 additions & 6 deletions OpenKh.Command.ImgTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
using OpenKh.Command.ImgTool.Interfaces;
using OpenKh.Command.ImgTool.Utils;
using OpenKh.Common;
using OpenKh.Imaging;
using OpenKh.Kh2;
using OpenKh.Kh2.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -76,8 +76,8 @@ protected int OnExecute(CommandLineApplication app)
using (var stream = File.OpenRead(inputFile))
{
var imgd = Imgd.Read(stream);
var bitmap = ImgdBitmapUtil.ToBitmap(imgd);
bitmap.Save(outputFile);
using var outputStream = File.Create(outputFile);
PngImage.Write(outputStream, imgd);
}
return 0;
}
Expand Down Expand Up @@ -123,8 +123,8 @@ protected int OnExecute(CommandLineApplication app)
{
var outputFile = Path.Combine(outputDir, $"{Path.GetFileNameWithoutExtension(inputFile)}-{1 + index}.png");

var bitmap = ImgdBitmapUtil.ToBitmap(imgd);
bitmap.Save(outputFile);
using var outputStream = File.Create(outputFile);
PngImage.Write(outputStream, imgd);
}
}
}
Expand Down Expand Up @@ -164,7 +164,7 @@ protected int OnExecute(CommandLineApplication app)
Directory.CreateDirectory(Path.GetDirectoryName(outputFile));

// Alpha enabled png → always 32 bpp
using (var bitmap = new Bitmap(inputFile))
var bitmap = PngImage.Read(new MemoryStream(File.ReadAllBytes(inputFile)));
{
var imgd = ImgdBitmapUtil.ToImgd(bitmap, BitsPerPixel, QuantizerFactory.MakeFrom(this), Swizzle);

Expand Down
220 changes: 12 additions & 208 deletions OpenKh.Command.ImgTool/Utils/ImgdBitmapUtil.cs
Original file line number Diff line number Diff line change
@@ -1,155 +1,37 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using OpenKh.Common;
using OpenKh.Imaging;
using OpenKh.Kh2;
using static System.Drawing.Imaging.PixelFormat;

namespace OpenKh.Kh2.Utils
{
public class ImgdBitmapUtil
{
public static Bitmap ToBitmap(Imgd imgd)
{
switch (imgd.PixelFormat)
{
case Imaging.PixelFormat.Indexed4:
{
var bitmap = new Bitmap(imgd.Size.Width, imgd.Size.Height, PixelFormat.Format4bppIndexed);
var dest = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
var sourceBits = imgd.GetData();
var sourceWidth = imgd.Size.Width;
var sourceStride = ((sourceWidth + 1) / 2) & (~1);
for (int y = 0; y < bitmap.Height; y++)
{
Marshal.Copy(sourceBits, sourceStride * y, dest.Scan0 + dest.Stride * y, sourceStride);
}
}
finally
{
bitmap.UnlockBits(dest);
}

{
var clut = imgd.GetClut();
var palette = bitmap.Palette;
for (int index = 0; index < 16; index++)
{
palette.Entries[index] = Color.FromArgb(
clut[4 * index + 3],
clut[4 * index + 0],
clut[4 * index + 1],
clut[4 * index + 2]
);
}
bitmap.Palette = palette;
}

return bitmap;
}
case Imaging.PixelFormat.Indexed8:
{
var bitmap = new Bitmap(imgd.Size.Width, imgd.Size.Height, PixelFormat.Format8bppIndexed);
var dest = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
var sourceBits = imgd.GetData();
var sourceWidth = imgd.Size.Width;
for (int y = 0; y < bitmap.Height; y++)
{
Marshal.Copy(sourceBits, sourceWidth * y, dest.Scan0 + dest.Stride * y, sourceWidth);
}
}
finally
{
bitmap.UnlockBits(dest);
}

{
var clut = imgd.GetClut();
var palette = bitmap.Palette;
for (int index = 0; index < 256; index++)
{
palette.Entries[index] = Color.FromArgb(
clut[4 * index + 3],
clut[4 * index + 0],
clut[4 * index + 1],
clut[4 * index + 2]
);
}
bitmap.Palette = palette;
}

return bitmap;
}
case Imaging.PixelFormat.Rgba8888:
{
var bitmap = new Bitmap(imgd.Size.Width, imgd.Size.Height, PixelFormat.Format32bppPArgb);
var dest = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.WriteOnly, bitmap.PixelFormat);
try
{
var sourceBits = imgd.GetData();
var sourceWidth = 4 * imgd.Size.Width;
for (int y = 0; y < bitmap.Height; y++)
{
Marshal.Copy(sourceBits, sourceWidth * y, dest.Scan0 + dest.Stride * y, sourceWidth);
}
}
finally
{
bitmap.UnlockBits(dest);
}

return bitmap;
}
}
throw new NotSupportedException($"{imgd.PixelFormat} not recognized!");
}

class ReadAs32bppPixels
{
public ReadAs32bppPixels(Bitmap bitmap)
public ReadAs32bppPixels(IImageRead bitmap)
{
Width = bitmap.Width;
Height = bitmap.Height;
Width = bitmap.Size.Width;
Height = bitmap.Size.Height;

var src = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var srcBits = new byte[src.Stride * src.Height];
try
{
Marshal.Copy(src.Scan0, srcBits, 0, srcBits.Length);
}
finally
{
bitmap.UnlockBits(src);
}

Pixels = new List<uint>(Width * Height);

for (int y = 0; y < Height; y++)
{
for (int x = 0; x < Width; x++)
{
Pixels.Add(BitConverter.ToUInt32(srcBits, src.Stride * y + 4 * x));
}
}
Pixels = new uint[Width * Height];

Buffer.BlockCopy(bitmap.ToBgra32(), 0, Pixels, 0, 4 * Pixels.Length);
}

public List<uint> Pixels { get; }
public uint[] Pixels { get; }
public int Width { get; }
public int Height { get; }
}

static class PaletteColorUsageCounter
{
public static bool IfMaxColorCountIsOver(List<uint> pixels, int maxColors)
public static bool IfMaxColorCountIsOver(uint[] pixels, int maxColors)
{
return pixels
.GroupBy(pixel => pixel)
Expand All @@ -159,7 +41,7 @@ public static bool IfMaxColorCountIsOver(List<uint> pixels, int maxColors)

class PaletteGenerator
{
public PaletteGenerator(List<uint> pixels, int maxColors)
public PaletteGenerator(uint[] pixels, int maxColors)
{
MostUsedPixels = pixels
.GroupBy(pixel => pixel)
Expand Down Expand Up @@ -206,7 +88,7 @@ public int FindNearest(uint pixel)
}
}

public static Imgd ToImgd(Bitmap bitmap, int bpp, Func<Bitmap, Bitmap> quantizer, bool swizzle = false)
public static Imgd ToImgd(IImageRead bitmap, int bpp, Func<IImageRead, IImageRead> quantizer, bool swizzle = false)
{
if (quantizer != null)
{
Expand Down Expand Up @@ -342,13 +224,13 @@ public static Imgd ToImgd(Bitmap bitmap, int bpp, Func<Bitmap, Bitmap> quantizer
throw new NotSupportedException($"BitsPerPixel {bpp} not recognized!");
}

public static IEnumerable<Imgd> FromFileToImgdList(string anyFile, int bitsPerPixel, Func<Bitmap, Bitmap> quantizer, bool swizzle)
public static IEnumerable<Imgd> FromFileToImgdList(string anyFile, int bitsPerPixel, Func<IImageRead, IImageRead> quantizer, bool swizzle)
{
switch (Path.GetExtension(anyFile).ToLowerInvariant())
{
case ".png":
{
yield return new Bitmap(anyFile).Using(bitmap => ToImgd(bitmap, bitsPerPixel, quantizer, swizzle));
yield return ToImgd(PngImage.Read(new MemoryStream(File.ReadAllBytes(anyFile))), bitsPerPixel, quantizer, swizzle);
break;
}
case ".imd":
Expand All @@ -366,83 +248,5 @@ public static IEnumerable<Imgd> FromFileToImgdList(string anyFile, int bitsPerPi
}
}
}

public static Imgd ToImgd(Bitmap bitmap)
{
switch (bitmap.PixelFormat)
{
case PixelFormat.Format4bppIndexed:
{
var destHeight = bitmap.Height;
var destStride = (bitmap.Width + 1) & (~1);
var destBits = new byte[destStride * destHeight];
var src = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, bitmap.PixelFormat);
try
{
for (int y = 0; y < bitmap.Height; y++)
{
Marshal.Copy(src.Scan0 + src.Stride * y, destBits, destStride * y, destStride);
}
}
finally
{
bitmap.UnlockBits(src);
}

var clut = new byte[4 * 16];
{
var palette = bitmap.Palette;
for (int index = 0; index < 16; index++)
{
var color = palette.Entries[index];

clut[4 * index + 0] = color.R;
clut[4 * index + 1] = color.G;
clut[4 * index + 2] = color.B;
clut[4 * index + 3] = color.A;
}
bitmap.Palette = palette;
}

return Imgd.Create(bitmap.Size, Imaging.PixelFormat.Indexed4, destBits, clut, false);
}
case PixelFormat.Format8bppIndexed:
{
var destHeight = bitmap.Height;
var destStride = bitmap.Width;
var destBits = new byte[destStride * destHeight];
var src = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, bitmap.PixelFormat);
try
{
for (int y = 0; y < bitmap.Height; y++)
{
Marshal.Copy(src.Scan0 + src.Stride * y, destBits, destStride * y, destStride);
}
}
finally
{
bitmap.UnlockBits(src);
}

var clut = new byte[4 * 256];
{
var palette = bitmap.Palette;
for (int index = 0; index < 256; index++)
{
var color = palette.Entries[index];

clut[4 * index + 0] = color.R;
clut[4 * index + 1] = color.G;
clut[4 * index + 2] = color.B;
clut[4 * index + 3] = color.A;
}
bitmap.Palette = palette;
}

return Imgd.Create(bitmap.Size, Imaging.PixelFormat.Indexed4, destBits, clut, false);
}
}
throw new NotSupportedException($"{bitmap.PixelFormat} not recognized!");
}
}
}
Loading

0 comments on commit cc6fa93

Please sign in to comment.