Exemplo n.º 1
0
        /// <summary>
        /// Exports a given Bundle asynchronously, calling a progress handler along the way
        /// </summary>
        /// <param name="bundle">The bundle 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 task representing the concurrent export progress</returns>
        public async Task ExportBundleConcurrent(Bundle bundle, CancellationToken cancellationToken = new CancellationToken(), BundleExportProgressEventHandler progressHandler = null)
        {
            // Start with initial values for the progress export of every sheet
            float[] stageProgresses = new float[bundle.AnimationSheets.Count];
            var     exports         = new List <BundleSheetJson>();

            var progressAction = new Action <AnimationSheet>(sheet =>
            {
                if (progressHandler == null)
                {
                    return;
                }

                // Calculate total progress
                int total = (int)Math.Floor(stageProgresses.Sum() / stageProgresses.Length * 100);
                progressHandler(new SheetGenerationBundleExportProgressEventArgs(sheet, BundleExportStage.TextureAtlasGeneration, total, total / 2, "Generating sheets"));
            });

            var generationList = new List <Task>();

            for (int i = 0; i < bundle.AnimationSheets.Count; i++)
            {
                var sheet = bundle.AnimationSheets[i];
                var j     = i;
                generationList.Add(new Task(() =>
                {
                    var exp = ExportBundleSheet(sheet, cancellationToken, args =>
                    {
                        stageProgresses[j] = (float)args.StageProgress / 100;
                        progressAction(sheet);
                    });

                    try
                    {
                        var sheetJson = new BundleSheetJson(exp.Result, sheet.Name, Path.GetFullPath(bundle.ExportPath) + "\\" + sheet.Name);
                        exports.Add(sheetJson);
                    }
                    catch (TaskCanceledException)
                    {
                        // unused
                    }
                }));
            }

            var concurrent = new Task(() => { StartAndWaitAllThrottled(generationList, 7, cancellationToken); });

            concurrent.Start();

            await concurrent;

            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }

            //
            // 2. Save the sheets to disk
            //
            for (int i = 0; i < exports.Count; i++)
            {
                var exp = exports[i];

                if (progressHandler != null)
                {
                    int progress = (int)((float)i / exports.Count * 100);
                    progressHandler.Invoke(new BundleExportProgressEventArgs(BundleExportStage.SavingToDisk, progress, 50 + progress / 2));
                }

                exp.BundleSheet.SaveToDisk(exp.ExportPath);
            }

            //
            // 3. Compose the main bundle .json
            //

            if (exports.Any(export => export.BundleSheet.ExportSettings.ExportJson))
            {
                var jsonSheetList = new List <Dictionary <string, object> >();

                foreach (var export in exports)
                {
                    if (!export.BundleSheet.ExportSettings.ExportJson)
                    {
                        continue;
                    }

                    var sheet = new Dictionary <string, object>();

                    // Path of final JSON file
                    var filePath = Utilities.GetRelativePath(Path.ChangeExtension(export.ExportPath, "json"),
                                                             bundle.ExportPath);

                    sheet["name"]        = export.SheetName;
                    sheet["sprite_file"] = filePath;

                    // Export animation list
                    var names = export.BundleSheet.Animations.Select(anim => anim.Name);

                    sheet["animations"] = names;

                    jsonSheetList.Add(sheet);
                }

                var json = new Dictionary <string, object>
                {
                    ["sheets"] = jsonSheetList
                };

                string finalPath = Path.ChangeExtension(Path.GetFullPath(bundle.ExportPath) + "\\" + bundle.Name, "json");
                string output    = JsonConvert.SerializeObject(json);

                File.WriteAllText(finalPath, output, Encoding.UTF8);
            }

            progressHandler?.Invoke(new BundleExportProgressEventArgs(BundleExportStage.Ended, 100, 100));
        }
Exemplo n.º 2
0
 /// <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));
 }
Exemplo n.º 3
0
        /// <summary>
        /// Generates a TextureAtlas from the given AnimationSheet object
        /// </summary>
        /// <param name="sheet">The AnimationSheet to generate the TextureAtlas of</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 TextureAtlas generated from the given AnimationSheet</returns>
        public async Task <TextureAtlas> GenerateAtlasFromAnimationSheet(AnimationSheet sheet, CancellationToken cancellationToken = new CancellationToken(), BundleExportProgressEventHandler progressHandler = null)
        {
            return(await GenerateAtlasFromAnimations(sheet.ExportSettings, sheet.Animations, sheet.Name, cancellationToken, args =>
            {
                progressHandler?.Invoke(args);

                _sheetProgress[sheet.ID] = (float)args.StageProgress / 100;
            }));
        }
