static void Main(string[] args) { Console.WriteLine("TexturePacker - Package rect/non pow 2 textures into square power of 2 atlas"); if (args.Length == 0) { DisplayInfo(); return; } List<string> prms = args.ToList(); string sourcePath = ""; string searchPattern = ""; string outName = ""; int textureSize = 1024; int border = 0; bool debug = false; for (int ip = 0; ip < prms.Count; ++ip) { prms[ip] = prms[ip].ToLowerInvariant(); switch (prms[ip]) { case "-sp": case "--sourcepath": if (!prms[ip + 1].StartsWith("-")) { sourcePath = prms[ip + 1]; ++ip; } break; case "-ft": case "--filetype": if (!prms[ip + 1].StartsWith("-")) { searchPattern = prms[ip + 1]; ++ip; } break; case "-o": case "--output": if (!prms[ip + 1].StartsWith("-")) { outName = prms[ip + 1]; ++ip; } break; case "-s": case "--size": if (!prms[ip + 1].StartsWith("-")) { textureSize = int.Parse(prms[ip + 1]); ++ip; } break; case "-b": case "--border": if (!prms[ip + 1].StartsWith("-")) { border = int.Parse(prms[ip + 1]); ++ip; } break; case "-d": case "--debug": debug = true; break; } } if (sourcePath == "" || searchPattern == "" || outName == "") { DisplayInfo(); return; } else { Console.WriteLine("Processing, please wait"); } Packer packer = new Packer(); packer.Process(sourcePath, searchPattern, textureSize, border, debug); packer.SaveAtlasses(outName); }
static unsafe void Main(string[] args) { int ValidateAtlasSize(int atlassize) { if (atlassize > 4096) { atlassize = 4096; } else if (atlassize > 2048) { atlassize = 2048; } else if (atlassize > 1024) { atlassize = 1024; } else if (atlassize > 512) { atlassize = 512; } else if (atlassize > 256) { atlassize = 256; } else if (atlassize > 128) { atlassize = 128; } else { atlassize = 64; } return(atlassize); } int ValidatePaddingAmount(int Padding) { return(Math.Min(Math.Max(Padding, 0), 16)); } int AtlasSize = 4096; int PaddingBetweenImages = 1; bool OutputXML = false; bool OutputBinary = false; bool OutputJson = false; bool EnablePremultiply = false; bool EnableTrimming = false; bool VerboseOutput = false; bool ForcePack = false; bool CheckUnique = false; bool CheckRotate = false; if (args.Length < 4) { Console.WriteLine(@" TexturePacker - command line texture packer ==================================== usage: TexturePacker [OUTPUT] [INPUT1,INPUT2,INPUT3...] [OPTIONS...] example: TexturePacker bin/atlases/atlas assets/characters,assets/tiles -p -t -v -u -r options: -d --default use default settings (-x -p -t -u) -x --xml saves the atlas data as a .xml file -b --binary saves the atlas data as a .bin file -j --json saves the atlas data as a .json file -p --premultiply premultiplies the pixels of the bitmaps by their alpha channel -t --trim trims excess transparency off the bitmaps -v --verbose print to the debug console as the packer works -f --force ignore the hash, forcing the packer to repack -u --unique remove duplicate bitmaps from the atlas -r --rotate enabled rotating bitmaps 90 degrees clockwise when packing -s# --size# max atlas size (# can be 4096, 2048, 1024, 512, 256, 128, or 64) -p# --pad# padding between images (# can be from 0 to 16) binary format: [int16] num_textures (below block is repeated this many times) [byte] img_rotated [byte] img_trimmed [byte] img_premultiplied [string] name [int16] atlas_width [int16] atlas_height [int16] num_images (below block is repeated this many times) [string] img_name [int16] img_x [int16] img_y [int16] img_width [int16] img_height [int16] img_frame_x (if --trim enabled) [int16] img_frame_y (if --trim enabled) [int16] img_frame_width (if --trim enabled) [int16] img_frame_height (if --trim enabled) "); } else { var OutputFileInfo = new FileInfo(args[0]); var InputDirectories = args[1].Split(',').Select(x => new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), x))).ToList(); for (int i = 2; i < args.Length; i++) { string arg = args[i]; if (arg == "-d" || arg == "--default") { OutputXML = EnablePremultiply = EnableTrimming = CheckUnique = true; } else if (arg == "-x" || arg == "--xml") { OutputXML = true; } else if (arg == "-b" || arg == "--binary") { OutputBinary = true; } else if (arg == "-j" || arg == "--json") { OutputJson = true; } else if (arg == "-p" || arg == "--premultiply") { EnablePremultiply = true; } else if (arg == "-t" || arg == "--trim") { EnableTrimming = true; } else if (arg == "-v" || arg == "--verbose") { VerboseOutput = true; } else if (arg == "-f" || arg == "--force") { ForcePack = true; } else if (arg == "-u" || arg == "--unique") { CheckUnique = true; } else if (arg == "-r" || arg == "--rotate") { CheckRotate = true; } else if (arg.Contains("--size")) { AtlasSize = ValidateAtlasSize(int.Parse(arg.Substring("--size".Length))); } else if (arg.Contains("-s")) { AtlasSize = ValidateAtlasSize(int.Parse(arg.Substring("-s".Length))); } else if (arg.Contains("--pad")) { PaddingBetweenImages = ValidatePaddingAmount(int.Parse(arg.Substring("--pad".Length))); } else if (arg.Contains("-p")) { PaddingBetweenImages = ValidatePaddingAmount(int.Parse(arg.Substring("-p".Length))); } else { Console.WriteLine($"unexpected argument: {arg}"); } } int NewHash = 0; for (int i = 0; i < args.Length; i++) { NewHash += args[i].GetHashCode(); } for (int i = 0; i < InputDirectories.Count; i++) { var Directory = InputDirectories[i]; foreach (var texfile in Directory.GetFiles("*.png")) { NewHash += texfile.GetHashCode(); } } bool AllowPack = ForcePack; if (OutputFileInfo.Directory.Exists) { if (File.Exists($"{OutputFileInfo.FullName}.hash") && !ForcePack) { int oldhash = int.Parse(File.ReadAllText($"{OutputFileInfo.FullName}.hash")); if (oldhash != NewHash) { File.WriteAllText($"{OutputFileInfo.FullName}.hash", NewHash.ToString()); AllowPack = true; } } else { File.WriteAllText($"{OutputFileInfo.FullName}.hash", NewHash.ToString()); AllowPack = true; } } else { OutputFileInfo.Directory.Create(); File.WriteAllText($"{OutputFileInfo.FullName}.hash", NewHash.ToString()); } if (AllowPack) { if (VerboseOutput) { Console.WriteLine("Reading all pngs "); } List <PackerBitmap> Bitmaps = new List <PackerBitmap>(); List <Packer> Packers = new List <Packer>(); foreach (var Directory in InputDirectories) { foreach (var texfile in Directory.GetFiles("*.png")) { var texture = new TexHandle(); TextureLoadUtil.LoadTexture(texfile.FullName, ref texture); Bitmaps.Add(new PackerBitmap(texture, texfile.Name, EnablePremultiply, EnableTrimming)); } } Bitmaps.Sort(); while (Bitmaps.Count > 0) { if (VerboseOutput) { Console.WriteLine("packing " + Bitmaps.Count + " images..."); } var packer = new Packer(AtlasSize, AtlasSize, PaddingBetweenImages); packer.Pack(Bitmaps, VerboseOutput, CheckUnique, CheckRotate); Packers.Add(packer); if (VerboseOutput) { Console.WriteLine("finished packing: " + Packers.Count + " (" + packer.Width + " x " + packer.Height + ')'); } if (packer.Bitmaps.Count <= 0) { Console.WriteLine("packing failed, could not fit any bitmap "); return; } } var OutputAtlasData = new List <Atlas>(); for (int i = 0; i < Packers.Count; i++) { var OutAtlas = new Atlas(); OutAtlas.Name = OutputFileInfo.Name + i + ".png"; OutAtlas.Width = Packers[i].Width; OutAtlas.Height = Packers[i].Height; OutAtlas.IsRotated = CheckRotate; OutAtlas.IsTrimmed = EnableTrimming; OutAtlas.IsPremultiplied = EnablePremultiply; OutAtlas.Images = new List <AtlasImage>(); for (int t = 0; t < Packers[i].Bitmaps.Count; t++) { var Image = new AtlasImage(); Image.Name = Packers[i].Bitmaps[t].Name.Remove(Packers[i].Bitmaps[t].Name.Length - ".png".Length); Image.X = Packers[i].Points[t].x; Image.Y = Packers[i].Points[t].y; Image.Width = Packers[i].Bitmaps[t].Width; Image.Height = Packers[i].Bitmaps[t].Height; Image.FrameX = Packers[i].Bitmaps[t].FrameX; Image.FrameY = Packers[i].Bitmaps[t].FrameY; Image.FrameW = Packers[i].Bitmaps[t].FrameW; Image.FrameH = Packers[i].Bitmaps[t].FrameH; OutAtlas.Images.Add(Image); } OutputAtlasData.Add(OutAtlas); } for (int i = 0; i < Packers.Count; ++i) { if (VerboseOutput) { Console.WriteLine("writing png: " + OutputFileInfo.Name + i + ".png"); } Packers[i].SavePng(OutputFileInfo.FullName + i + ".png"); } if (OutputBinary) { if (VerboseOutput) { Console.WriteLine("Saving binary: " + OutputFileInfo.Name + ".bin"); } var FStream = File.OpenWrite(OutputFileInfo.FullName + ".bin"); using (var stringwriter = new BinaryWriter(FStream, Encoding.ASCII)) { stringwriter.Write((ushort)OutputAtlasData.Count); stringwriter.Write((byte)(CheckRotate ? 1 : 0)); stringwriter.Write((byte)(EnableTrimming ? 1 : 0)); stringwriter.Write((byte)(EnablePremultiply ? 1 : 0)); for (int i = 0; i < OutputAtlasData.Count; ++i) { stringwriter.Write(OutputAtlasData[i].Name); stringwriter.Write((ushort)OutputAtlasData[i].Width); stringwriter.Write((ushort)OutputAtlasData[i].Height); stringwriter.Write((ushort)OutputAtlasData[i].Images.Count); for (int t = 0; t < OutputAtlasData[i].Images.Count; t++) { stringwriter.Write(OutputAtlasData[i].Images[t].Name); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].X); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].Y); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].Width); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].Height); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].FrameX); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].FrameY); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].FrameW); stringwriter.Write((ushort)OutputAtlasData[i].Images[t].FrameH); } } } FStream.Close(); } if (OutputXML) { if (VerboseOutput) { Console.WriteLine("Saving xml: " + OutputFileInfo.Name + ".xml"); } using (var stringwriter = new StringWriter()) { var serializer = new XmlSerializer(OutputAtlasData.GetType()); serializer.Serialize(stringwriter, OutputAtlasData); File.WriteAllText(OutputFileInfo.FullName + ".xml", stringwriter.ToString()); } } if (OutputJson) { if (VerboseOutput) { Console.WriteLine("Saving json: " + OutputFileInfo.Name + ".json"); } File.WriteAllText(OutputFileInfo.FullName + ".json", JsonConvert.SerializeObject(OutputAtlasData, Newtonsoft.Json.Formatting.Indented)); } } else { Console.WriteLine("Atlas is unchanged"); } } }