From bde657a85de55bdf5f4ecf6a40d843d8e62265ec Mon Sep 17 00:00:00 2001 From: Ragath Date: Tue, 10 Jan 2023 23:54:12 +0100 Subject: [PATCH] net7 --- .github/workflows/dotnet.yml | 52 ++++++ GitVersion.yml | 4 + LICENSE.txt => LICENSE | 2 +- README.md | 2 +- Tyxel/Extensions.cs | 24 +++ Tyxel/Model/Migrations.cs | 17 ++ Tyxel/Model/ProjectConfig.cs | 23 ++- .../ProjectConfigConverter.Migrations.cs | 14 -- Tyxel/Model/ProjectConfigConverter.cs | 28 ---- Tyxel/Model/TilesetConfig.cs | 17 +- Tyxel/Program.cs | 53 +++--- Tyxel/ProjectExtensions.cs | 23 ++- Tyxel/ProjectManager.cs | 63 ++++--- Tyxel/PyxelExtensions.cs | 33 ++-- Tyxel/PyxelProcessing.cs | 156 +++++++++--------- Tyxel/Tiled/Tileset.cs | 51 +++--- Tyxel/Tyxel.csproj | 49 ++++-- Tyxel/Usings.cs | 4 + Tyxel/Watcher.cs | 95 ++++++----- 19 files changed, 384 insertions(+), 326 deletions(-) create mode 100644 .github/workflows/dotnet.yml create mode 100644 GitVersion.yml rename LICENSE.txt => LICENSE (97%) create mode 100644 Tyxel/Extensions.cs create mode 100644 Tyxel/Model/Migrations.cs delete mode 100644 Tyxel/Model/ProjectConfigConverter.Migrations.cs delete mode 100644 Tyxel/Model/ProjectConfigConverter.cs create mode 100644 Tyxel/Usings.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..3d286e8 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,52 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + push: + branches: [ "master" ] + tags: [ "v*" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} # checkout the correct branch name + fetch-depth: 0 + + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0.9.7 + with: + versionSpec: '5.x' + + - name: Determine Version + uses: gittools/actions/gitversion/execute@v0.9.7 + with: + useConfigFile: true + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + + - name: Build + run: dotnet build -p:Version=$GITVERSION_NUGETVERSION + + - name: Test + run: dotnet test --no-build --verbosity normal + + - name: Pack + run: dotnet pack --no-build -o ./ -p:Version=$GITVERSION_NUGETVERSION + + - name: Push + if: ${{ startsWith(github.ref, 'refs/tags/v') }} + run: dotnet nuget push *.nupkg --source https://api.nuget.org/v3/index.json --api-key $APIKey + env: + APIKey: ${{ secrets.NUGETKEY }} \ No newline at end of file diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..70ce7e8 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1,4 @@ +mode: ContinuousDeployment +branches: {} +ignore: + sha: [] diff --git a/LICENSE.txt b/LICENSE similarity index 97% rename from LICENSE.txt rename to LICENSE index d443cc7..a73bd21 100644 --- a/LICENSE.txt +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Daniel Sör +Copyright (c) 2023 Daniel Sör Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6537d13..9e613cc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Tyxel - [*Quickstart guide*](./Docs/Getting_started.md) [![Build status](https://ci.appveyor.com/api/projects/status/ghf0md1regh88xgx/branch/master?svg=true)](https://ci.appveyor.com/project/Ragath/tyxel/branch/master) -Pyxel->Tiled Converter. Observes changes in *.pyxel file and (re-)generates a Tiled-tilesheet. +Pyxel->Tiled Converter. Observes changes in *.pyxel file and generates a Tiled-tilesheet. Built ontop of [PyxelParser.Net](https://github.com/Ragath/PyxelParser.Net/) Sample project included in Sample.json and Sample.pyxel. ![tyxel](https://cloud.githubusercontent.com/assets/1191717/22309491/befe966c-e34b-11e6-8636-240b0d4a3001.png) diff --git a/Tyxel/Extensions.cs b/Tyxel/Extensions.cs new file mode 100644 index 0000000..5979026 --- /dev/null +++ b/Tyxel/Extensions.cs @@ -0,0 +1,24 @@ +using SkiaSharp; + +namespace Tyxel; +static class Extensions +{ + + public static void Draw(this SKBitmap bitmap, Action action) + { + if (!bitmap.ReadyToDraw) + throw new Exception(nameof(bitmap.ReadyToDraw)); + + using var canvas = new SKCanvas(bitmap); + action(canvas); + canvas.Flush(); + } + + + public static void Save(this SKBitmap bitmap, string path) + { + using var stream = File.Create(path); + if (!bitmap.Encode(stream, SKEncodedImageFormat.Png, 100)) + throw new Exception(nameof(bitmap.Encode)); + } +} diff --git a/Tyxel/Model/Migrations.cs b/Tyxel/Model/Migrations.cs new file mode 100644 index 0000000..af3dc08 --- /dev/null +++ b/Tyxel/Model/Migrations.cs @@ -0,0 +1,17 @@ +namespace Tyxel.Model; + +internal static class Migrations +{ + static IReadOnlyList> MigrationActions { get; } = new Action[] + { + data => data.Version = 1 + }; + + public static int CurrentVersion => MigrationActions.Count; + + public static void ApplyMigrations(this ProjectConfig cfg) + { + while (cfg.Version < CurrentVersion) + MigrationActions[cfg.Version](cfg); + } +} diff --git a/Tyxel/Model/ProjectConfig.cs b/Tyxel/Model/ProjectConfig.cs index 1812163..26bddc6 100644 --- a/Tyxel/Model/ProjectConfig.cs +++ b/Tyxel/Model/ProjectConfig.cs @@ -1,16 +1,15 @@ -using Newtonsoft.Json; +namespace Tyxel.Model; -namespace Tyxel.Model +public class ProjectConfig : IJsonOnDeserialized, IJsonOnDeserializing { - [JsonConverter(typeof(ProjectConfigConverter))] - public class ProjectConfig - { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] - public int Version { get; set; } = ProjectConfigConverter.Migrations.Count; - public string Pyxel { get; set; } - public TilesetConfig Tileset { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public int Version { get; set; } = Migrations.CurrentVersion; + public string Pyxel { get; set; } = null!; + public TilesetConfig Tileset { get; set; } = null!; - [JsonIgnore] - public string Root { get; set; } - } + [JsonIgnore] + public string? Root { get; set; } + + public void OnDeserialized() => Migrations.ApplyMigrations(this); + public void OnDeserializing() => Version = default; } diff --git a/Tyxel/Model/ProjectConfigConverter.Migrations.cs b/Tyxel/Model/ProjectConfigConverter.Migrations.cs deleted file mode 100644 index 7b8689d..0000000 --- a/Tyxel/Model/ProjectConfigConverter.Migrations.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json.Linq; - -namespace Tyxel.Model -{ - public partial class ProjectConfigConverter - { - internal static IReadOnlyList> Migrations { get; } = new Action[] - { - data => data["Version"] = 1 - }; - } -} diff --git a/Tyxel/Model/ProjectConfigConverter.cs b/Tyxel/Model/ProjectConfigConverter.cs deleted file mode 100644 index 635a87d..0000000 --- a/Tyxel/Model/ProjectConfigConverter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Tyxel.Model -{ - public partial class ProjectConfigConverter : JsonConverter - { - public override bool CanConvert(Type objectType) => typeof(ProjectConfig).IsAssignableFrom(objectType); - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var jObject = JObject.Load(reader); - - for (var v = (int?)jObject["Version"] ?? 0; v < Migrations.Count; v = (int)jObject["Version"]) - Migrations[v](jObject); - - - var result = existingValue as ProjectConfig ?? new ProjectConfig(); - serializer.Populate(jObject.CreateReader(), result); - return result; - } - - - public override bool CanWrite => false; - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); - } -} diff --git a/Tyxel/Model/TilesetConfig.cs b/Tyxel/Model/TilesetConfig.cs index b0834c4..e1280f0 100644 --- a/Tyxel/Model/TilesetConfig.cs +++ b/Tyxel/Model/TilesetConfig.cs @@ -1,12 +1,11 @@ -namespace Tyxel.Model +namespace Tyxel.Model; + +public class TilesetConfig { - public class TilesetConfig - { - public string OutputDir { get; set; } - public string Name { get; set; } - public ushort Spacing { get; set; } + public string OutputDir { get; set; } = null!; + public string Name { get; set; } = null!; + public ushort Spacing { get; set; } - public string ImageFile => $"{Name}.png"; - public string TilesetFile => $"{Name}.json"; - } + public string ImageFile => $"{Name}.png"; + public string TilesetFile => $"{Name}.json"; } diff --git a/Tyxel/Program.cs b/Tyxel/Program.cs index 3a1f813..29199d3 100644 --- a/Tyxel/Program.cs +++ b/Tyxel/Program.cs @@ -1,41 +1,26 @@ -using System; -using System.IO; -using System.Linq; -using Tyxel.Model; - -namespace Tyxel +if (!args.Any()) { - class Program - { - static void Main(string[] args) - { - if (!args.Any()) - { - Console.WriteLine("Please use this program to open a .json file."); - Console.WriteLine("Drag & drop the *.json file onto the .exe is one way of doing so."); - return; - } - - var input = string.Join(" ", args); - if (!File.Exists(input)) - throw new FileNotFoundException(); + Console.WriteLine("Please use this program to open a .json file."); + Console.WriteLine("Drag & drop the *.json file onto the .exe is one way of doing so."); + return; +} +var input = string.Join(" ", args); +if (!File.Exists(input)) + throw new FileNotFoundException(); - ProjectConfig project; - if (Path.GetExtension(input).Equals(".pyxel", StringComparison.InvariantCultureIgnoreCase)) - project = ProjectManager.CreateProject(input); - else - project = ProjectManager.LoadProject(input); +ProjectConfig project; +if (Path.GetExtension(input).Equals(".pyxel", StringComparison.InvariantCultureIgnoreCase)) + project = ProjectManager.CreateProject(input); +else + project = ProjectManager.LoadProject(input); - var pyxelPath = project.Pyxel; - Console.WriteLine($"Observing: {pyxelPath}"); - using (var watcher = new Watcher(pyxelPath, filename => PyxelProcessing.ProcessFile(project))) - { - while (true) - watcher.WaitForChange(); - } - } - } +var pyxelPath = project.Pyxel; +Console.WriteLine($"Observing: {pyxelPath}"); +using (var watcher = new Watcher(pyxelPath, filename => PyxelProcessing.ProcessFile(project))) +{ + while (true) + watcher.WaitForChange(); } diff --git a/Tyxel/ProjectExtensions.cs b/Tyxel/ProjectExtensions.cs index 4c9f156..a381978 100644 --- a/Tyxel/ProjectExtensions.cs +++ b/Tyxel/ProjectExtensions.cs @@ -1,16 +1,13 @@ -using System.IO; +namespace Tyxel; -namespace Tyxel +static class ProjectExtensions { - static class ProjectExtensions - { - public static string GetOutputDir(this Model.ProjectConfig project) - => Path.IsPathRooted(project.Tileset.OutputDir) ? project.Tileset.OutputDir : Path.Combine(project.Root, project.Tileset.OutputDir); - public static string GetPyxelPath(this Model.ProjectConfig project) - => Path.IsPathRooted(project.Pyxel) ? project.Pyxel : Path.Combine(project.Root, project.Pyxel); - public static string GetTilesetJsonPath(this Model.ProjectConfig project) - => Path.Combine(project.GetOutputDir(), project.Tileset.TilesetFile); - public static string GetTilesetImagePath(this Model.ProjectConfig project) - => Path.Combine(project.GetOutputDir(), project.Tileset.ImageFile); - } + public static string GetOutputDir(this Model.ProjectConfig project) + => Path.IsPathRooted(project.Tileset.OutputDir) ? project.Tileset.OutputDir : Path.Combine(project.Root!, project.Tileset.OutputDir); + public static string GetPyxelPath(this Model.ProjectConfig project) + => Path.IsPathRooted(project.Pyxel) ? project.Pyxel : Path.Combine(project.Root!, project.Pyxel); + public static string GetTilesetJsonPath(this Model.ProjectConfig project) + => Path.Combine(project.GetOutputDir(), project.Tileset.TilesetFile); + public static string GetTilesetImagePath(this Model.ProjectConfig project) + => Path.Combine(project.GetOutputDir(), project.Tileset.ImageFile); } diff --git a/Tyxel/ProjectManager.cs b/Tyxel/ProjectManager.cs index 8089212..7e8875e 100644 --- a/Tyxel/ProjectManager.cs +++ b/Tyxel/ProjectManager.cs @@ -1,43 +1,42 @@ -using System; -using System.IO; -using Newtonsoft.Json; -using Tyxel.Model; +namespace Tyxel; -namespace Tyxel +static class ProjectManager { - static class ProjectManager + public static ProjectConfig LoadProject(string path) { - public static ProjectConfig LoadProject(string path) + Console.WriteLine($"Opening project: {path}"); + var project = JsonSerializer.Deserialize(File.ReadAllText(path), new JsonSerializerOptions(JsonSerializerDefaults.Web) { - Console.WriteLine($"Opening project: {path}"); - var project = JsonConvert.DeserializeObject(File.ReadAllText(path)); - project.Root = Path.GetDirectoryName(Path.GetFullPath(path)); - return project; - } + ReadCommentHandling = JsonCommentHandling.Skip + }) ?? throw new NullReferenceException(); + + project.Root = Path.GetDirectoryName(Path.GetFullPath(path)); + + return project; + } - public static ProjectConfig CreateProject(string pyxelPath) + public static ProjectConfig CreateProject(string pyxelPath) + { + var projectPath = Path.ChangeExtension(Path.GetFullPath(pyxelPath), ".json"); + if (File.Exists(projectPath)) + return LoadProject(projectPath); + else { - var projectPath = Path.ChangeExtension(Path.GetFullPath(pyxelPath), ".json"); - if (File.Exists(projectPath)) - return LoadProject(projectPath); - else + var project = new ProjectConfig() { - var project = new ProjectConfig() + Pyxel = Path.GetFileName(pyxelPath), + Root = Path.GetDirectoryName(Path.GetFullPath(projectPath)), + Tileset = new TilesetConfig() { - Pyxel = Path.GetFileName(pyxelPath), - Root = Path.GetDirectoryName(Path.GetFullPath(projectPath)), - Tileset = new TilesetConfig() - { - Name = Path.GetFileNameWithoutExtension(projectPath) + "_Tileset", - OutputDir = "Output", - Spacing = 1 - } - }; - Console.WriteLine($"Creating project: {projectPath}"); - File.WriteAllText(projectPath, JsonConvert.SerializeObject(project, Formatting.Indented)); - return project; - } + Name = Path.GetFileNameWithoutExtension(projectPath) + "_Tileset", + OutputDir = "Output", + Spacing = 1 + } + }; + Console.WriteLine($"Creating project: {projectPath}"); + File.WriteAllText(projectPath, JsonSerializer.Serialize(project, new JsonSerializerOptions { WriteIndented = true })); + return project; } - } + } diff --git a/Tyxel/PyxelExtensions.cs b/Tyxel/PyxelExtensions.cs index 7b29abd..b68d9fb 100644 --- a/Tyxel/PyxelExtensions.cs +++ b/Tyxel/PyxelExtensions.cs @@ -1,19 +1,26 @@ using System.Drawing; using PyxelParser; +using SkiaSharp; -namespace Tyxel +namespace Tyxel; + +static class PyxelExtensions { - static class PyxelExtensions - { - public static int GetColumns(this Canvas canvas) => canvas.Width / canvas.TileWidth; - public static int GetRows(this Canvas canvas) => canvas.Height / canvas.TileHeight; + public static int GetColumns(this Canvas canvas) => canvas.Width / canvas.TileWidth; + public static int GetRows(this Canvas canvas) => canvas.Height / canvas.TileHeight; - public static Rectangle GetCell(this Canvas canvas, int x, int y) => new Rectangle - { - X = x * canvas.TileWidth, - Y = y * canvas.TileHeight, - Width = canvas.TileWidth, - Height = canvas.TileHeight - }; - } + public static Rectangle GetCell(this Canvas canvas, int x, int y) => new() + { + X = x * canvas.TileWidth, + Y = y * canvas.TileHeight, + Width = canvas.TileWidth, + Height = canvas.TileHeight + }; + public static SKRect ToRect(this Rectangle rect) => new() + { + Left = rect.Left, + Top = rect.Top, + Bottom = rect.Bottom, + Right = rect.Right + }; } diff --git a/Tyxel/PyxelProcessing.cs b/Tyxel/PyxelProcessing.cs index 37fc411..1fde6df 100644 --- a/Tyxel/PyxelProcessing.cs +++ b/Tyxel/PyxelProcessing.cs @@ -1,98 +1,98 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Linq; -using Newtonsoft.Json; -using Tyxel.Model; +using System.Diagnostics; +using SkiaSharp; -namespace Tyxel +namespace Tyxel; + +static class PyxelProcessing { - static class PyxelProcessing + public static void ProcessFile(ProjectConfig project) { - public static void ProcessFile(ProjectConfig project) - { - var sw = Stopwatch.StartNew(); - while (true) - try - { - var pyxelPath = project.GetPyxelPath(); - Console.WriteLine($"Processing { pyxelPath}"); - if (File.Exists(pyxelPath)) - using (var stream = File.Open(pyxelPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + var sw = Stopwatch.StartNew(); + while (true) + try + { + var pyxelPath = project.GetPyxelPath(); + Console.WriteLine($"Processing {pyxelPath}"); + if (File.Exists(pyxelPath)) + using (var stream = File.Open(pyxelPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + using (var doc = new PyxelParser.Document(stream)) { - using (var doc = new PyxelParser.Document(stream)) - { - var meta = doc.MetaData.Value; - - bool sheetFinder(KeyValuePair l) => l.Value.Name.Trim().Equals("Sheet", StringComparison.InvariantCultureIgnoreCase); + var meta = doc.MetaData.Value; - if (!meta.Canvas.Layers.Any(sheetFinder)) - throw new KeyNotFoundException("Could not find a layer named 'Sheet'"); + bool sheetFinder(KeyValuePair l) => l.Value.Name.Trim().Equals("Sheet", StringComparison.InvariantCultureIgnoreCase); - var sheetLayer = meta.Canvas.Layers.Single(sheetFinder); + if (!meta.Canvas.Layers.Any(sheetFinder)) + throw new KeyNotFoundException("Could not find a layer named 'Sheet'"); - var columns = meta.Canvas.GetColumns(); - var rows = meta.Canvas.GetRows(); + var sheetLayer = meta.Canvas.Layers.Single(sheetFinder); - using (var img = doc.GetImages(entry => Image.FromStream(entry.Stream)).Single(i => i.Path == $"layer{sheetLayer.Key}.png").Value) - { - Directory.CreateDirectory(Path.GetDirectoryName(project.GetTilesetImagePath())); - var imgPath = project.GetTilesetImagePath(); - SaveSheet(project.Tileset, meta, columns, rows, img, imgPath); - } + var columns = meta.Canvas.GetColumns(); + var rows = meta.Canvas.GetRows(); - var ts = new Tiled.Tileset() - { - firstgid = 1, - Columns = columns, - ImagePath = project.Tileset.ImageFile, - imagewidth = meta.Canvas.Width, - imageheight = meta.Canvas.Height, - margin = 0, - name = project.Tileset.Name, - spacing = project.Tileset.Spacing, - TileCount = columns * rows, - tileheight = meta.Canvas.TileHeight, - tilewidth = meta.Canvas.TileWidth, - transparentcolor = null - }; - Directory.CreateDirectory(Path.GetDirectoryName(project.GetTilesetJsonPath())); - File.WriteAllText(project.GetTilesetJsonPath(), JsonConvert.SerializeObject(ts, Formatting.Indented)); + using (var img = doc.GetImages(entry => SKBitmap.Decode(entry.Stream)).Single(i => i.Path == $"layer{sheetLayer.Key}.png").Value) + { + Directory.CreateDirectory(Path.GetDirectoryName(project.GetTilesetImagePath())!); + var imgPath = project.GetTilesetImagePath(); + SaveSheet(project.Tileset, meta, columns, rows, img, imgPath); } + + var ts = new Tiled.Tileset() + { + firstgid = 1, + Columns = columns, + ImagePath = project.Tileset.ImageFile, + imagewidth = meta.Canvas.Width, + imageheight = meta.Canvas.Height, + margin = 0, + name = project.Tileset.Name, + spacing = project.Tileset.Spacing, + TileCount = columns * rows, + tileheight = meta.Canvas.TileHeight, + tilewidth = meta.Canvas.TileWidth, + transparentcolor = null + }; + Directory.CreateDirectory(Path.GetDirectoryName(project.GetTilesetJsonPath())!); + File.WriteAllText(project.GetTilesetJsonPath(), JsonSerializer.Serialize(ts, new JsonSerializerOptions { WriteIndented = true })); } + } - break; - } - catch (IOException) - { - System.Threading.Thread.Sleep(10); - if (sw.Elapsed > TimeSpan.FromSeconds(3)) - throw; - } - } + break; + } + catch (IOException) //HACK: Wtf? + { + Thread.Sleep(10); + if (sw.Elapsed > TimeSpan.FromSeconds(3)) + throw; + } + } - static void SaveSheet(TilesetConfig output, PyxelParser.PyxelData meta, int columns, int rows, Image img, string outPath) + static void SaveSheet(TilesetConfig output, PyxelParser.PyxelData meta, int columns, int rows, SKBitmap img, string outPath) + { + if (output.Spacing == 0) + img.Save(outPath); + else { - if (output.Spacing == 0) - img.Save(outPath); - else + var w = columns * meta.Canvas.TileWidth + (columns - 1) * output.Spacing; + var h = rows * meta.Canvas.TileHeight + (rows - 1) * output.Spacing; + using var result = new SKBitmap(w, h, isOpaque: false); + + + result.Draw(canvas => { - using (var result = new Bitmap(columns * meta.Canvas.TileWidth + (columns - 1) * output.Spacing, rows * meta.Canvas.TileHeight + (rows - 1) * output.Spacing)) - { - using (var gfx = Graphics.FromImage(result)) + int DestX(int x) => x * meta.Canvas.TileWidth + x * output.Spacing; + int DestY(int y) => y * meta.Canvas.TileHeight + y * output.Spacing; + for (int y = 0; y < rows; y++) + for (int x = 0; x < columns; x++) { - int DestX(int x) => x * meta.Canvas.TileWidth + x * output.Spacing; - int DestY(int y) => y * meta.Canvas.TileHeight + y * output.Spacing; - for (int y = 0; y < rows; y++) - for (int x = 0; x < columns; x++) - gfx.DrawImage(img, DestX(x), DestY(y), meta.Canvas.GetCell(x, y), GraphicsUnit.Pixel); - gfx.Flush(); + var src = meta.Canvas.GetCell(x, y).ToRect(); + var dst = SKRect.Create(new(DestX(x), DestY(y)), src.Size); + + canvas.DrawBitmap(img, src, dst); } - result.Save(outPath); - } - } + }); + + result.Save(outPath); } } } diff --git a/Tyxel/Tiled/Tileset.cs b/Tyxel/Tiled/Tileset.cs index 5fbda3d..e113103 100644 --- a/Tyxel/Tiled/Tileset.cs +++ b/Tyxel/Tiled/Tileset.cs @@ -1,38 +1,37 @@ -using System.Collections.Generic; -using Newtonsoft.Json; +namespace Tyxel.Tiled; -namespace Tyxel.Tiled + +public class Tileset { + public int firstgid { get; set; } - public class Tileset - { - public int firstgid { get; set; } + [JsonPropertyName("columns")] + public int Columns { get; set; } - [JsonProperty("columns")] - public int Columns { get; set; } + public int imageheight { get; set; } + [JsonPropertyName("image")] + public string? ImagePath { get; set; } + public int imagewidth { get; set; } + public int margin { get; set; } + public string name { get; set; } = null!; - public int imageheight { get; set; } - [JsonProperty("image")] - public string ImagePath { get; set; } - public int imagewidth { get; set; } - public int margin { get; set; } - public string name { get; set; } + [JsonPropertyName("properties")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary? Properties { get; set; } - [JsonProperty("properties", NullValueHandling = NullValueHandling.Ignore)] - public Dictionary Properties { get; set; } + public int spacing { get; set; } - public int spacing { get; set; } + [JsonPropertyName("tilecount")] + public int TileCount { get; set; } - [JsonProperty("tilecount")] - public int TileCount { get; set; } + public int tileheight { get; set; } - public int tileheight { get; set; } + [JsonPropertyName("tileproperties")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Dictionary>? TileProperties { get; set; } - [JsonProperty("tileproperties", NullValueHandling = NullValueHandling.Ignore)] - public Dictionary> TileProperties { get; set; } + public int tilewidth { get; set; } - public int tilewidth { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string transparentcolor { get; set; } - } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? transparentcolor { get; set; } } diff --git a/Tyxel/Tyxel.csproj b/Tyxel/Tyxel.csproj index b284527..de9b313 100644 --- a/Tyxel/Tyxel.csproj +++ b/Tyxel/Tyxel.csproj @@ -1,20 +1,37 @@  - - net462 - exe - exe - + + net7.0 + exe + exe + True + + true + enable + + + + Daniel Sör + Daniel Sör + Pyxel->Tiled Converter. Observes changes in *.pyxel file and generates a Tiled-tilesheet. + pyxel tiled converter tool + Copyright 2017 (c) Daniel Sör. All rights reserved. + https://github.com/Ragath/Tyxel/ + https://github.com/Ragath/Tyxel/ + git + LICENSE + + + + + True + / + + + + + + + - - - - All - - - - - - - \ No newline at end of file diff --git a/Tyxel/Usings.cs b/Tyxel/Usings.cs new file mode 100644 index 0000000..6b7b383 --- /dev/null +++ b/Tyxel/Usings.cs @@ -0,0 +1,4 @@ +global using Tyxel; +global using Tyxel.Model; +global using System.Text.Json; +global using System.Text.Json.Serialization; diff --git a/Tyxel/Watcher.cs b/Tyxel/Watcher.cs index f405313..c00e826 100644 --- a/Tyxel/Watcher.cs +++ b/Tyxel/Watcher.cs @@ -1,56 +1,53 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; +namespace Tyxel; -namespace Tyxel +class Watcher : IDisposable { - class Watcher : IDisposable + Dictionary Buffer { get; } = new Dictionary(); + FileSystemWatcher FileSystemWatcher { get; } + + Action Work { get; } + int MillisecondsTimeout { get; } + + public Watcher(string filename, Action work, int millisecondsTimeout = 10) { - Dictionary Buffer { get; } = new Dictionary(); - FileSystemWatcher FileSystemWatcher { get; } - - Action Work { get; } - int MillisecondsTimeout { get; } - - public Watcher(string filename, Action work, int millisecondsTimeout = 10) - { - var path = Path.GetFullPath(filename); - Work = work; - MillisecondsTimeout = millisecondsTimeout; - FileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path)) { EnableRaisingEvents = true }; - ScheduleWork(path); - } - - public void WaitForChange() - { - var change = FileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed); - var filename = Path.GetFullPath(change.Name); - ScheduleWork(filename); - } - - private void ScheduleWork(string filename) - { - if (!Buffer.TryGetValue(filename, out var scheduled) || !scheduled) - lock (Buffer) - { - Buffer[filename] = true; + var path = Path.GetFullPath(filename); + if (path == null) + throw new NullReferenceException(nameof(path)); + + Work = work; + MillisecondsTimeout = millisecondsTimeout; + FileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(path), Path.GetFileName(path)) { EnableRaisingEvents = true }; + ScheduleWork(path); + } + + public void WaitForChange() + { + var change = FileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed); + var filename = Path.GetFullPath(change.Name ?? throw new NullReferenceException(nameof(change.Name))); + ScheduleWork(filename); + } + + private void ScheduleWork(string filename) + { + if (!Buffer.TryGetValue(filename, out var scheduled) || !scheduled) + lock (Buffer) + { + Buffer[filename] = true; - ThreadPool.QueueUserWorkItem(state => + ThreadPool.QueueUserWorkItem(state => + { + Thread.Sleep(MillisecondsTimeout); + lock (Buffer) { - Thread.Sleep(MillisecondsTimeout); - lock (Buffer) - { - Buffer[filename] = false; - Work(filename); - } - }); - } - } - - public void Dispose() - { - ((IDisposable)FileSystemWatcher).Dispose(); - } + Buffer[filename] = false; + Work(filename); + } + }); + } + } + + public void Dispose() + { + ((IDisposable)FileSystemWatcher).Dispose(); } }