Exemplo n.º 4
0
        /// <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);
        }
Exemplo n.º 5
0
 /// <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));
     }
 }
Exemplo n.º 6
0
 /// <summary>
 /// Exports the given animations into a BundleSheetExport and returns the created sheet
 /// </summary>
 /// <param name="sheet">The sheet 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 animation sheet passed ready to be saved to disk</returns>
 public async Task <BundleSheetExport> ExportBundleSheet(AnimationSheet sheet, CancellationToken cancellationToken = new CancellationToken(), BundleExportProgressEventHandler progressHandler = null)
 {
     //
     // 1. Generate texture atlas
     //
     using (var atlas = await GenerateAtlasFromAnimationSheet(sheet, cancellationToken, progressHandler))
     {
         //
         // 2. Generate an export sheet from the texture atlas
         //
         return(BundleSheetExport.FromAtlas(atlas));
     }
 }
Exemplo n.º 7
0
        /// <summary>
        /// Packs a given atlas with a specified progress event handler
        /// </summary>
        /// <param name="atlas">The texture atlas to pack</param>
        /// <param name="cancellationToken">A cancellation token that can be used to abort this task</param>
        /// <param name="handler">The event handler for the packing process</param>
        public async Task Pack(TextureAtlas atlas, CancellationToken cancellationToken, BundleExportProgressEventHandler handler = null)
        {
            if (atlas.FrameCount == 0)
            {
                atlas.AtlasRectangle = new Rectangle(0, 0, 1, 1);
                return;
            }

            // Cache some fields as locals
            List <IFrame> frameList = atlas.FrameList;

            _progressHandler  = handler;
            _frameComparision = new FrameComparision(atlas.ExportSettings.ForceMinimumDimensions);

            // 1. (Optional) Sort the frames from largest to smallest before packing
            if (atlas.ExportSettings.AllowUnorderedFrames)
            {
                // Use a stable sort
                frameList.AddRange(frameList.OrderBy(frame => frame, _frameComparision).ToList());
                frameList.RemoveRange(0, frameList.Count / 2);
            }

            // 2. (Optional) Find identical frames and pack them to use the same sheet area
            if (atlas.ExportSettings.ReuseIdenticalFramesArea)
            {
                MarkIdenticalFramesFromList(frameList);
            }

            // 3. Find the maximum possible horizontal sheet size
            CalculateMaximumSizes(frameList);

            // 4. Iterate through possible widths and match the smallest area to use as a maxWidth
            uint atlasWidth;
            uint atlasHeight;

            var frameBoundsMap = PrepareAtlas(atlas, atlas.ExportSettings.UseUniformGrid ? _maxFrameWidth : -1, atlas.ExportSettings.UseUniformGrid ? _maxFrameHeight : -1);

            Rectangle[] frameBounds = frameBoundsMap.SheetBounds;

            var minAreaTask = new Task <int>(() => IterateAtlasSize(atlas, frameBounds, _maxWidthCapped, out atlasWidth, out atlasHeight, cancellationToken), cancellationToken);

            minAreaTask.Start();

            int minAreaWidth = await minAreaTask;

            atlasWidth  = 0;
            atlasHeight = 0;

            // 5. Pack the texture atlas
            Rectangle[] finalFrameRegions = InternalPack(atlas.ExportSettings, frameBounds, ref atlasWidth, ref atlasHeight, minAreaWidth);

            // Replace bounds now
            frameBoundsMap.ReplaceSheetBounds(finalFrameRegions);

            // Unwrap on atlas now
            //atlas.BoundsList
            for (int i = 0; i < atlas.FrameList.Count; i++)
            {
                var frame       = atlas.FrameList[i];
                var sheetBounds = frameBoundsMap.GetSheetBoundsForFrame(frame);

                if (sheetBounds != null)
                {
                    atlas.BoundsList[i] = sheetBounds.Value;
                }

                var localBounds = frameBoundsMap.GetLocalBoundsForFrame(frame);

                if (localBounds != null)
                {
                    atlas.OriginsList[i] = localBounds.Value;
                }
            }

            // Round up to the closest power of two
            if (atlas.ExportSettings.ForcePowerOfTwoDimensions)
            {
                atlasWidth  = Utilities.SnapToNextPowerOfTwo(atlasWidth);
                atlasHeight = Utilities.SnapToNextPowerOfTwo(atlasHeight);
            }

            if (atlasWidth == 0)
            {
                atlasWidth = 1;
            }
            if (atlasHeight == 0)
            {
                atlasHeight = 1;
            }

            atlas.AtlasRectangle = new Rectangle(0, 0, (int)atlasWidth, (int)atlasHeight);

            // Assign the information on the texture atlas
            atlas.Information.ReusedFrameOriginsCount = _frameComparision.CachedSimilarCount;

            // Register reusal in atlas
            atlas.ReuseCount.Clear();

            var simMatrix = _frameComparision.SimilarMatrixIndexDictionary;

            foreach (IFrame frame in atlas.FrameList)
            {
                if (atlas.ExportSettings.ReuseIdenticalFramesArea)
                {
                    int repCount = 0;

                    int index;
                    if (simMatrix.TryGetValue(frame.ID, out index))
                    {
                        repCount = _frameComparision.SimilarFramesMatrix[index].Count - 1;
                    }

                    atlas.ReuseCount.Add(repCount);
                }
                else
                {
                    atlas.ReuseCount.Add(0);
                }
            }
        }
