diff --git a/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePacker.cs b/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePacker.cs index a745e0f..d3b2410 100644 --- a/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePacker.cs +++ b/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePacker.cs @@ -22,9 +22,12 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas int powLimit; Node masterNode; - Dictionary imageHandlers; + Dictionary imageHandlersDictionary; int textureLength; - + int tpl; + int TexturePowerLength { get { return tpl; } set { textureLength = (int)Math.Pow(2, value); tpl = value; } } + public int TextureLength { get { return textureLength; } } + public int TexturesFound { get { return imageHandlersDictionary.Count; } } /// /// Machine to pack multiple textures into one large texture. /// @@ -35,22 +38,28 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas { this.powLimit = powLimit; string[] paths = Directory.GetFiles(rootDirectoryPath); - textureLength = startingPower; + TexturePowerLength = startingPower; List imageHandlers = new List(); + int minAreaRequired = 0; for (int pathID = 0; pathID < paths.Length; pathID++) { SupportedExtensions extension; - if (Enum.TryParse(Path.GetExtension(paths[pathID]), out extension)) + if (Enum.TryParse(Path.GetExtension(paths[pathID]).ToLower().Substring(1), out extension)) { ImageHandler image = new ImageHandler(paths[pathID]); imageHandlers.Add(image); + minAreaRequired += image.Area; + while (minAreaRequired > textureLength*textureLength) + { + TexturePowerLength++; + } } } imageHandlers.Sort(); - this.imageHandlers = new Dictionary(); + this.imageHandlersDictionary = new Dictionary(); foreach (ImageHandler imageHandler in imageHandlers) { - this.imageHandlers.Add(imageHandler.Name, imageHandler); + this.imageHandlersDictionary.Add(imageHandler.Name, imageHandler); } } @@ -63,23 +72,23 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas masterNode = new Node(); masterNode.region.Width = textureLength; masterNode.region.Height = textureLength; - Queue imageHandlerQueue = new Queue(imageHandlers.Values); + Queue imageHandlerQueue = new Queue(imageHandlersDictionary.Values); ImageHandler imageHandler; while (imageHandlerQueue.TryDequeue(out imageHandler)) { Node activeNode = null; - do + activeNode = masterNode.InsertImageHandler(imageHandler); + if (activeNode == null) { - activeNode = masterNode.InsertImageHandler(imageHandler); - if (activeNode == null) + if (!AutoCorrectAtlasSize || TexturePowerLength + 1 > powLimit) { - if (!AutoCorrectAtlasSize || (textureLength *= 2) > Math.Pow(2, powLimit)) - { - throw new InvalidOperationException("Dimensions of texture goes past limit amount of " + powLimit + " which is " + Math.Pow(2, powLimit) + ". New texture size would be " + textureLength + "x" + textureLength + "."); - } + throw new InvalidOperationException("Texture not large enough. Current size: " + textureLength + "x" + textureLength + "."); } + TexturePowerLength += 1; + imageHandlerQueue.Clear(); + Build(AutoCorrectAtlasSize); } - while (activeNode == null); + } } @@ -92,28 +101,33 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas { GraphicsOptions gOptions = new GraphicsOptions(); - TextureAtlasData.TextureAtlasRegion[] regions = new TextureAtlasData.TextureAtlasRegion[imageHandlers.Count]; + TextureAtlasData.TextureAtlasRegion[] regions = new TextureAtlasData.TextureAtlasRegion[imageHandlersDictionary.Count]; using (Image atlasTexture = new Image(textureLength, textureLength)) { - ImageHandler[] imageHandlers = this.imageHandlers.Values.ToArray(); + ImageHandler[] imageHandlers = this.imageHandlersDictionary.Values.ToArray(); for (int i = 0; i < imageHandlers.Length; i++) { regions[i] = new TextureAtlasData.TextureAtlasRegion(); - ImageHandler imageH = imageHandlers[i]; - regions[i].SetBounds(imageH.x, imageH.y, imageH.Width, imageH.Height); - regions[i].ninePatchData = imageH.ninePatchData; - atlasTexture.Mutate(img => img.DrawImage(gOptions, imageH.image, new Point(imageH.x, imageH.y))); + ImageHandler ih = imageHandlers[i]; + regions[i].SetBounds(ih.x, ih.y, ih.Width, ih.Height); + regions[i].ninePatchData = ih.ninePatchData; + regions[i].name = ih.Name; + using (Image image = Image.Load(ih.path)) + { + atlasTexture.Mutate(img => img.DrawImage(gOptions, image, new Point(ih.x, ih.y))); + } } - using (FileStream stream = new FileStream(output + atlasName + ".png", FileMode.Create)) + Directory.CreateDirectory(output); + using (FileStream stream = new FileStream(output + "/" + atlasName + ".png", FileMode.Create)) { atlasTexture.SaveAsPng(stream); } } - string serialized = JsonConvert.SerializeObject(new TextureAtlasData(atlasName + ".png", regions)); + string serialized = JsonConvert.SerializeObject(new TextureAtlasData(atlasName + ".png", regions), Formatting.Indented); - using (StreamWriter stream = new StreamWriter(output + atlasName + ".tatlas")) + using (StreamWriter stream = new StreamWriter(output + "/" + atlasName + ".tatlas")) { stream.WriteLine(serialized); } @@ -121,24 +135,20 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas public void SetNinePatch(string fileName, int a, int b, int c, int d) { - ImageHandler imageHandler = imageHandlers[fileName]; + ImageHandler imageHandler = imageHandlersDictionary[fileName]; NinePatchData ninePatchData = new NinePatchData(fileName, a, b, c, d); imageHandler.ninePatchData = ninePatchData; } public void RemoveNinePatch(string fileName) { - imageHandlers[fileName].ninePatchData = null; + imageHandlersDictionary[fileName].ninePatchData = null; } public void Dispose() { - ImageHandler[] imageHandlers = this.imageHandlers.Values.ToArray(); - this.imageHandlers.Clear(); - foreach (ImageHandler imageHandler in imageHandlers) - { - imageHandler.Dispose(); - } + ImageHandler[] imageHandlers = this.imageHandlersDictionary.Values.ToArray(); + this.imageHandlersDictionary.Clear(); } private class Node @@ -148,13 +158,12 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas public Node childA { get { if (a == null) a = new Node(this); return a; } set { value.parent = this; a = value; } } public Node childB { get { if (b == null) { b = new Node(this); } return b; } set { value.parent = this; b = value; } } public Rectangle region; - public bool containsImage = false; - public bool Filled { get { return containsImage || (a != null && b != null && a.Filled && b.Filled); } } - + public bool ContainsImage = false; + public bool CanPlaceImage { get { return (a == null && b == null && !ContainsImage); } } public Node(Node parent = null) { this.parent = parent; - region = parent.region; + if (parent != null) region = parent.region; } /// @@ -171,17 +180,21 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas if (a == null) { childA.region.Width = imageHandler.Width; - childB.region.Width = region.Width - childA.region.Width; } - if (!childA.Filled && imageHandler.Width <= childA.region.Width) + Node attemptedNode = null; + if (!childA.ContainsImage && imageHandler.Width <= childA.region.Width) { - return childA.InsertImageHandler(imageHandler); + attemptedNode = childA.InsertImageHandler(imageHandler); } - if (!childB.Filled) + if (attemptedNode == null && !childB.ContainsImage) { - return childB.InsertImageHandler(imageHandler); + childB.region.Width = region.Width - childA.region.Width; + childB.region.X = childA.region.X + childA.region.Width; + attemptedNode = childB.InsertImageHandler(imageHandler); } + + return attemptedNode; } } else if (imageHandler.Height != region.Height) @@ -191,35 +204,37 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas if (a == null) { childA.region.Height = imageHandler.Height; + } + Node attemptedNode = null; + if (!childA.ContainsImage && imageHandler.Height <= childA.region.Height) + { + attemptedNode = childA.InsertImageHandler(imageHandler); + } + + if (attemptedNode == null && !childB.ContainsImage) + { childB.region.Height = region.Height - childA.region.Height; + childB.region.Y = childA.region.Y + childA.region.Height; + attemptedNode = childB.InsertImageHandler(imageHandler); } - - if (!childA.Filled && imageHandler.Width <= childA.region.Width) - { - return childA.InsertImageHandler(imageHandler); - } - - if (!childB.Filled) - { - return childB.InsertImageHandler(imageHandler); - } + return attemptedNode; } } - else + else if (CanPlaceImage) { imageHandler.x = region.X; imageHandler.y = region.Y; - containsImage = true; + ContainsImage = true; return this; } return null; } } - private class ImageHandler : IComparable, IDisposable + private class ImageHandler : IComparable { public readonly string path; - public readonly Image image; + public readonly IImageInfo image; public int Area { get { return image.Width * image.Height; } } public string Name { get { return Path.GetFileName(path); } } public int Width { get { return image.Width; } } @@ -232,7 +247,7 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas this.path = path; using (FileStream stream = new FileStream(path, FileMode.Open)) { - image = Image.Load(stream); + image = Image.Identify(stream); } } @@ -240,11 +255,6 @@ namespace RecrownedAthenaeum.Tools.TextureAtlas { return Area - tImage.Area; } - - public void Dispose() - { - image.Dispose(); - } } } } diff --git a/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePackerCommand.cs b/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePackerCommand.cs index fd7c7d7..44d88a8 100644 --- a/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePackerCommand.cs +++ b/RecrownedAthenaeum.ConsoleTools/TextureAtlasTools/TexturePackerCommand.cs @@ -9,7 +9,6 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools { class TexturePackerCommand : EngineCommand { - TexturePacker texturePacker; public TexturePackerCommand() : base("texturepacker") { } @@ -38,9 +37,13 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools public override void Run(string[] arguments) { + TexturePacker texturePacker = null; string path = null; int mp = 8; int sp = 8; + bool dau = false; + string output = null; + if (arguments == null) throw new ArgumentException("Requires arguments. Type \"help texturepacker\" for more info."); for (int i = 0; i < arguments.Length; i++) @@ -58,19 +61,28 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools { if (i + 1 >= arguments.Length || !int.TryParse(arguments[i + 1], out sp)) throw new ArgumentException("sp is not followed by maximum power."); } + if (arguments[i] == "-o") + { + if (i + 1 >= arguments.Length) throw new ArgumentException("-o is not followed by path for output files. (eg. path/to/file where file is the name for the atlas.)"); + output = arguments[i + 1]; + } + + if (i == arguments.Length - 1 && output == null) + { + throw new ArgumentException("no -o argument found to specify output."); + } + + if (arguments[i] == "-dau") dau = true; } texturePacker = new TexturePacker(path, mp, sp); + ConsoleUtilities.WriteWrappedLine("Calculated minimum texture size: " + texturePacker.TextureLength + "x" + texturePacker.TextureLength + " with a total of " + texturePacker.TexturesFound + " textures."); if (texturePacker != null) { - bool dau = false; - for (int i = 0; i < arguments.Length; i++) - { - if (arguments[i] == "-dau") dau = true; - } try { texturePacker.Build(!dau); - } catch (InvalidOperationException e) + } + catch (InvalidOperationException e) { throw new ArgumentException(e.Message); } @@ -91,21 +103,8 @@ namespace RecrownedAthenaeum.Tools.TextureAtlasTools } } } - - for (int i = 0; i < arguments.Length; i++) - { - if (arguments[i] == "-o") - { - if (i + 1 >= arguments.Length) throw new ArgumentException("-o is not followed by path for output files. (eg. path/to/file where file is the name for the atlas.)"); - texturePacker.Save(Path.GetDirectoryName(arguments[i + 1]), Path.GetFileName(arguments[i + 1])); - return; - } - - if (i == arguments.Length - 1) - { - throw new ArgumentException("no -o argument found to specify output."); - } - } + texturePacker.Save(Path.GetDirectoryName(output), Path.GetFileName(output)); + Console.WriteLine("Complete. Final texture size: " + texturePacker.TextureLength + "x" + texturePacker.TextureLength + "."); } else { diff --git a/RecrownedAthenaeum/Persistence/PreferencesManager.cs b/RecrownedAthenaeum/Persistence/PreferencesManager.cs index f005cbf..30e343d 100644 --- a/RecrownedAthenaeum/Persistence/PreferencesManager.cs +++ b/RecrownedAthenaeum/Persistence/PreferencesManager.cs @@ -58,12 +58,18 @@ namespace RecrownedAthenaeum.Persistence private bool LoadSpecific(Type preference) { string path = savePath + "/" + preference.Name + ".xml"; - if (File.Exists(path)) + try { - Stream stream = new FileStream(path, FileMode.Open); - preferenceList[preference] = xmlSerializer.Deserialize(stream); - stream.Close(); - return true; + if (File.Exists(path)) + { + Stream stream = new FileStream(path, FileMode.Open); + preferenceList[preference] = xmlSerializer.Deserialize(stream); + stream.Close(); + return true; + } + } catch (InvalidOperationException) + { + return false; } return false; }