From 2ba06ebc069641204c0cd25ba93a181114911f5c Mon Sep 17 00:00:00 2001 From: Vladislav Stepanov Date: Sun, 2 Jun 2024 17:00:28 +0300 Subject: [PATCH] Replace "System.Drawing" with "SkiaSharp" in all scripts. (#24) * Restore WIP script changes * Finish removing/replacing of "System.Drawing" in all scripts (not tested) * Update MergeImages.csx * Update ImportMasks.csx --------- Co-authored-by: Miepee <38186597+Miepee@users.noreply.github.com> --- .../UndertaleDialogSimulator.csx | 9 +- .../Community Scripts/ImportGMS2FontData.csx | 23 ++-- .../Community Scripts/ScaleAllTextures.csx | 26 +--- .../ApplyBasicGraphicsMod.csx | 9 +- .../Resource Repackers/ImportFontData.csx | 120 +++++++++++------- .../Resource Repackers/ImportGraphics.csx | 85 +++++++------ .../Resource Repackers/ImportMasks.csx | 17 +-- .../Resource Repackers/NewTextureRepacker.csx | 37 ++---- .../ReduceEmbeddedTexturePages.csx | 71 ++++++----- .../Resource Unpackers/ExportAllTextures.csx | 1 - .../ExportAllTexturesGrouped.csx | 1 - .../ExportTextureGroups.csx | 1 - .../Resource Unpackers/MergeImages.csx | 51 +++----- .../ImportGraphics_Full_Repack.csx | 84 ++++++------ 14 files changed, 274 insertions(+), 261 deletions(-) diff --git a/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx b/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx index cdf8b698b..b03e186da 100644 --- a/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx +++ b/UndertaleModTool/Scripts/Builtin Scripts/UndertaleDialogSimulator.csx @@ -3,7 +3,6 @@ using System.IO; using System; -using System.Drawing; using System.Windows.Forms; using UndertaleModLib.Util; @@ -25,10 +24,10 @@ else if (GameName == "deltarune chapter 1&2") } if (Data.GeneralInfo.Name.Content == "NXTALE" || Data.GeneralInfo.Name.Content.StartsWith("UNDERTALE")) { - if (!ScriptQuestion("Would you like to apply this mod?")) - { - return; - } + if (!ScriptQuestion("Would you like to apply this mod?")) + { + return; + } } else if (Data.GeneralInfo.DisplayName.Content == "SURVEY_PROGRAM" || Data.GeneralInfo.DisplayName.Content == "DELTARUNE Chapter 1") { diff --git a/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx b/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx index c6bd9aba8..b6a07452e 100644 --- a/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx +++ b/UndertaleModTool/Scripts/Community Scripts/ImportGMS2FontData.csx @@ -2,7 +2,7 @@ using System; using System.IO; -using System.Drawing; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -11,6 +11,7 @@ using UndertaleModLib; using UndertaleModLib.Util; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using UndertaleModLib.Util; EnsureDataLoaded(); @@ -96,10 +97,10 @@ else if (attemptToFixFontNotAppearing) fontTexGroup.Fonts.Add(new UndertaleResourceById() { Resource = font }); } -// Prepare font texture -Bitmap textureBitmap = new Bitmap(fontTexturePath); -// Make the DPI exactly 96 for this bitmap -textureBitmap.SetResolution(96.0F, 96.0F); +// Get texture properties +var imgSize = TextureWorker.GetImageSizeFromFile(fontTexturePath); +ushort width = (ushort)imgSize.Width; +ushort height = (ushort)imgSize.Height; UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture(); // ??? Why? @@ -115,14 +116,14 @@ texturePageItem.Name = new UndertaleString("PageItem " + Data.TexturePageItems.C texturePageItem.TexturePage = texture; texturePageItem.SourceX = 0; texturePageItem.SourceY = 0; -texturePageItem.SourceWidth = (ushort)textureBitmap.Width; -texturePageItem.SourceHeight = (ushort)textureBitmap.Height; +texturePageItem.SourceWidth = width; +texturePageItem.SourceHeight = height; texturePageItem.TargetX = 0; texturePageItem.TargetY = 0; -texturePageItem.TargetWidth = (ushort)textureBitmap.Width; -texturePageItem.TargetHeight = (ushort)textureBitmap.Height; -texturePageItem.BoundingWidth = (ushort)textureBitmap.Width; -texturePageItem.BoundingHeight = (ushort)textureBitmap.Height; +texturePageItem.TargetWidth = width; +texturePageItem.TargetHeight = height; +texturePageItem.BoundingWidth = width; +texturePageItem.BoundingHeight = height; Data.TexturePageItems.Add(texturePageItem); font.DisplayName = Data.Strings.MakeString((string)fontData["fontName"]); diff --git a/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx b/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx index f1adc7fb8..8b290ea96 100644 --- a/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx +++ b/UndertaleModTool/Scripts/Community Scripts/ScaleAllTextures.csx @@ -1,8 +1,6 @@ using System; using System.IO; -using System.Drawing; -using System.Drawing.Imaging; -using System.Drawing.Drawing2D; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -21,9 +19,9 @@ if (!ScriptQuestion("Visual glitches are very likely to occur in game. Do you ac TextureWorker worker = new TextureWorker(); double scale = -1; -bool SelectScale = true; +bool selectScale = true; -if (SelectScale) +if (selectScale) { bool success = false; while (scale <= 0 || scale > 10) @@ -131,9 +129,8 @@ ChangeSelection(Data.Rooms.ByName("room_ruins1")); void ScaleEmbeddedTexture(UndertaleEmbeddedTexture tex) { - Bitmap embImage = worker.GetEmbeddedTexture(tex); - embImage = ResizeBitmap(embImage, (int)(embImage.Width * scale), (int)(embImage.Height * scale)); - embImage.SetResolution(96.0F, 96.0F); + SKBitmap embImage = worker.GetEmbeddedTexture(tex); + embImage = TextureWorker.ResizeImage(embImage, (int)(embImage.Width * scale), (int)(embImage.Height * scale), useNearestNeighbor: true); try { var width = (uint)embImage.Width; @@ -144,7 +141,7 @@ void ScaleEmbeddedTexture(UndertaleEmbeddedTexture tex) } using (var stream = new MemoryStream()) { - embImage.Save(stream, System.Drawing.Imaging.ImageFormat.Png); + embImage.Encode(stream, SKEncodedImageFormat.Png, 100); tex.TextureData.TextureBlob = stream.ToArray(); } } @@ -153,14 +150,3 @@ void ScaleEmbeddedTexture(UndertaleEmbeddedTexture tex) //ScriptError("Failed to import file: " + ex.Message, "Failed to import file"); } } - -private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height) -{ - Bitmap result = new Bitmap(width, height); - using (Graphics g = Graphics.FromImage(result)) - { - g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; - g.DrawImage(sourceBMP, 0, 0, width, height); - } - return result; -} diff --git a/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx b/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx index 05018a98a..311713f18 100644 --- a/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx +++ b/UndertaleModTool/Scripts/Resource Repackers/ApplyBasicGraphicsMod.csx @@ -1,6 +1,6 @@ using System; using System.IO; -using System.Drawing; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -105,12 +105,7 @@ await Task.Run(() => { { try { - Bitmap bmp; - using (var ms = new MemoryStream(TextureWorker.ReadTextureBlob(file))) - { - bmp = new Bitmap(ms); - } - bmp.SetResolution(96.0F, 96.0F); + SKBitmap bmp = SKBitmap.Decode(file); var width = (uint)bmp.Width; var height = (uint)bmp.Height; var CheckWidth = (uint)(sprite.Textures[frame].Texture.TargetWidth); diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx index f849db582..882e2d44e 100644 --- a/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx +++ b/UndertaleModTool/Scripts/Resource Repackers/ImportFontData.csx @@ -3,7 +3,7 @@ using System; using System.IO; -using System.Drawing; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -37,7 +37,6 @@ int atlasCount = 0; foreach (Atlas atlas in packer.Atlasses) { string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount); - Bitmap atlasBitmap = new Bitmap(atlasName); UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture(); texture.Name = new UndertaleString("Texture " + ++lastTextPage); texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName); @@ -86,8 +85,6 @@ foreach (Atlas atlas in packer.Atlasses) atlasCount++; } - - HideProgressBar(); ScriptMessage("Import Complete!"); @@ -176,10 +173,17 @@ public enum BestFitHeuristic Area, MaxOneAxis, } +public struct Rect +{ + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } +} public class Node { - public Rectangle Bounds; + public Rect Bounds; public TextureInfo Texture; public SplitType SplitType; } @@ -201,6 +205,26 @@ public class Packer public bool DebugMode; public BestFitHeuristic FitHeuristic; public List Atlasses; + public static readonly SKPaint paintGreen = new() + { + Color = SKColors.Green, + BlendMode = SKBlendMode.Src + }; + public static readonly SKPaint paintBlack = new() + { + Color = SKColors.Black, + BlendMode = SKBlendMode.Src + }; + public static readonly SKPaint paintWhite = new() + { + Color = SKColors.White, + BlendMode = SKBlendMode.Src + }; + public static readonly SKPaint paintDarkMagenta = new() + { + Color = SKColors.DarkMagenta, + BlendMode = SKBlendMode.Src + }; public Packer() { @@ -256,13 +280,8 @@ public class Packer { string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount); //1: Save images - Image img = CreateAtlasImage(atlas); - //DPI fix start - Bitmap ResolutionFix = new Bitmap(img); - ResolutionFix.SetResolution(96.0F, 96.0F); - Image img2 = ResolutionFix; - //DPI fix end - img2.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png); + using SKBitmap img = CreateAtlasImage(atlas); + TextureWorker.SaveImageToFile(atlasName, img); //2: save description in file foreach (Node n in atlas.Nodes) { @@ -293,25 +312,27 @@ public class Packer FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories); foreach (FileInfo fi in files) { - Image img = Image.FromFile(fi.FullName); - if (img != null) + var imgSize = TextureWorker.GetImageSizeFromFile(fi.FullName); + if (imgSize == default) + continue; + int width = imgSize.Width; + int height = imgSize.Height; + + if (width <= AtlasSize && height <= AtlasSize) { - if (img.Width <= AtlasSize && img.Height <= AtlasSize) - { - TextureInfo ti = new TextureInfo(); + TextureInfo ti = new TextureInfo(); - ti.Source = fi.FullName; - ti.Width = img.Width; - ti.Height = img.Height; + ti.Source = fi.FullName; + ti.Width = width; + ti.Height = height; - SourceTextures.Add(ti); + SourceTextures.Add(ti); - Log.WriteLine("Added " + fi.FullName); - } - else - { - Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); - } + Log.WriteLine("Added " + fi.FullName); + } + else + { + Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); } } } @@ -404,7 +425,8 @@ public class Packer _Atlas.Nodes = new List(); textures = _Textures.ToList(); Node root = new Node(); - root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height); + root.Bounds.Width = _Atlas.Width; + root.Bounds.Height = _Atlas.Height; root.SplitType = SplitType.Horizontal; freeList.Add(root); while (freeList.Count > 0 && textures.Count > 0) @@ -432,42 +454,48 @@ public class Packer return textures; } - private Image CreateAtlasImage(Atlas _Atlas) + private SKBitmap CreateAtlasImage(Atlas _Atlas) { - Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - Graphics g = Graphics.FromImage(img); + SKBitmap img = new(_Atlas.Width, _Atlas.Height); + using SKCanvas g = new(img); if (DebugMode) - { - g.FillRectangle(Brushes.Green, new Rectangle(0, 0, _Atlas.Width, _Atlas.Height)); - } + g.DrawRect(0, 0, _Atlas.Width, _Atlas.Height, paintGreen); + foreach (Node n in _Atlas.Nodes) { + SKRect rect = SKRect.Create(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); + if (n.Texture != null) { - Image sourceImg = Image.FromFile(n.Texture.Source); - g.DrawImage(sourceImg, n.Bounds); + using SKBitmap sourceImg = SKBitmap.Decode(n.Texture.Source); + g.DrawBitmap(sourceImg, rect); if (DebugMode) { string label = Path.GetFileNameWithoutExtension(n.Texture.Source); - SizeF labelBox = g.MeasureString(label, SystemFonts.MenuFont, new SizeF(n.Bounds.Size)); - RectangleF rectBounds = new Rectangle(n.Bounds.Location, new Size((int)labelBox.Width, (int)labelBox.Height)); - g.FillRectangle(Brushes.Black, rectBounds); - g.DrawString(label, SystemFonts.MenuFont, Brushes.White, rectBounds); + SKRect labelBox = default; + paintWhite.MeasureText(label, ref labelBox); + SKRect rectBounds = SKRect.Create(n.Bounds.X, n.Bounds.Y, labelBox.Width, labelBox.Height); + float yOff = paintWhite.FontMetrics.Ascent - paintWhite.FontMetrics.Descent - 1; // I am not sure if it's the correct way, but it works + g.DrawRect(rectBounds, paintBlack); + g.DrawText(label, rectBounds.Left, rectBounds.Top - yOff, paintWhite); } } else { - g.FillRectangle(Brushes.DarkMagenta, n.Bounds); + g.DrawRect(rect, paintDarkMagenta); if (DebugMode) { - string label = n.Bounds.Width.ToString() + "x" + n.Bounds.Height.ToString(); - SizeF labelBox = g.MeasureString(label, SystemFonts.MenuFont, new SizeF(n.Bounds.Size)); - RectangleF rectBounds = new Rectangle(n.Bounds.Location, new Size((int)labelBox.Width, (int)labelBox.Height)); - g.FillRectangle(Brushes.Black, rectBounds); - g.DrawString(label, SystemFonts.MenuFont, Brushes.White, rectBounds); + string label = $"{n.Bounds.Width}x{n.Bounds.Height}"; + SKRect labelBox = default; + paintWhite.MeasureText(label, ref labelBox); + SKRect rectBounds = SKRect.Create(n.Bounds.X, n.Bounds.Y, labelBox.Width, labelBox.Height); + float yOff = paintWhite.FontMetrics.Ascent - paintWhite.FontMetrics.Descent - 1; + g.DrawRect(rectBounds, paintBlack); + g.DrawText(label, rectBounds.Left, rectBounds.Top - yOff, paintWhite); } } } + return img; } } diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx index f2fd5e374..679ff2879 100644 --- a/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx +++ b/UndertaleModTool/Scripts/Resource Repackers/ImportGraphics.csx @@ -3,7 +3,7 @@ using System; using System.IO; -using System.Drawing; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -43,7 +43,7 @@ int atlasCount = 0; foreach (Atlas atlas in packer.Atlasses) { string atlasName = Path.Combine(packDir, String.Format(prefix + "{0:000}" + ".png", atlasCount)); - Bitmap atlasBitmap = new Bitmap(atlasName); + SKBitmap atlasBitmap = SKBitmap.Decode(atlasName); UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture(); texture.Name = new UndertaleString("Texture " + ++lastTextPage); texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName); @@ -148,19 +148,23 @@ foreach (Atlas atlas in packer.Atlasses) newSprite.Textures.Add(null); } newSprite.CollisionMasks.Add(newSprite.NewMaskEntry()); - Rectangle bmpRect = new Rectangle(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); - System.Drawing.Imaging.PixelFormat format = atlasBitmap.PixelFormat; - Bitmap cloneBitmap = atlasBitmap.Clone(bmpRect, format); + + SKBitmap cloneBitmap = new(); + var bmpRect = SKRectI.Create(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); + atlasBitmap.ExtractSubset(cloneBitmap, bmpRect); + cloneBitmap = cloneBitmap.Copy(); + int width = ((n.Bounds.Width + 7) / 8) * 8; BitArray maskingBitArray = new BitArray(width * n.Bounds.Height); for (int y = 0; y < n.Bounds.Height; y++) { for (int x = 0; x < n.Bounds.Width; x++) { - Color pixelColor = cloneBitmap.GetPixel(x, y); - maskingBitArray[y * width + x] = (pixelColor.A > 0); + SKColor pixelColor = cloneBitmap.GetPixel(x, y); + maskingBitArray[y * width + x] = (pixelColor.Alpha > 0); } } + cloneBitmap.Dispose(); BitArray tempBitArray = new BitArray(width * n.Bounds.Height); for (int i = 0; i < maskingBitArray.Length; i += 8) { @@ -234,9 +238,16 @@ public enum BestFitHeuristic MaxOneAxis, } +public struct Rect +{ + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } +} public class Node { - public Rectangle Bounds; + public Rect Bounds; public TextureInfo Texture; public SplitType SplitType; } @@ -313,8 +324,8 @@ public class Packer { string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount); //1: Save images - Image img = CreateAtlasImage(atlas); - img.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png); + using SKBitmap img = CreateAtlasImage(atlas); + TextureWorker.SaveImageToFile(atlasName, img); //2: save description in file foreach (Node n in atlas.Nodes) { @@ -345,25 +356,27 @@ public class Packer FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories); foreach (FileInfo fi in files) { - Image img = Image.FromFile(fi.FullName); - if (img != null) + var imgSize = TextureWorker.GetImageSizeFromFile(fi.FullName); + if (imgSize == default) + continue; + int width = imgSize.Width; + int height = imgSize.Height; + + if (width <= AtlasSize && height <= AtlasSize) { - if (img.Width <= AtlasSize && img.Height <= AtlasSize) - { - TextureInfo ti = new TextureInfo(); + TextureInfo ti = new TextureInfo(); - ti.Source = fi.FullName; - ti.Width = img.Width; - ti.Height = img.Height; + ti.Source = fi.FullName; + ti.Width = width; + ti.Height = height; - SourceTextures.Add(ti); + SourceTextures.Add(ti); - Log.WriteLine("Added " + fi.FullName); - } - else - { - Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); - } + Log.WriteLine("Added " + fi.FullName); + } + else + { + Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); } } } @@ -456,7 +469,8 @@ public class Packer _Atlas.Nodes = new List(); textures = _Textures.ToList(); Node root = new Node(); - root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height); + root.Bounds.Width = _Atlas.Width; + root.Bounds.Height = _Atlas.Height; root.SplitType = SplitType.Horizontal; freeList.Add(root); while (freeList.Count > 0 && textures.Count > 0) @@ -484,24 +498,21 @@ public class Packer return textures; } - private Image CreateAtlasImage(Atlas _Atlas) + private SKBitmap CreateAtlasImage(Atlas _Atlas) { - Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - Graphics g = Graphics.FromImage(img); + SKBitmap img = new(_Atlas.Width, _Atlas.Height); + using SKCanvas g = new(img); foreach (Node n in _Atlas.Nodes) { if (n.Texture != null) { - Image sourceImg = Image.FromFile(n.Texture.Source); - g.DrawImage(sourceImg, n.Bounds); + using SKBitmap sourceImg = SKBitmap.Decode(n.Texture.Source); + SKRect rect = SKRect.Create(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); + g.DrawBitmap(sourceImg, rect); } } - // DPI FIX START - Bitmap ResolutionFix = new Bitmap(img); - ResolutionFix.SetResolution(96.0F, 96.0F); - Image img2 = ResolutionFix; - return img2; - // DPI FIX END + + return img; } } diff --git a/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx b/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx index a9ac426d8..ac26d56b4 100644 --- a/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx +++ b/UndertaleModTool/Scripts/Resource Repackers/ImportMasks.csx @@ -4,7 +4,6 @@ using System; using System.IO; -using System.Drawing; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -38,15 +37,13 @@ foreach (string file in dirFiles) { throw new ScriptException("Getting the sprite name of " + FileNameWithExtension + " failed."); } - if (Data.Sprites.ByName(spriteName) == null) // Reject non-existing sprites - { - throw new ScriptException(FileNameWithExtension + " could not be imported as the sprite " + spriteName + " does not exist."); - } - using (Image img = Image.FromFile(file)) - { - if ((Data.Sprites.ByName(spriteName).Width != (uint)img.Width) || (Data.Sprites.ByName(spriteName).Height != (uint)img.Height)) - throw new ScriptException(FileNameWithExtension + " is not the proper size to be imported! Please correct this before importing! The proper dimensions are width: " + Data.Sprites.ByName(spriteName).Width.ToString() + " px, height: " + Data.Sprites.ByName(spriteName).Height.ToString() + " px."); - } + var sprite = Data.Sprites.ByName(spriteName); + if (sprite is null) // Reject non-existing sprites + throw new ScriptException($"{FileNameWithExtension} could not be imported as the sprite {spriteName} does not exist."); + + var imgSize = TextureWorker.GetImageSizeFromFile(file); + if ((sprite.Width != (uint)imgSize.Width) || (sprite.Height != (uint)imgSize.Height)) + throw new ScriptException($"{FileNameWithExtension} is not the proper size to be imported! Please correct this before importing! The proper dimensions are width: {sprite.Width} px, height: {sprite.Height} px."); Int32 validFrameNumber = 0; try diff --git a/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx b/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx index ed88ba3c5..9d98ce05e 100644 --- a/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx +++ b/UndertaleModTool/Scripts/Resource Repackers/NewTextureRepacker.csx @@ -26,7 +26,7 @@ using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using System.Collections.Concurrent; -using System.Drawing; +using SkiaSharp; using UndertaleModLib.Scripting; using UndertaleModLib.Util; using UndertaleModLib.Models; @@ -440,12 +440,10 @@ await Task.Run(() => UndertaleEmbeddedTexture tex = new UndertaleEmbeddedTexture(); tex.Name = new UndertaleString("Texture " + ++lastTextPage); Data.EmbeddedTextures.Add(tex); - Bitmap img = new Bitmap(atlas.Width, atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - // DPI fix - img.SetResolution(96.0F, 96.0F); + using SKBitmap img = new(atlas.Width, atlas.Height); + using SKCanvas g = new(img); - Graphics g = Graphics.FromImage(img); tex.Scaled = group.First().Scaled; // Make sure the original pane "Scaled" value is mantained. // Dump debug info regarding splits @@ -457,8 +455,8 @@ await Task.Run(() => { f.WriteLine($"tex: {texPageItems.IndexOf(item)}: {item.NewRect.X}, {item.NewRect.Y}, {item.NewRect.Width}, {item.NewRect.Height}"); - using (Bitmap source = new Bitmap(item.Filename)) - g.DrawImage(source, item.NewRect.X, item.NewRect.Y); + using SKBitmap source = TextureWorker.ReadImageFromFile(item.Filename); + g.DrawBitmap(source, item.NewRect.X, item.NewRect.Y); item.Item.TexturePage = tex; item.Item.SourceX = (ushort)item.NewRect.X; @@ -470,10 +468,8 @@ await Task.Run(() => // Save atlas into a file and load it back into string atlasFile = Path.Combine(packagerDirectory, $"atlas_{atlasName}.png"); - img.Save(atlasFile, System.Drawing.Imaging.ImageFormat.Png); + TextureWorker.SaveImageToFile(atlasFile, img); tex.TextureData.TextureBlob = File.ReadAllBytes(atlasFile); - - img.Dispose(); } else { @@ -493,21 +489,16 @@ await Task.Run(() => int potw = NearestPowerOf2((uint)item.OriginalRect.Width), poth = NearestPowerOf2((uint)item.OriginalRect.Height); - Bitmap img = new Bitmap(potw, poth, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - // DPI fix - img.SetResolution(96.0F, 96.0F); - - Graphics g = Graphics.FromImage(img); - - // Load texture - using (Bitmap source = new Bitmap(item.Filename)) - g.DrawImage(source, 0, 0); + // Load texture and draw in on a new one with POT size + using SKBitmap img = new(potw, poth); + using (SKCanvas g = new(img)) + { + using SKBitmap source = SKBitmap.Decode(item.Filename); + g.DrawBitmap(source, 0, 0); + } itemFile = Path.Combine(packagerDirectory, $"pot_{texPageItems.IndexOf(item)}.png"); - img.Save(itemFile, System.Drawing.Imaging.ImageFormat.Png); - - img.Dispose(); + TextureWorker.SaveImageToFile(itemFile, img); } tex.TextureData.TextureBlob = File.ReadAllBytes(itemFile); diff --git a/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx b/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx index 4ecd1ba8f..df29c43ba 100644 --- a/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx +++ b/UndertaleModTool/Scripts/Resource Repackers/ReduceEmbeddedTexturePages.csx @@ -4,7 +4,7 @@ using System; using System.IO; -using System.Drawing; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -128,7 +128,6 @@ int atlasCount = 0; foreach (Atlas atlas in packer.Atlasses) { string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount); - Bitmap atlasBitmap = new Bitmap(atlasName); UndertaleEmbeddedTexture texture = new UndertaleEmbeddedTexture(); texture.Name = new UndertaleString("Texture " + ++lastTextPage); texture.TextureData.TextureBlob = File.ReadAllBytes(atlasName); @@ -273,9 +272,17 @@ public enum BestFitHeuristic MaxOneAxis, } +public struct Rect +{ + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } +} + public class Node { - public Rectangle Bounds; + public Rect Bounds; public TextureInfo Texture; public SplitType SplitType; } @@ -352,8 +359,8 @@ public class Packer { string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount); //1: Save images - Image img = CreateAtlasImage(atlas); - img.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png); + SKBitmap img = CreateAtlasImage(atlas); + TextureWorker.SaveImageToFile(atlasName, img); //2: save description in file foreach (Node n in atlas.Nodes) { @@ -384,25 +391,27 @@ public class Packer FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories); foreach (FileInfo fi in files) { - Image img = Image.FromFile(fi.FullName); - if (img != null) + var imgSize = TextureWorker.GetImageSizeFromFile(fi.FullName); + if (imgSize == default) + continue; + int width = imgSize.Width; + int height = imgSize.Height; + + if (width <= AtlasSize && height <= AtlasSize) { - if (img.Width <= AtlasSize && img.Height <= AtlasSize) - { - TextureInfo ti = new TextureInfo(); + TextureInfo ti = new TextureInfo(); - ti.Source = fi.FullName; - ti.Width = img.Width; - ti.Height = img.Height; + ti.Source = fi.FullName; + ti.Width = width; + ti.Height = height; - SourceTextures.Add(ti); + SourceTextures.Add(ti); - Log.WriteLine("Added " + fi.FullName); - } - else - { - Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); - } + Log.WriteLine("Added " + fi.FullName); + } + else + { + Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); } } } @@ -495,7 +504,8 @@ public class Packer _Atlas.Nodes = new List(); textures = _Textures.ToList(); Node root = new Node(); - root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height); + root.Bounds.Width = _Atlas.Width; + root.Bounds.Height = _Atlas.Height; root.SplitType = SplitType.Horizontal; freeList.Add(root); while (freeList.Count > 0 && textures.Count > 0) @@ -523,23 +533,20 @@ public class Packer return textures; } - private Image CreateAtlasImage(Atlas _Atlas) + private SKBitmap CreateAtlasImage(Atlas _Atlas) { - Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - Graphics g = Graphics.FromImage(img); + SKBitmap img = new(_Atlas.Width, _Atlas.Height); + using SKCanvas g = new(img); foreach (Node n in _Atlas.Nodes) { if (n.Texture != null) { - Image sourceImg = Image.FromFile(n.Texture.Source); - g.DrawImage(sourceImg, n.Bounds); + using SKBitmap sourceImg = SKBitmap.Decode(n.Texture.Source); + SKRect rect = SKRect.Create(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); + g.DrawBitmap(sourceImg, rect); } } - // DPI FIX START - Bitmap ResolutionFix = new Bitmap(img); - ResolutionFix.SetResolution(96.0F, 96.0F); - Image img2 = ResolutionFix; - return img2; - // DPI FIX END + + return img; } } \ No newline at end of file diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx index 88ff02a36..cc7094863 100644 --- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx +++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTextures.csx @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Drawing; using System.Collections; using System.Collections.Generic; using System.Linq; diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx index 47b399b59..eab3a8511 100644 --- a/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx +++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportAllTexturesGrouped.csx @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Drawing; using System.Collections; using System.Collections.Generic; using System.Linq; diff --git a/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx b/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx index d07be5afe..17814b9d5 100644 --- a/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx +++ b/UndertaleModTool/Scripts/Resource Unpackers/ExportTextureGroups.csx @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Drawing; using System.Collections; using System.Collections.Generic; using System.Linq; diff --git a/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx b/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx index 66982b35b..e71d94811 100644 --- a/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx +++ b/UndertaleModTool/Scripts/Resource Unpackers/MergeImages.csx @@ -1,8 +1,6 @@ using System; using System.IO; -using System.Drawing; -using System.Drawing.Imaging; -using System.Drawing.Drawing2D; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -10,19 +8,16 @@ using System.Text; using UndertaleModLib.Util; string importFolderA = PromptChooseDirectory(); -if (importFolderA == null) { +if (importFolderA is null) throw new ScriptException("The import folder was not set."); -} string importFolderB = PromptChooseDirectory(); -if (importFolderB == null) { +if (importFolderB is null) throw new ScriptException("The import folder was not set."); -} string exportFolder = PromptChooseDirectory(); -if (exportFolder == null) { +if (exportFolder is null) throw new ScriptException("The export folder was not set."); -} string searchPattern = "*.png"; @@ -30,31 +25,25 @@ DirectoryInfo textureDirectoryA = new DirectoryInfo(importFolderA); DirectoryInfo textureDirectoryB = new DirectoryInfo(importFolderB); FileInfo[] filesA = textureDirectoryA.GetFiles(searchPattern, SearchOption.AllDirectories); -foreach (FileInfo fileA in filesA) { +foreach (FileInfo fileA in filesA) +{ FileInfo[] fileMatch = textureDirectoryB.GetFiles(fileA.Name); - if (fileMatch == null) { + if (fileMatch is null) continue; - } FileInfo fileB = fileMatch[0]; - Bitmap bitmapA = new Bitmap(System.IO.Path.Combine(importFolderA, fileA.Name)); - Bitmap bitmapB = new Bitmap(System.IO.Path.Combine(importFolderB, fileA.Name)); - int width = bitmapA.Size.Width + bitmapB.Size.Width; - int height = bitmapA.Size.Width; - if (bitmapB.Size.Width > height) { - height = bitmapB.Size.Width; + using SKBitmap bitmapA = SKBitmap.Decode(Path.Combine(importFolderA, fileA.Name)); + using SKBitmap bitmapB = SKBitmap.Decode(Path.Combine(importFolderB, fileA.Name)); + int width = bitmapA.Width + bitmapB.Width; + int height = bitmapA.Height; + if (bitmapB.Width > height) + height = bitmapB.Width; + + using SKBitmap outputBitmap = new(width, height); + using (SKCanvas g = new(outputBitmap)) + { + g.DrawBitmap(bitmapA, 0, 0); + g.DrawBitmap(bitmapB, bitmapA.Width, 0); } - Bitmap outputBitmap = new Bitmap(width, height); - - outputBitmap = SuperimposeOntoBitmap(bitmapA, outputBitmap, 0); - outputBitmap = SuperimposeOntoBitmap(bitmapB, outputBitmap, bitmapA.Size.Width); - - outputBitmap.Save(System.IO.Path.Combine(exportFolder, fileA.Name), ImageFormat.Png); -} - -Bitmap SuperimposeOntoBitmap(Bitmap bitmapToAdd, Bitmap baseBitmap, int x) { - Graphics g = Graphics.FromImage(baseBitmap); - g.CompositingMode = CompositingMode.SourceOver; - g.DrawImage(bitmapToAdd, new System.Drawing.Point(x, 0)); - return baseBitmap; + TextureWorker.SaveImageToFile(Path.Combine(exportFolder, fileA.Name), outputBitmap); } diff --git a/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx b/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx index 7d038bea6..bae30e190 100644 --- a/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx +++ b/UndertaleModTool/Scripts/Technical Scripts/ImportGraphics_Full_Repack.csx @@ -4,7 +4,7 @@ using System; using System.IO; -using System.Drawing; +using SkiaSharp; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -338,19 +338,23 @@ foreach (Atlas atlas in packer.Atlasses) newSprite.Textures.Add(null); } newSprite.CollisionMasks.Add(newSprite.NewMaskEntry()); - Rectangle bmpRect = new Rectangle(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); - System.Drawing.Imaging.PixelFormat format = atlasBitmap.PixelFormat; - Bitmap cloneBitmap = atlasBitmap.Clone(bmpRect, format); + + SKBitmap cloneBitmap = new(); + var bmpRect = SKRectI.Create(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); + atlasBitmap.ExtractSubset(cloneBitmap, bmpRect); + cloneBitmap = cloneBitmap.Copy(); + int width = ((n.Bounds.Width + 7) / 8) * 8; BitArray maskingBitArray = new BitArray(width * n.Bounds.Height); for (int y = 0; y < n.Bounds.Height; y++) { for (int x = 0; x < n.Bounds.Width; x++) { - Color pixelColor = cloneBitmap.GetPixel(x, y); - maskingBitArray[y * width + x] = (pixelColor.A > 0); + SKColor pixelColor = cloneBitmap.GetPixel(x, y); + maskingBitArray[y * width + x] = (pixelColor.Alpha > 0); } } + cloneBitmap.Dispose(); BitArray tempBitArray = new BitArray(width * n.Bounds.Height); for (int i = 0; i < maskingBitArray.Length; i += 8) { @@ -428,9 +432,17 @@ public enum BestFitHeuristic MaxOneAxis, } +public struct Rect +{ + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } +} + public class Node { - public Rectangle Bounds; + public Rect Bounds; public TextureInfo Texture; public SplitType SplitType; } @@ -507,8 +519,8 @@ public class Packer { string atlasName = String.Format(prefix + "{0:000}" + ".png", atlasCount); //1: Save images - Image img = CreateAtlasImage(atlas); - img.Save(atlasName, System.Drawing.Imaging.ImageFormat.Png); + using SKBitmap img = CreateAtlasImage(atlas); + TextureWorker.SaveImageToFile(atlasName, img); //2: save description in file foreach (Node n in atlas.Nodes) { @@ -539,25 +551,27 @@ public class Packer FileInfo[] files = di.GetFiles(_Wildcard, SearchOption.AllDirectories); foreach (FileInfo fi in files) { - Image img = Image.FromFile(fi.FullName); - if (img != null) + var imgSize = TextureWorker.GetImageSizeFromFile(fi.FullName); + if (imgSize == default) + continue; + int width = imgSize.Width; + int height = imgSize.Height; + + if (width <= AtlasSize && height <= AtlasSize) { - if (img.Width <= AtlasSize && img.Height <= AtlasSize) - { - TextureInfo ti = new TextureInfo(); + TextureInfo ti = new TextureInfo(); - ti.Source = fi.FullName; - ti.Width = img.Width; - ti.Height = img.Height; + ti.Source = fi.FullName; + ti.Width = width; + ti.Height = height; - SourceTextures.Add(ti); + SourceTextures.Add(ti); - Log.WriteLine("Added " + fi.FullName); - } - else - { - Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); - } + Log.WriteLine("Added " + fi.FullName); + } + else + { + Error.WriteLine(fi.FullName + " is too large to fix in the atlas. Skipping!"); } } } @@ -650,7 +664,8 @@ public class Packer _Atlas.Nodes = new List(); textures = _Textures.ToList(); Node root = new Node(); - root.Bounds.Size = new Size(_Atlas.Width, _Atlas.Height); + root.Bounds.Width = _Atlas.Width; + root.Bounds.Height = _Atlas.Height; root.SplitType = SplitType.Horizontal; freeList.Add(root); while (freeList.Count > 0 && textures.Count > 0) @@ -678,23 +693,20 @@ public class Packer return textures; } - private Image CreateAtlasImage(Atlas _Atlas) + private SKBitmap CreateAtlasImage(Atlas _Atlas) { - Image img = new Bitmap(_Atlas.Width, _Atlas.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - Graphics g = Graphics.FromImage(img); + SKBitmap img = new(_Atlas.Width, _Atlas.Height); + using SKCanvas g = new(img); foreach (Node n in _Atlas.Nodes) { if (n.Texture != null) { - Image sourceImg = Image.FromFile(n.Texture.Source); - g.DrawImage(sourceImg, n.Bounds); + using SKBitmap sourceImg = SKBitmap.Decode(n.Texture.Source); + SKRect rect = SKRect.Create(n.Bounds.X, n.Bounds.Y, n.Bounds.Width, n.Bounds.Height); + g.DrawBitmap(sourceImg, rect); } } - // DPI FIX START - Bitmap ResolutionFix = new Bitmap(img); - ResolutionFix.SetResolution(96.0F, 96.0F); - Image img2 = ResolutionFix; - return img2; - // DPI FIX END + + return img; } } \ No newline at end of file