internal static bool packSprites(string sheetFile, string mapFile = "") { bool result = true; // generate our output ImagePacker imagePacker = new ImagePacker(); Bitmap outputSheet; Dictionary <string, Rectangle> outputMap; try { // pack the image, generating a map only if desired if (imagePacker.PackImage(images, false, true, 1024, 1024, 3, mapFile != "", out outputSheet, out outputMap) == 0) { outputSheet.Save(sheetFile); if (mapFile != "") { saveMap(mapFile, outputMap); } Console.WriteLine("Generated Sprite Sheet: {0}.", sheetFile); } else { Console.WriteLine("There was an error making the image sheet {0}.", sheetFile); result = false; } } catch (Exception e) { Console.WriteLine("Error saving sheet: {0} {1}", sheetFile, e.Message); result = false; } return(result); }
public override AtlasContent Process(AtlasDeclaration input, ContentProcessorContext context) { Dictionary <int, Bitmap> images = new Dictionary <int, Bitmap>(); Dictionary <int, string> imageNames = new Dictionary <int, string>(); ImagePacker imagePacker = new ImagePacker(); var imgFiles = input.Images.Select(i => Path.Combine(input.AtlasRootDir, i.Replace('/', '\\'))); if (imgFiles.Count() == 0) { throw new ArgumentException("No Image found"); } Bitmap output; Dictionary <string, Sprite> map; imagePacker.PackImage(imgFiles, true, true, 4096, 4096, 0, true, out output, out map); var finalSprites = map.Select(s => { s.Value.Name = s.Key.Substring(0, s.Key.LastIndexOf('.')).Substring(input.AtlasRootDir.Length + 1).Replace('\\', '/').Trim('.', '/'); return(s.Value); }).ToArray(); var atlasPngPath = Path.Combine(input.AtlasRootDir, input.Name + ".png"); using (FileStream outputSpriteFile = new FileStream(atlasPngPath, FileMode.Create)) { output.Save(outputSpriteFile, ImageFormat.Png); } context.AddOutputFile(atlasPngPath); ExternalReference <TextureContent> texture = new ExternalReference <TextureContent>(atlasPngPath); texture = BuildTexture($"{input.Name}Texture", texture, context); return(new AtlasContent { Texture = texture, Sprites = finalSprites }); }
public static int Pack(string atlasDir, string imageOutputDir, string codeOutputDir, Func <string, string> namingFunc) { string imgFile = Path.Combine(imageOutputDir, "Atlas.png"); string codeFile = Path.Combine(codeOutputDir, "Atlas.cs"); // find all images List <string> images = new List <string>(); foreach (var file in Directory.GetFiles(atlasDir, "*.*", SearchOption.AllDirectories)) { FileInfo info = new FileInfo(file); if (info.Extension == ".png") { images.Add(info.FullName); } } // PACKIT! var imagePacker = new ImagePacker(); Bitmap outputImage; Dictionary <string, Rectangle> outputMap; int result = imagePacker.PackImage(images, true, true, MAXIMGSIZE, MAXIMGSIZE, 1, true, out outputImage, out outputMap); if (result != 0) { Dbg.Write("There was an error making the image sheet."); return(result); } if (File.Exists(imgFile)) { File.Delete(imgFile); } IImageExporter imageExporter = new PngImageExporter(); imageExporter.Save(imgFile, outputImage); if (File.Exists(codeFile)) { File.Delete(codeFile); } CsGenExporter mapExporter = new CsGenExporter(); mapExporter.namerFunc = namingFunc; mapExporter.atlasSize = outputImage.Width; mapExporter.Save(codeFile, outputMap); return(200); }
public void PackingWorks() { var files = GetSpriteFiles(); Assert.NotEmpty(files); var packer = new ImagePacker(); packer.PackImage(files, true, true, 4096, 4096, 2, out var image, out var map); Assert.NotNull(image); Assert.NotNull(map); var outFile = new FileInfo("TestOutput.png"); using (var stream = outFile.Open(FileMode.Create)) { image.Save(stream, new PngEncoder()); } }
void PackOnce() { if (mPackTasks.Count == 0) { return; } // generate our output ImagePacker imagePacker = new ImagePacker(); Bitmap outputImage; Dictionary <string, System.Drawing.Rectangle> outputMap; PackTask pt = mPackTasks.Dequeue(); Console.WriteLine("Packing {0} ({1} left to pack)", pt.outputFile, mPackTasks.Count); // pack the image, generating a map only if desired int result = imagePacker.PackImage(pt.inputFiles, REQUIRE_POW2, REQUIRE_SQUARE, 4096, 4096, PADDING, true, out outputImage, out outputMap); if (result != 0) { Console.WriteLine("There was an error making the image sheet."); return; } // try to save using our exporters try { if (File.Exists(pt.outputFile)) { File.Delete(pt.outputFile); } mImageExporter.Save(pt.outputFile, outputImage); Console.WriteLine("Saved atlas {0}.", pt.outputFile); } catch (Exception e) { Console.WriteLine("Error saving file: " + e.Message); return; } if (mMapExporter != null) { try { if (File.Exists(pt.outputMap)) { File.Delete(pt.outputMap); } mMapExporter.Save(pt.outputMap, outputMap); Console.WriteLine("Saved atlas map {0}.", pt.outputMap); } catch (Exception e) { Console.WriteLine("Error saving file: " + e.Message); return; } } foreach (string filename in pt.inputFiles) { if (File.Exists(filename)) { try { File.Delete(filename); } catch (IOException) { // Welp } } } }
/// <summary> /// Create the atlas image and map xml /// </summary> /// <returns>Success code if complete, any other FailCode otherwise</returns> public FailCode CreateAtlas() { //input checks if (Atlas == null) { throw new BadMemeException("you forgot to set the atlas object. nice."); } totalMillisecondsToCreateImage = 0; stopwatch.Restart(); //configure names and paths //set the name of the mapfile based on the filename of the atlas image, if not set from xml load Logging.Info("[atlas file {0}]: Preparing to create atlas", Atlas.AtlasFile); if (string.IsNullOrEmpty(Atlas.MapFile)) { Atlas.MapFile = string.Format("{0}.xml", Path.GetFileNameWithoutExtension(Atlas.AtlasFile)); } //set the paths of the created image and map file Atlas.AtlasImageFilePath = Path.Combine(Atlas.AtlasSaveDirectory, Atlas.AtlasFile); Atlas.AtlasMapFilePath = Path.Combine(Atlas.AtlasSaveDirectory, Atlas.MapFile); //set location to extract original WG atlas files. If not custom set, then set them to the RelhaxTempfolder location if (string.IsNullOrEmpty(Atlas.TempAtlasImageFilePath)) { Atlas.TempAtlasImageFilePath = Path.Combine(Settings.RelhaxTempFolderPath, Atlas.AtlasFile); } if (string.IsNullOrEmpty(Atlas.TempAtlasMapFilePath)) { Atlas.TempAtlasMapFilePath = Path.Combine(Settings.RelhaxTempFolderPath, Atlas.MapFile); } //prepare the temp and output directories (lock to prevent multiple threads creating folders. Could get messy. lock (AtlasUtils.AtlasLoaderLockObject) { //create temp directory if it does not already exist if (!Directory.Exists(Path.GetDirectoryName(Atlas.TempAtlasImageFilePath))) { Directory.CreateDirectory(Path.GetDirectoryName(Atlas.TempAtlasImageFilePath)); } //create the save directory if it does not already exist if (!Directory.Exists(Atlas.AtlasSaveDirectory)) { Directory.CreateDirectory(Atlas.AtlasSaveDirectory); } } //delete the temp files if they exist if (File.Exists(Atlas.TempAtlasImageFilePath)) { File.Delete(Atlas.TempAtlasImageFilePath); } if (File.Exists(Atlas.TempAtlasMapFilePath)) { File.Delete(Atlas.TempAtlasMapFilePath); } stopwatch.Stop(); Logging.Info("[atlas file {0}]: Preparing to create atlas completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //extract the map and atlas files Logging.Info("[atlas file {0}]: Unpack of atlas and map starting", Atlas.AtlasFile); Logging.Debug("[atlas file {0}]: Atlas file unpack: pkg={1}, sourcePath={2}, dest={3}", Path.GetFileName(Atlas.AtlasFile), Atlas.Pkg, Path.Combine(Atlas.DirectoryInArchive, Atlas.AtlasFile), Atlas.TempAtlasImageFilePath); lock (AtlasUtils.AtlasLoaderLockObject) { FileUtils.Unpack(Atlas.Pkg, Path.Combine(Atlas.DirectoryInArchive, Atlas.AtlasFile), Atlas.TempAtlasImageFilePath); } OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); Logging.Debug("[atlas file {0}]: Map file unpack: pkg={1}, sourcePath={2}, dest={3}", Path.GetFileName(Atlas.AtlasFile), Atlas.Pkg, Path.Combine(Atlas.DirectoryInArchive, Atlas.MapFile), Atlas.TempAtlasMapFilePath); //because of the potential to use the same package for multiple threads, it's safer to do one at a time lock (AtlasUtils.AtlasLoaderLockObject) { FileUtils.Unpack(Atlas.Pkg, Path.Combine(Atlas.DirectoryInArchive, Atlas.MapFile), Atlas.TempAtlasMapFilePath); } OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); stopwatch.Stop(); Logging.Info("[atlas file {0}]: Unpack completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //parse the xml map file into the list of sub-textures Logging.Info("[atlas file {0}]: Parsing map file", Atlas.AtlasFile); Logging.Debug("[atlas file {0}]: Using map file path: {1}", Atlas.AtlasFile, Atlas.TempAtlasMapFilePath); Atlas.TextureList = mapHandler.LoadMapFile(Atlas.TempAtlasMapFilePath); OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); stopwatch.Stop(); Logging.Info("[atlas file {0}]: Parsing map completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //using the parsed size and location definitions from above, copy each individual sub-texture to the texture list Logging.Info("[atlas file {0}]: Parsing atlas to bitmap data", Atlas.AtlasFile); Logging.Debug("[atlas file {0}]: Using atlas file {1}", Atlas.AtlasFile, Atlas.TempAtlasImageFilePath); //the native library can only be used once at a time lock (AtlasUtils.AtlasLoaderLockObject) { atlasImage = imageHandler.LoadDDS(Atlas.TempAtlasImageFilePath); } OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); stopwatch.Stop(); Logging.Info("[atlas file {0}]: Parsing atlas completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Stop(); //if the max width and height weren't given, then use 1.2x width and height of the original Size originalAtlasSize = new Size(); originalAtlasSize = atlasImage.Size; if ((Atlas.AtlasHeight < 1) || (Atlas.AtlasWidth < 1)) { Logging.Debug("Atlas width and/or height were not provided, using a 1.2x multiplier instead"); Atlas.AtlasHeight = (int)(originalAtlasSize.Height * 1.2); Atlas.AtlasWidth = (int)(originalAtlasSize.Width * 1.2); } else if ((originalAtlasSize.Height * originalAtlasSize.Width) > (Atlas.AtlasWidth * Atlas.AtlasHeight)) { Logging.Warning("[atlas file {0}]: Max possible size is smaller then original size", Atlas.AtlasFile); Logging.Warning("Original h x w: {1} x {2}", originalAtlasSize.Height, originalAtlasSize.Width); Logging.Warning("Max possible h x w: {3} x {4}", Atlas.AtlasHeight, Atlas.AtlasWidth); Logging.Warning("Using a 1.2x multiplier instead"); Atlas.AtlasHeight = (int)(originalAtlasSize.Height * 1.2); Atlas.AtlasWidth = (int)(originalAtlasSize.Width * 1.2); } else { Logging.Debug("[atlas file {0}]: Max possible size of new atlas file-> {1} (h) x {2} (w)", Atlas.AtlasFile, Atlas.AtlasHeight, Atlas.AtlasWidth); } //copy the sub-texture bitmap data to each texture bitmap data stopwatch.Start(); Logging.Info("[atlas file {0}]: Parsing bitmap data", Atlas.AtlasFile); //get the overall size of the bitmap Rectangle rect = new Rectangle(0, 0, atlasImage.Width, atlasImage.Height); foreach (Texture texture in Atlas.TextureList) { Rectangle textureRect = new Rectangle(texture.X, texture.Y, texture.Width, texture.Height); //copy the texture bitmap data from the atlas bitmap into the texture bitmap //https://docs.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.clone?redirectedfrom=MSDN&view=netframework-4.8#System_Drawing_Bitmap_Clone_System_Drawing_Rectangle_System_Drawing_Imaging_PixelFormat texture.AtlasImage = atlasImage.Clone(textureRect, atlasImage.PixelFormat); //do a quick lock on the bits to ensure that the image data is deep copied. Clone() seems to only shallow copy //https://stackoverflow.com/a/13935966/3128017 BitmapData data = texture.AtlasImage.LockBits(new Rectangle(0, 0, texture.AtlasImage.Width, texture.AtlasImage.Height), ImageLockMode.ReadOnly, texture.AtlasImage.PixelFormat); texture.AtlasImage.UnlockBits(data); } //dispose of the original cause now we're done with it atlasImage.Dispose(); OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); stopwatch.Stop(); Logging.Info("[atlas file {0}]: Parsing bitmap data completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //wait for parsing of mod/custom images task here Logging.Info("[atlas file {0}]: Waiting for mod texture parse task", Atlas.AtlasFile); ParseCustomTexturesTask.Wait(); Logging.Info("[atlas file {0}]: Mod texture parse task complete, continue execution", Atlas.AtlasFile); OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); //check if any custom mod contour icons were parsed. if not, then there's no need to make a new one if (CustomContourIconImages.Count > 0) { Logging.Info("[atlas file {0}]: {1} custom icons parsed", Atlas.AtlasFile, CustomContourIconImages.Count); } else { Logging.Warning("[atlas file {0}]: 0 custom icons parsed for atlas file {1}, no need to make a custom atlas (is this the intent?)", CustomContourIconImages.Count, Atlas.AtlasFile); return(FailCode.None); } totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //replace the original atlas textures with the mod ones Logging.Info("[atlas file {0}]: Replacing stock WG images with custom/mod images", Atlas.AtlasFile); for (int i = 0; i < Atlas.TextureList.Count; i++) { Token.ThrowIfCancellationRequested(); Texture tex = Atlas.TextureList[i]; //get the matching texture, if it exists Texture[] originalResults = CustomContourIconImages.Where(texturee => texturee.Name.Equals(Atlas.TextureList[i].Name)).ToArray(); if (originalResults.Count() == 0) { continue; } Texture textureResult = originalResults[originalResults.Count() - 1]; //here means the count is one, replace the WG original subtexture with the mod one tex.AtlasImage.Dispose(); tex.AtlasImage = null; tex.AtlasImage = textureResult.AtlasImage; tex.X = 0; tex.Y = 0; tex.Height = textureResult.AtlasImage.Height; tex.Width = textureResult.AtlasImage.Width; } OnAtlasProgres?.Invoke(this, null); Logging.Info("[atlas file {0}]: Replacing stock WG images completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //run the atlas creator program Logging.Info("[atlas file {0}]: Atlas image packing starting", Atlas.AtlasFile); FailCode result = imagePacker.PackImage(Atlas.TextureList, Atlas.PowOf2, Atlas.Square, Atlas.FastImagePacker, Atlas.AtlasWidth, Atlas.AtlasHeight, #pragma warning disable IDE0068 // Use recommended dispose pattern Atlas.Padding, Atlas.AtlasFile, out Bitmap outputImage, out Dictionary <string, Rectangle> outputMap); #pragma warning restore IDE0068 // Use recommended dispose pattern OnAtlasProgres?.Invoke(this, null); if (result != 0) { Logging.Error("[atlas file {0}]: There was an error making the image sheet", Atlas.AtlasFile); return(result); } else { Logging.Info("[atlas file {0}]: Success packing into {1} x {2} pixel", Atlas.AtlasFile, outputImage.Height, outputImage.Width); } OnAtlasProgres?.Invoke(this, null); Token.ThrowIfCancellationRequested(); stopwatch.Stop(); Logging.Info("[atlas file {0}]: Atlas image packing completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //save it to the class for disposal outputAtlasImage = outputImage; //check if we're on a 32bit process. if we are and the atlas size is above the 2GB (estimated) limit, then return an error code. //honestly why are you on a 32bit system to begin with. it's 2020. like come on. if (!Environment.Is64BitProcess) { Logging.Warning("This is a 32bit process, need to check if the atlas file is too large to process"); int outputImageArea = outputImage.Width * outputImage.Height; if (outputImageArea > MAX_ATLAS_SIZE_32BIT) { Logging.Error("The output image is dimensions: W={0}, H={1}, Area={2}. Maximum area for processing on a 32bit system is {3} (W={4}, H={5}).", outputImage.Width, outputImage.Height, outputImageArea, MAX_ATLAS_SIZE_32BIT, 8000, 8000); return(FailCode.OutOfMemory32bit); } } totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //export the atlas image file Logging.Info("[atlas file {0}]: Atlas image creation starting", Atlas.AtlasFile); if (File.Exists(Atlas.AtlasImageFilePath)) { Logging.Info("[atlas file {0}]: File already exists before write, deleting", Atlas.AtlasFile); File.Delete(Atlas.AtlasImageFilePath); } if (!imageHandler.SaveDDS(Atlas.AtlasImageFilePath, outputImage)) { Logging.Error("[atlas file {0}]: Failed to create atlas image: {1}", Atlas.AtlasFile, Atlas.AtlasFile); return(FailCode.ImageExporter); } stopwatch.Stop(); Logging.Info("[atlas file {0}]: Atlas image creation completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Restart(); //export the atlas map file Logging.Info("[atlas file {0}]: Atlas map creation starting", Atlas.AtlasFile); if (File.Exists(Atlas.AtlasMapFilePath)) { File.Delete(Atlas.AtlasMapFilePath); } mapHandler.SaveMapfile(Atlas.AtlasMapFilePath, outputMap); stopwatch.Stop(); Logging.Info("[atlas file {0}]: Atlas map creation completed in {1} msec", Atlas.AtlasFile, stopwatch.ElapsedMilliseconds); totalMillisecondsToCreateImage += stopwatch.ElapsedMilliseconds; stopwatch.Stop(); //done Logging.Info("[atlas file {0}]: Creating atlas process completed in {1} msec", Atlas.AtlasFile, totalMillisecondsToCreateImage); return(FailCode.None); }
public static int Main(string[] args) { var app = new CommandLineApplication(false); var folderOption = app.Option("-f | --folder", "Specifies input folder", CommandOptionType.SingleValue); var outputOption = app.Option("-o | --output", "Specifies the output image's file name", CommandOptionType.SingleValue); var mapOption = app.Option("-m | --map", "Specifies the map's file name", CommandOptionType.SingleValue); var powTwoOption = app.Option("-2 | --pow2", "Forces that the output to have power of two dimensions", CommandOptionType.NoValue); var squareOption = app.Option("-s | --square", "Forces that the output to be have equal width and length", CommandOptionType.NoValue); var maxWidthOption = app.Option("-w | --maxwidth", "Specifies the maximum allowed output width", CommandOptionType.SingleValue); var maxHeightOption = app.Option("-h | --maxwidth", "Specifies the maximum allowed output height", CommandOptionType.SingleValue); var paddingOption = app.Option("-p | --padding", "Specifies the padding in pixel between packed subimages", CommandOptionType.SingleValue); app.HelpOption("-? | -h | --help"); app.OnExecute(() => { if (!folderOption.HasValue() || !outputOption.HasValue()) { Console.WriteLine("An input folder and an output filename are required"); return(1); } var inputDir = new DirectoryInfo(folderOption.Value()); var inputFiles = inputDir.GetFiles().Where(d => ImagePacker.SupportedImageExtensions.Contains(d.Extension.ToLower())).ToArray(); if (!inputFiles.Any()) { Console.WriteLine("No supported files found"); return(1); } var outFile = new FileInfo(outputOption.Value()); var outEncoder = ImagePacker.GetEncoderFromExtension(outFile.Extension); if (outEncoder == null) { Console.WriteLine("Unsupported output file format"); return(1); } var outMap = mapOption.HasValue() ? new FileInfo(mapOption.Value()) : null; IMapGenerator outGenerator = null; if (outMap != null) { outGenerator = MapGenerators.FirstOrDefault(d => d.MapExtension == outMap.Extension.ToLower()); if (outGenerator == null) { Console.WriteLine("Unsupported output map format"); return(1); } } var valueParsed = int.TryParse(maxWidthOption.Value(), out var maxWidth); if (!valueParsed) { maxWidth = DefaultSize; } valueParsed = int.TryParse(maxHeightOption.Value(), out var maxHeight); if (!valueParsed) { maxHeight = DefaultSize; } valueParsed = int.TryParse(paddingOption.Value(), out var padding); if (!valueParsed) { padding = DefaultPadding; } var packer = new ImagePacker(); packer.PackImage(inputFiles, powTwoOption.HasValue(), squareOption.HasValue(), maxWidth, maxHeight, padding, out var packedImage, out var packedMap); using (var outStream = outFile.Open(FileMode.Create)) { packedImage.Save(outStream, outEncoder); } if (outGenerator != null) { var mapBytes = outGenerator.Generate(packedMap); using (var outStream = outMap.Open(FileMode.Create)) { outStream.Write(mapBytes, 0, mapBytes.Length); } } return(0); }); return(app.Execute(args)); }
public void Test02_TextureTest() { Logfile log = UnitTestHelper.CreateLogfile(); Assert.IsNotNull(log); Assert.IsTrue(log.CanWrite); //create objects List <Texture> texturelist = null; XmlNodeList xmlTextureList = null; MapHandler mapHandler = new MapHandler(); ImagePacker imagePacker = new ImagePacker(); XmlDocument textureDocument = new XmlDocument(); //setup paths string testFileIn = Path.Combine(UnitTestHelper.ResourcesFolder, "battleAtlas.xml"); Assert.IsTrue(File.Exists(testFileIn)); string testFileOut = Path.Combine(UnitTestHelper.ResourcesFolder, "battleAtlas2.xml"); if (File.Exists(testFileOut)) { File.Delete(testFileOut); } //load sub-texture count to xml textureDocument.Load(testFileIn); xmlTextureList = textureDocument.SelectNodes("//SubTexture"); int numSubTextures = xmlTextureList.Count; //load texture list log.Write("Asserting to load a xml texture file to Texture list"); texturelist = mapHandler.LoadMapFile(testFileIn); log.Write(string.Format("Texture class load status: {0}", texturelist != null)); Assert.IsNotNull(texturelist); //compare texture count log.Write(string.Format("Xml node textures: {0}, parsed: {1}", numSubTextures, texturelist.Count)); Assert.AreEqual(numSubTextures, texturelist.Count); //for the packer, need to create bitmaps. we won't use them foreach (Texture tex in texturelist) { tex.AtlasImage = new Bitmap(1, 1); } //compare each individual texture log.Write("Asserting each texture matches from xml to texture list"); for (int i = 0; i < texturelist.Count; i++) { Texture texture = texturelist[i]; XmlElement xmlTexture = xmlTextureList[i] as XmlElement; //properties match lowercase xml properties names foreach (string propertyName in texture.PropertiesForSerializationElements()) { XmlElement element = xmlTexture.SelectSingleNode(propertyName.ToLower()) as XmlElement; Assert.IsNotNull(element); string xmlValue = element.InnerText.Trim(); //for reference //PropertyInfo property = listObjectType.GetProperty(attributeName); PropertyInfo property = typeof(Texture).GetProperty(propertyName); Assert.IsNotNull(property); object value = property.GetValue(texture); Assert.IsNotNull(value); Assert.AreEqual(xmlValue, value.ToString()); } } //pack textures FailCode code = imagePacker.PackImage(texturelist, true, false, true, 8192, 8192, 1, "battleAtlas.dds", out Bitmap map, out Dictionary <string, Rectangle> imageSizes); log.Write(string.Format("Packer fail code: {0}", code.ToString())); Assert.AreEqual(FailCode.None, code); //for the packer, need to dispose bitmaps map.Dispose(); foreach (Texture tex in texturelist) { tex.AtlasImage.Dispose(); } //save to new xml file mapHandler.SaveMapfile(testFileOut, imageSizes); Assert.IsTrue(File.Exists(testFileOut)); //compare texture count textureDocument = new XmlDocument(); textureDocument.Load(testFileOut); xmlTextureList = textureDocument.SelectNodes("//SubTexture"); numSubTextures = xmlTextureList.Count; log.Write(string.Format("Xml node textures: {0}, parsed: {1}", numSubTextures, imageSizes.Count)); Assert.AreEqual(numSubTextures, imageSizes.Count); //compare each individual texture log.Write("Asserting each texture matches from xml to dictionary"); for (int i = 0; i < xmlTextureList.Count; i++) { XmlElement xmlTexture = xmlTextureList[i] as XmlElement; Assert.IsNotNull(xmlTexture); string textureName = xmlTexture.SelectSingleNode(nameof(Texture.Name).ToLower()).InnerText.Trim(); Rectangle imageSize = imageSizes[textureName]; Assert.IsNotNull(imageSize); string[] propertiesToCheck = { nameof(imageSize.X), nameof(imageSize.Y), nameof(imageSize.Width), nameof(imageSize.Height) }; foreach (string propertyName in propertiesToCheck) { XmlElement xmlProperty = xmlTexture.SelectSingleNode(propertyName.ToLower()) as XmlElement; Assert.IsNotNull(xmlProperty); PropertyInfo property = imageSize.GetType().GetProperty(propertyName); Assert.IsNotNull(property); object propertyValue = property.GetValue(imageSize); Assert.IsNotNull(propertyValue); Assert.IsTrue(propertyValue is int); Assert.AreEqual(xmlProperty.InnerText.Trim(), propertyValue.ToString()); } } File.Delete(testFileOut); UnitTestHelper.DestroyLogfile(ref log, false); Assert.IsNull(log); }