Exemplo n.º 8
0
 /// <summary>
 /// Packs a given atlas with a specified progress event handler
 /// </summary>
 /// <param name="atlas">The texture atlas to pack</param>
 /// <param name="handler">The event handler for the packing process</param>
 public void Pack(TextureAtlas atlas, BundleExportProgressEventHandler handler = null)
 {
     Pack(atlas, new CancellationToken(), handler).Wait();
 }
Exemplo n.º 9
0
        /// <summary>
        /// Generates a preview for the AnimationSheet currently loaded into this form
        /// </summary>
        public void GeneratePreview()
        {
            RepopulateExportSettings();
            UpdateCountLabels();

            if (_sheetToEdit.Animations.Length <= 0)
            {
                lbl_alertLabel.Text    = AnimationMessages.TextNoAnimationInSheetToGeneratePreview;
                pnl_alertPanel.Visible = true;
                return;
            }

            // Time the bundle export
            pb_exportProgress.Visible = true;

            BundleExportProgressEventHandler handler = args =>
            {
                Invoke(new Action(() =>
                {
                    pb_exportProgress.Value = args.StageProgress;
                }));
            };

            var form = FindForm();

            if (form != null)
            {
                form.Cursor = Cursors.WaitCursor;
            }

            btn_generatePreview.Enabled = false;

            var sw = Stopwatch.StartNew();

            _sheetCancellation = new CancellationTokenSource();

            // Export the bundle
            var t = _controller.GenerateBundleSheet(_exportSettings, _sheetCancellation.Token, handler, _sheetToEdit.Animations);

            t.ContinueWith(task =>
            {
                Invoke(new Action(() =>
                {
                    btn_generatePreview.Enabled = true;

                    // Dispose of current preview
                    RemovePreview();

                    if (_sheetCancellation.IsCancellationRequested)
                    {
                        _sheetCancellation = null;
                        Close();
                        return;
                    }

                    _sheetCancellation = null;

                    _bundleSheetExport = task.Result;

                    Image img = _bundleSheetExport.Sheet;

                    sw.Stop();

                    if (form != null)
                    {
                        form.Cursor = Cursors.Default;
                    }

                    zpb_sheetPreview.SetImage(img);

                    pb_exportProgress.Visible = false;

                    // Update labels
                    lbl_sheetPreview.Text = AnimationMessages.TextSheetPreviewGenerated + sw.ElapsedMilliseconds + @"ms)";

                    lbl_dimensions.Text    = img.Width + @"x" + img.Height;
                    lbl_pixelCount.Text    = (img.Width * img.Height).ToString("N0");
                    lbl_framesOnSheet.Text = (_bundleSheetExport.FrameCount - _bundleSheetExport.ReusedFrameCount) + "";
                    lbl_reusedFrames.Text  = (_bundleSheetExport.ReusedFrameCount) + "";
                    lbl_memoryUsage.Text   = Utilities.FormatByteSize(ImageUtilities.MemoryUsageOfImage(img));

                    if (pnl_alertPanel.Visible &&
                        lbl_alertLabel.Text == AnimationMessages.TextNoAnimationInSheetToGeneratePreview)
                    {
                        pnl_alertPanel.Visible = false;
                    }

                    if (cb_showFrameBounds.Checked)
                    {
                        ShowFrameBounds();
                    }
                }));
            });
        }