/// <summary> /// Exports the given animations into a BundleSheetExport and returns the created sheet /// </summary> /// <param name="settings">The export settings for the sheet</param> /// <param name="anims">The list of animations to export</param> /// <param name="cancellationToken">A cancelation token that is passed to the exporters and can be used to cancel the export process mid-way</param> /// <param name="progressHandler">Optional event handler for reporting the export progress</param> /// <returns>A BundleSheetExport representing the animations passed ready to be saved to disk</returns> public async Task <BundleSheetExport> ExportBundleSheet(AnimationExportSettings settings, Animation[] anims, CancellationToken cancellationToken = new CancellationToken(), BundleExportProgressEventHandler progressHandler = null) { using (var atlas = await GenerateAtlasFromAnimations(settings, anims, "", cancellationToken, progressHandler)) { return(BundleSheetExport.FromAtlas(atlas)); } }
/// <summary> /// Loads an AnimationSheet from the given stream, using the specified version /// number when reading properties /// </summary> /// <param name="stream">The stream to load the animation sheet from</param> /// <param name="parentBundle">The bundle that will contain this AnimationSheet</param> /// <param name="version">The version that the stream was written on</param> /// <returns>The Animation object loaded</returns> public static AnimationSheet LoadAnimationSheetFromStream(Stream stream, Bundle parentBundle, int version) { BinaryReader reader = new BinaryReader(stream); // Load the animation sheet data int id = reader.ReadInt32(); string name = reader.ReadString(); AnimationExportSettings settings = LoadExportSettingsFromStream(stream, version); // Create the animation sheet AnimationSheet sheet = new AnimationSheet(name) { ID = id, ExportSettings = settings }; // Load the animation indices int animCount = reader.ReadInt32(); for (int i = 0; i < animCount; i++) { Animation anim = parentBundle.GetAnimationByID(reader.ReadInt32()); if (anim != null) { sheet.AddAnimation(anim); } } return(sheet); }
/// <summary> /// Loads an AnimationSheet from the given stream, using the specified version /// number when reading properties /// </summary> /// <param name="stream">The stream to load the animation sheet from</param> /// <returns>The Animation object loaded</returns> protected AnimationSheet LoadAnimationSheetFromStream(Stream stream) { BinaryReader reader = new BinaryReader(stream); // Load the animation sheet data int id = reader.ReadInt32(); string name = reader.ReadString(); AnimationExportSettings settings = LoadExportSettingsFromStream(stream); // Create the animation sheet AnimationSheet sheet = new AnimationSheet(name) { ID = id, ExportSettings = settings }; // Load the animation indices int animCount = reader.ReadInt32(); for (int i = 0; i < animCount; i++) { Animation anim = owningFile.LoadedBundle.GetAnimationByID(reader.ReadInt32()); if (anim != null) { sheet.AddAnimation(anim); } else { throw new Exception(@"The animation referenced by an animation sheet stored is invalid. This may be due to a corrupted file."); } } return(sheet); }
/// <summary> /// Internal atlas packer method, tailored to work with individual rectangle frames /// </summary> /// <param name="exportSettings">The export settings to use when packing the rectangles</param> /// <param name="rectangles">The list of frame rectangles to try to pack</param> /// <param name="atlasWidth">An output atlas width uint</param> /// <param name="atlasHeight">At output atlas height uint</param> /// <param name="maxWidth">The maximum width the generated sheet can have</param> /// <returns>An array of rectangles, where each index matches the original passed Rectangle array, and marks the final computed bounds of the rectangle frames calculated</returns> private Rectangle[] InternalPack(AnimationExportSettings exportSettings, Rectangle[] rectangles, ref uint atlasWidth, ref uint atlasHeight, int maxWidth) { // Cache some fields as locals int x = exportSettings.XPadding; var boundsFinal = new Rectangle[rectangles.Length]; for (int i = 0; i < rectangles.Length; i++) { var width = rectangles[i].Width; var height = rectangles[i].Height; // X coordinate wrapping if (x + width > maxWidth) { x = exportSettings.XPadding; } var y = exportSettings.YPadding; // Do a little trickery to find the minimum Y for this frame if (x - exportSettings.XPadding < atlasWidth) { // Intersect the current frame rectangle with all rectangles above it, and find the maximum bottom Y coordinate between all the intersections int contactRectX = x - exportSettings.XPadding; int contactRectWidth = width + exportSettings.XPadding * 2; for (int j = 0; j < i; j++) { Rectangle rect = boundsFinal[j]; if (rect.X < (contactRectX + contactRectWidth) && contactRectX < (rect.X + rect.Width)) { y = Math.Max(y, rect.Y + rect.Height + exportSettings.YPadding); } } } //// //// 3. Calculate frame area on sheet //// boundsFinal[i] = new Rectangle(x, y, width, height); atlasWidth = (uint)Math.Max(atlasWidth, boundsFinal[i].X + boundsFinal[i].Width + exportSettings.XPadding); atlasHeight = (uint)Math.Max(atlasHeight, boundsFinal[i].Y + boundsFinal[i].Height + exportSettings.YPadding); // X coordinate update x += boundsFinal[i].Width + exportSettings.XPadding; if (x > maxWidth) // Jump to next line, at left-most corner { x = exportSettings.XPadding; } } return(boundsFinal); }
/// <summary> /// Creates a new TextureAtlas, preparing the atlas to export using the given export settings /// </summary> /// <param name="settings">The export settings to use when packing the frames</param> /// <param name="name">An optional name for the TextureAtlas to be used on progress report</param> public TextureAtlas(AnimationExportSettings settings, string name = "") { _animationList = new List <Animation>(); FrameList = new List <IFrame>(); BoundsList = new List <Rectangle>(); OriginsList = new List <Rectangle>(); ReuseCount = new List <int>(); ExportSettings = settings; Information = new TextureAtlasInformation(); Name = name; }
/// <summary> /// Saves the given AnimationExportSettings into a stream /// </summary> /// <param name="settings">The export settings to write to the stream</param> /// <param name="stream">The stream to write the animation sheet to</param> protected void SaveExportSettingsToStream(AnimationExportSettings settings, Stream stream) { BinaryWriter writer = new BinaryWriter(stream); writer.Write(settings.FavorRatioOverArea); writer.Write(settings.ForcePowerOfTwoDimensions); writer.Write(settings.ForceMinimumDimensions); writer.Write(settings.ReuseIdenticalFramesArea); writer.Write(settings.HighPrecisionAreaMatching); writer.Write(settings.AllowUnorderedFrames); writer.Write(settings.UseUniformGrid); writer.Write(settings.UsePaddingOnJson); writer.Write(settings.ExportJson); writer.Write(settings.XPadding); writer.Write(settings.YPadding); }
/// <summary> /// Tests a sheet export procedure with a given export settings struct /// </summary> /// <param name="settings">The export settings to use in this test</param> /// <param name="failMessage">The message to print if the test fails</param> private void TestSheetExportWithSettings(AnimationExportSettings settings, string failMessage = "Exported animation sheets should be equivalent to their original sheets") { // In theory, if you export a sheet and import it back just the way it was described on the generated json file, it will equal the original sheet completely OriginalSheet = new AnimationSheet("Sheet1") { ExportSettings = settings }; for (int i = 0; i < 10; i++) { int animationWidth = 12 + i / 2; int animationHeight = 12 + i / 2; OriginalSheet.AddAnimation(AnimationGenerator.GenerateAnimation("Anim" + OriginalSheet.AnimationCount, animationWidth, animationHeight, 10, OriginalSheet.AnimationCount * 2)); } // Generate export path _tempExportPath = Path.GetTempPath() + Path.DirectorySeparatorChar + Path.GetRandomFileName(); Directory.CreateDirectory(_tempExportPath); string exportPath = _tempExportPath + Path.DirectorySeparatorChar + OriginalSheet.Name; string jsonPath = exportPath + ".json"; // Export and save to disk IBundleExporter exporter = new DefaultPngExporter(); exporter.ExportBundleSheet(OriginalSheet.ExportSettings, OriginalSheet.Animations).Result .SaveToDisk(_tempExportPath + Path.DirectorySeparatorChar + OriginalSheet.Name); // Export sheet temporarely for (int i = 0; i < OriginalSheet.Animations.Length; i++) { var animation = OriginalSheet.Animations[i]; var image = exporter.GenerateSpriteStrip(animation); var path = _tempExportPath + Path.DirectorySeparatorChar + OriginalSheet.Name + "_" + i + ".png"; image.Save(path, ImageFormat.Png); } // Import it back up SheetFromDisk = (AnimationSheet)ImportSheetFile(jsonPath); SheetFromDisk.ExportSettings = OriginalSheet.ExportSettings; Assert.AreEqual(OriginalSheet, SheetFromDisk, failMessage); }
/// <summary> /// Initializes a new instance of the AnimationSheetEditor class /// </summary> /// <param name="controller">The controller that owns this form</param> /// <param name="sheetToEdit">The sheet to edit on this form. Leave null to show an interface to create a new sheet</param> public AnimationSheetView(Controller controller, AnimationSheet sheetToEdit = null) { InitializeComponent(); zpb_sheetPreview.HookToControl(this); _controller = controller; _sheetToEdit = sheetToEdit; if (sheetToEdit != null) { _exportSettings = sheetToEdit.ExportSettings; } InitializeFiends(); ValidateFields(); }
/// <summary> /// Loads an AnimationExportSettings from the given stream, using the specified /// version number when reading properties /// </summary> /// <param name="stream">The stream to load the export settings from</param> /// <returns>The AnimationExportSettings object loaded</returns> protected AnimationExportSettings LoadExportSettingsFromStream(Stream stream) { BinaryReader reader = new BinaryReader(stream); AnimationExportSettings settings = new AnimationExportSettings { FavorRatioOverArea = reader.ReadBoolean(), ForcePowerOfTwoDimensions = reader.ReadBoolean(), ForceMinimumDimensions = reader.ReadBoolean(), ReuseIdenticalFramesArea = reader.ReadBoolean(), HighPrecisionAreaMatching = reader.ReadBoolean(), AllowUnorderedFrames = reader.ReadBoolean(), UseUniformGrid = reader.ReadBoolean(), UsePaddingOnJson = reader.ReadBoolean(), ExportJson = reader.ReadBoolean(), XPadding = reader.ReadInt32(), YPadding = reader.ReadInt32() }; return(settings); }
/// <summary> /// Exports the given animations into an image sheet and returns the created sheet /// </summary> /// <param name="exportSettings">The export settings for the sheet</param> /// <param name="anims">The list of animations to export</param> /// <param name="name">The name for the generated texture atlas. Used for progress reports</param> /// <param name="cancellationToken">A cancelation token that is passed to the exporters and can be used to cancel the export process mid-way</param> /// <param name="progressHandler">Optional event handler for reporting the export progress</param> /// <returns>An image sheet representing the animations passed</returns> public async Task <TextureAtlas> GenerateAtlasFromAnimations(AnimationExportSettings exportSettings, Animation[] anims, string name = "", CancellationToken cancellationToken = new CancellationToken(), BundleExportProgressEventHandler progressHandler = null) { var atlas = new TextureAtlas(exportSettings, name); // // 1. Add the frames to the texture atlas // foreach (var anim in anims) { for (int i = 0; i < anim.FrameCount; i++) { atlas.InsertFrame(anim.GetFrameAtIndex(i)); } } // // 2. Pack the frames into the atlas // ITexturePacker packer = new DefaultTexturePacker(); await packer.Pack(atlas, cancellationToken, progressHandler); return(atlas); }
/// <summary> /// Loads an AnimationExportSettings from the given stream, using the specified /// version number when reading properties /// </summary> /// <param name="stream">The stream to load the export settings from</param> /// <param name="version">The version that the stream was writter on</param> /// <returns>The AnimationExportSettings object loaded</returns> public static AnimationExportSettings LoadExportSettingsFromStream(Stream stream, int version) { BinaryReader reader = new BinaryReader(stream); AnimationExportSettings settings = new AnimationExportSettings { FavorRatioOverArea = reader.ReadBoolean(), ForcePowerOfTwoDimensions = reader.ReadBoolean(), ForceMinimumDimensions = reader.ReadBoolean(), ReuseIdenticalFramesArea = reader.ReadBoolean(), // >= Version 4 HighPrecisionAreaMatching = version >= 4 && reader.ReadBoolean(), AllowUnorderedFrames = reader.ReadBoolean(), // >= Version 7 UseUniformGrid = version >= 7 && reader.ReadBoolean(), UsePaddingOnJson = reader.ReadBoolean(), // >= Version 5 ExportJson = version < 5 || reader.ReadBoolean(), XPadding = reader.ReadInt32(), YPadding = reader.ReadInt32() }; return(settings); }
/// <summary> /// Generates a BundleSheetExport object that contains information about the export of a sheet, using a custom event handler /// for export progress callback /// </summary> /// <param name="exportSettings">The export settings for the sheet</param> /// <param name="cancellationToken">A cancelation token that can be used to cancel the process mid-way</param> /// <param name="callback">The callback delegate to be used during the generation process</param> /// <param name="anims">The list of animations to export</param> /// <returns>A BundleSheetExport object that contains information about the export of the sheet</returns> public Task <BundleSheetExport> GenerateBundleSheet(AnimationExportSettings exportSettings, CancellationToken cancellationToken, BundleExportProgressEventHandler callback, params Animation[] anims) { return(GetExporter().ExportBundleSheet(exportSettings, anims, cancellationToken, callback)); }
/// <summary> /// Generates a BundleSheetExport object that contains information about the export of a sheet /// </summary> /// <param name="exportSettings">The export settings for the sheet</param> /// <param name="anims">The list of animations to export</param> /// <returns>A BundleSheetExport object that contains information about the export of the sheet</returns> public Task <BundleSheetExport> GenerateBundleSheet(AnimationExportSettings exportSettings, params Animation[] anims) { return(GetExporter().ExportBundleSheet(exportSettings, anims)); }