From 1ffe6cc5c984708f43511a1a1b055eea3e567ac6 Mon Sep 17 00:00:00 2001 From: "Dr.Abc" Date: Fri, 16 Aug 2024 18:09:18 +0800 Subject: [PATCH] update cli create spr --- SPRView.Net.CLI/Cli/CLICommandManager.cs | 4 +- SPRView.Net.CLI/Cli/CreateCommand.cs | 46 +++++++ SPRView.Net.CLI/Cli/InformationCommand.cs | 4 +- SPRView.Net.CLI/Cli/PreviewCommand.cs | 4 +- SPRView.Net.CLI/Cli/SaveImageCommand.cs | 4 +- SPRView.Net.CLI/Cli/ThumbnailCommand.cs | 4 +- SPRView.Net.CLI/Program.cs | 2 +- SPRView.Net.Lib/Class/Frame.cs | 1 + SPRView.Net.Lib/Class/Sprite.cs | 131 ++++++++++++++++++- SPRView.Net.Lib/Interface/ISprite.cs | 4 + SPRView.Net/Storage/CStorage.cs | 2 +- SPRView.Net/ViewModel/CreateNewViewModel.cs | 138 +------------------- 12 files changed, 199 insertions(+), 145 deletions(-) create mode 100644 SPRView.Net.CLI/Cli/CreateCommand.cs diff --git a/SPRView.Net.CLI/Cli/CLICommandManager.cs b/SPRView.Net.CLI/Cli/CLICommandManager.cs index 166ef0a..6d42faa 100644 --- a/SPRView.Net.CLI/Cli/CLICommandManager.cs +++ b/SPRView.Net.CLI/Cli/CLICommandManager.cs @@ -1,5 +1,6 @@ using CliFx; -namespace SPRView.Net.Cli; + +namespace SPRView.Net.CLI.Cli; public class CLICommandManager { public async void Run(string[] args) => await new CliApplicationBuilder() @@ -7,6 +8,7 @@ public class CLICommandManager .AddCommand() .AddCommand() .AddCommand() + .AddCommand() .Build() .RunAsync(args); } \ No newline at end of file diff --git a/SPRView.Net.CLI/Cli/CreateCommand.cs b/SPRView.Net.CLI/Cli/CreateCommand.cs new file mode 100644 index 0000000..3722523 --- /dev/null +++ b/SPRView.Net.CLI/Cli/CreateCommand.cs @@ -0,0 +1,46 @@ +using CliFx; +using CliFx.Attributes; +using CliFx.Infrastructure; +using SPRView.Net.Lib.Class; +using SPRView.Net.Lib.Interface; + +namespace SPRView.Net.CLI.Cli; + +[Command("create", Description = "create a spr from files")] +public class CreateCommand : ICommand +{ + [CommandParameter(0, Description = "Images path, Use \",\" connect multiple paths")] + public required string Paths { get; set; } + + [CommandParameter(1, Description = "Spr width")] + public required int Width { get; set; } + + [CommandParameter(2, Description = "Spr Height")] + public required int Height { get; set; } + + [CommandParameter(3, Description = "Path to save spr file")] + public required string SavePath { get; set; } + + [CommandOption("type", 't', Description = "Spr type")] + public int Type { get; set; } = (int)ISprite.SpriteType.Parallel; + + [CommandOption("format", 'f', Description = "Spr format")] + public int Format { get; set; } = (int)ISprite.SpriteFormat.Normal; + + [CommandOption("sync", 's', Description = "Spr sync")] + public bool Sync { get; set; } = true; + + [CommandOption("beam length", 'b', Description = "Spr beam lenght")] + public float BeamLenght { get; set; } = 0.0f; + + [CommandOption("unpack", 'u', Description = "Unpack multiple frames of images")] + public bool Unpack { get; set; } = false; + + public ValueTask ExecuteAsync(IConsole console) + { + using FileStream fs = File.OpenWrite(SavePath); + string[] paths = Array.ConvertAll(Paths.Split(','), s => s.Trim()); + CSprite.Save(paths, fs, Width, Height, (ISprite.SpriteFormat)Format, (ISprite.SpriteType)Type, (ISprite.SpriteSynchron)(Sync ? 0 : 1), BeamLenght, Unpack); + return default; + } +} diff --git a/SPRView.Net.CLI/Cli/InformationCommand.cs b/SPRView.Net.CLI/Cli/InformationCommand.cs index d4be6e8..2f9f444 100644 --- a/SPRView.Net.CLI/Cli/InformationCommand.cs +++ b/SPRView.Net.CLI/Cli/InformationCommand.cs @@ -3,9 +3,9 @@ using CliFx.Infrastructure; using Pastel; using SixLabors.ImageSharp.PixelFormats; -using SPRView.Net.Lib; +using SPRView.Net.Lib.Class; using System.Drawing; -namespace SPRView.Net.Cli; +namespace SPRView.Net.CLI.Cli; [Command("information", Description = "Get spr information")] public class InformationCommand : ICommand { diff --git a/SPRView.Net.CLI/Cli/PreviewCommand.cs b/SPRView.Net.CLI/Cli/PreviewCommand.cs index bdcfaaa..8fef0cd 100644 --- a/SPRView.Net.CLI/Cli/PreviewCommand.cs +++ b/SPRView.Net.CLI/Cli/PreviewCommand.cs @@ -5,8 +5,8 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SPRView.Net.Lib; -namespace SPRView.Net.Cli; +using SPRView.Net.Lib.Class; +namespace SPRView.Net.CLI.Cli; [Command("preview", Description = "Preview a spr in console")] public class PreviewCommand : ICommand { diff --git a/SPRView.Net.CLI/Cli/SaveImageCommand.cs b/SPRView.Net.CLI/Cli/SaveImageCommand.cs index 99c5b1e..86bdc49 100644 --- a/SPRView.Net.CLI/Cli/SaveImageCommand.cs +++ b/SPRView.Net.CLI/Cli/SaveImageCommand.cs @@ -2,8 +2,8 @@ using CliFx.Attributes; using CliFx.Infrastructure; using SixLabors.ImageSharp; -using SPRView.Net.Lib; -namespace SPRView.Net.Cli; +using SPRView.Net.Lib.Class; +namespace SPRView.Net.CLI.Cli; [Command("image", Description = "Save spr to image")] public class SaveImageCommand : ICommand { diff --git a/SPRView.Net.CLI/Cli/ThumbnailCommand.cs b/SPRView.Net.CLI/Cli/ThumbnailCommand.cs index 1601779..5bacc91 100644 --- a/SPRView.Net.CLI/Cli/ThumbnailCommand.cs +++ b/SPRView.Net.CLI/Cli/ThumbnailCommand.cs @@ -3,8 +3,8 @@ using CliFx.Infrastructure; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; -using SPRView.Net.Lib; -namespace SPRView.Net.Cli; +using SPRView.Net.Lib.Class; +namespace SPRView.Net.CLI.Cli; [Command("thumbnail", Description = "Generate thumbnail form a spr")] public class ThumbnailCommand : ICommand { diff --git a/SPRView.Net.CLI/Program.cs b/SPRView.Net.CLI/Program.cs index 69735c7..63e85bd 100644 --- a/SPRView.Net.CLI/Program.cs +++ b/SPRView.Net.CLI/Program.cs @@ -1,3 +1,3 @@ -using SPRView.Net.Cli; +using SPRView.Net.CLI.Cli; CLICommandManager cli = new(); cli.Run(args); \ No newline at end of file diff --git a/SPRView.Net.Lib/Class/Frame.cs b/SPRView.Net.Lib/Class/Frame.cs index 73be91a..dce3ca9 100644 --- a/SPRView.Net.Lib/Class/Frame.cs +++ b/SPRView.Net.Lib/Class/Frame.cs @@ -1,5 +1,6 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; +using SPRView.Net.Lib.Class; using SPRView.Net.Lib.Interface; namespace SPRView.Net.Lib; diff --git a/SPRView.Net.Lib/Class/Sprite.cs b/SPRView.Net.Lib/Class/Sprite.cs index 32f6868..00c4792 100644 --- a/SPRView.Net.Lib/Class/Sprite.cs +++ b/SPRView.Net.Lib/Class/Sprite.cs @@ -1,7 +1,9 @@ using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Quantization; using SPRView.Net.Lib.Interface; -namespace SPRView.Net.Lib; +namespace SPRView.Net.Lib.Class; public class CSprite : ISprite { public ISprite.SpriteType Type { get; set; } @@ -92,4 +94,131 @@ public Image GetImage(int frame) } return m_aryFrames[frame].GetImage(); } + public static void Save(string[] files, Stream stream, int width, int height, + ISprite.SpriteFormat format = ISprite.SpriteFormat.Normal, ISprite.SpriteType type = ISprite.SpriteType.Parallel, ISprite.SpriteSynchron sync = ISprite.SpriteSynchron.Sync, + float beamlength = 0, bool bUnpack = false) + { + //量化 + //降低一个维度,以便统一量化 + int buffersize = 0; + if (bUnpack) + { + foreach (var path in files) + { + Image frame = Image.Load(path); + buffersize += frame.Frames.Count; + frame.Dispose(); + } + //会多一个 + buffersize--; + } + else + buffersize = files.Length; + using Image image = new(width, height * buffersize); + int bufferseek = 0; + for (int i = 0; i < files.Length; i++) + { + var path = files[i]; + Image frame = Image.Load(path); + frame.Mutate(x => x.Resize(width, height)); + do + { + image.Mutate(x => x.DrawImage(frame, new Point(0, height * bufferseek), 1.0f)); + frame.Frames.RemoveFrame(0); + bufferseek++; + } while (frame.Frames.Count > 1); + frame.Dispose(); + } + bool isAlphaTest = format == ISprite.SpriteFormat.AlphaTest; + WuQuantizer quantizer = new(new QuantizerOptions + { + Dither = null, + MaxColors = isAlphaTest ? 255 : 256 + }); + image.Mutate(x => x.Quantize(quantizer)); + //生成色板 + Dictionary palette = []; + for (int j = 0; j < image.Height; j++) + { + for (int i = 0; i < image.Width; i++) + { + Rgba32 rgba32 = image[i, j]; + if (!palette.ContainsKey(rgba32)) + palette.Add(rgba32, (byte)palette.Count); + } + } + if (isAlphaTest) + palette.Add(new Rgba32(0, 0, 255, 255), 255); + //保存 + using BinaryWriter writer = new(stream); + //Header + writer.Write(0x50534449); + //Version + writer.Write(0x00000002); + //Type + writer.Write((int)type); + //Format + writer.Write((int)format); + //BoundRadius + float radius = (float)Math.Sqrt(Math.Pow(width, 2) + Math.Pow(height, 2)) / 2; + writer.Write(radius); + //Width + writer.Write(width); + //Height + writer.Write(height); + //Count + writer.Write(buffersize); + //BeamLength + writer.Write(beamlength); + //Sync + writer.Write((int)sync); + //Palette Size + writer.Write((short)palette.Count); + //Palette + foreach (var p in palette.Keys) + { + writer.Write(p.R); + writer.Write(p.G); + writer.Write(p.B); + } + if (palette.Count < 256 && isAlphaTest) + { + for (int i = palette.Count; i < 255; i++) + { + writer.Write((byte)0); + writer.Write((byte)0); + writer.Write((byte)0); + } + writer.Write((byte)0); + writer.Write((byte)0); + writer.Write((byte)255); + } + //保存数据 + for (int k = 0; k < buffersize; k++) + { + //Group + writer.Write(0x00000000); + //OriginX + writer.Write(0x00000000); + //OriginY + writer.Write(0x00000000); + //Width + writer.Write(width); + //Height + writer.Write(height); + + var startY = k * height; + for (int j = startY; j < startY + height; j++) + { + for (int i = 0; i < width; i++) + { + Rgba32 rgba32 = image[i, j]; + if (isAlphaTest && rgba32.A <= 128) + writer.Write(palette.Last().Value); + else + writer.Write(palette[rgba32]); + } + } + } + } } diff --git a/SPRView.Net.Lib/Interface/ISprite.cs b/SPRView.Net.Lib/Interface/ISprite.cs index df6da9d..01ae957 100644 --- a/SPRView.Net.Lib/Interface/ISprite.cs +++ b/SPRView.Net.Lib/Interface/ISprite.cs @@ -41,4 +41,8 @@ public enum SpriteSynchron abstract public static ISprite Create(Stream stream); abstract public static ISprite Create(string? path); abstract public Image GetImage(int frame); + + abstract public static void Save(string[] files, Stream stream, int width, int height, + SpriteFormat format = SpriteFormat.Normal, SpriteType type = SpriteType.Parallel, SpriteSynchron sync = SpriteSynchron.Sync, + float beamlength = 0, bool bUnpack = false); } diff --git a/SPRView.Net/Storage/CStorage.cs b/SPRView.Net/Storage/CStorage.cs index 2820e60..0964212 100644 --- a/SPRView.Net/Storage/CStorage.cs +++ b/SPRView.Net/Storage/CStorage.cs @@ -1,7 +1,7 @@ using Avalonia.Controls; using Avalonia.Media; using SixLabors.ImageSharp.PixelFormats; -using SPRView.Net.Lib; +using SPRView.Net.Lib.Class; using SPRView.Net.Lib.Interface; using System; diff --git a/SPRView.Net/ViewModel/CreateNewViewModel.cs b/SPRView.Net/ViewModel/CreateNewViewModel.cs index 46dfdb4..6da9ef7 100644 --- a/SPRView.Net/ViewModel/CreateNewViewModel.cs +++ b/SPRView.Net/ViewModel/CreateNewViewModel.cs @@ -2,10 +2,7 @@ using Avalonia.Media.Imaging; using Avalonia.Platform.Storage; using Avalonia.Threading; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Quantization; +using SPRView.Net.Lib.Class; using SPRView.Net.Lib.Interface; using System; using System.Collections.Generic; @@ -203,135 +200,10 @@ public async void SaveToSpr() }); if (files != null) { - //量化 - //降低一个维度,以便统一量化 - - int buffersize = 0; - if (UnPackAnimate) - { - foreach (var path in m_aryImagePaths) - { - SixLabors.ImageSharp.Image frame = SixLabors.ImageSharp.Image.Load(path); - buffersize += frame.Frames.Count; - frame.Dispose(); - } - //会多一个 - buffersize--; - } - else - buffersize = m_aryImagePaths.Count; - using Image image = new(Export_Width, Export_Height * buffersize); - int bufferseek = 0; - for (int i = 0; i < m_aryImagePaths.Count; i++) - { - var path = m_aryImagePaths[i]; - SixLabors.ImageSharp.Image frame = SixLabors.ImageSharp.Image.Load(path); - frame.Mutate(x => x.Resize(Export_Width, Export_Height)); - do - { - image.Mutate(x => x.DrawImage(frame, new Point(0, Export_Height * bufferseek), 1.0f)); - frame.Frames.RemoveFrame(0); - bufferseek++; - } while (frame.Frames.Count > 1); - frame.Dispose(); - } - bool isAlphaTest = Format == (int)ISprite.SpriteFormat.AlphaTest; - WuQuantizer quantizer = new(new QuantizerOptions - { - Dither = null, - MaxColors = isAlphaTest ? 255 : 256 - }); - Progress = 20; - image.Mutate(x => x.Quantize(quantizer)); - //生成色板 - Dictionary palette = []; - for (int j = 0; j < image.Height; j++) - { - for (int i = 0; i < image.Width; i++) - { - Rgba32 rgba32 = image[i, j]; - if (!palette.ContainsKey(rgba32)) - palette.Add(rgba32, (byte)palette.Count); - } - } - if (isAlphaTest) - palette.Add(new Rgba32(0, 0, 255, 255), 255); - Progress = 40; - //保存 - await using Stream file = await files.OpenWriteAsync(); - using BinaryWriter writer = new(file); - //Header - writer.Write(0x50534449); - //Version - writer.Write(0x00000002); - //Type - writer.Write(Type); - //Format - writer.Write(Format); - //BoundRadius - float radius = (float)Math.Sqrt(Math.Pow(Export_Width, 2) + Math.Pow(Export_Height, 2)) / 2; - writer.Write(radius); - //Width - writer.Write(Export_Width); - //Height - writer.Write(Export_Height); - //Count - writer.Write(buffersize); - //BeamLength - writer.Write(BeamLength); - //Sync - writer.Write(Sync); - Progress = 60; - //Palette Size - writer.Write((short)palette.Count); - //Palette - foreach (var p in palette.Keys) - { - writer.Write(p.R); - writer.Write(p.G); - writer.Write(p.B); - } - if (palette.Count < 256 && isAlphaTest) - { - for (int i = palette.Count; i < 255; i++) - { - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write((byte)0); - } - writer.Write((byte)0); - writer.Write((byte)0); - writer.Write((byte)255); - } - Progress = 100; - //保存数据 - for (int k = 0; k < buffersize; k++) - { - Progress += k * 100 / buffersize; - //Group - writer.Write(0x00000000); - //OriginX - writer.Write(0x00000000); - //OriginY - writer.Write(0x00000000); - //Width - writer.Write(Export_Width); - //Height - writer.Write(Export_Height); - - var startY = k * Export_Height; - for (int j = startY; j < startY + Export_Height; j++) - { - for (int i = 0; i < Export_Width; i++) - { - Rgba32 rgba32 = image[i, j]; - if (isAlphaTest && rgba32.A <= 128) - writer.Write(palette.Last().Value); - else - writer.Write(palette[rgba32]); - } - } - } + Progress = 0; + await using Stream fs = await files.OpenWriteAsync(); + CSprite.Save(m_aryImagePaths.ToArray(), fs, Export_Width, Export_Height, + (ISprite.SpriteFormat)Format, (ISprite.SpriteType)Type, (ISprite.SpriteSynchron)Sync, BeamLength, UnPackAnimate); Progress = 200; var box = MessageBoxWindow.CreateMessageBox("☑︎💾", null, Lang!.Shared_OK, Lang.Shared_Cancel); box.Position = new Avalonia.PixelPoint(Parent.Position.X + (int)Parent.Width / 2, Parent.Position.Y + (int)Parent.Height / 2);