From 37ba2cf9f6ac9bfaf5cd6acd6151d75f6880cd39 Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 21:40:44 -0400 Subject: [PATCH 1/8] Fix: Creating a new project now enables Close All Projects --- TileShop.WPF/Features/Project/ProjectTreeViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TileShop.WPF/Features/Project/ProjectTreeViewModel.cs b/TileShop.WPF/Features/Project/ProjectTreeViewModel.cs index 7e5765da..2689975c 100644 --- a/TileShop.WPF/Features/Project/ProjectTreeViewModel.cs +++ b/TileShop.WPF/Features/Project/ProjectTreeViewModel.cs @@ -499,6 +499,7 @@ public void AddNewProject() { var projectVM = new ProjectNodeViewModel((ProjectNode)success.Result.Tree.Root); Projects.Add(projectVM); + NotifyOfPropertyChange(() => HasProject); _events.PublishOnUIThread(new ProjectLoadedEvent()); }, fail => _windowManager.ShowMessageBox($"{fail.Reason}", "Project Error")); From 801f9be00f45f6f58174431f6cfe80f001a15383 Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 21:52:04 -0400 Subject: [PATCH 2/8] Heavily restructured serialization to mitigate partial element initialization issues. --- ImageMagitek.Services/ProjectService.cs | 1 + ImageMagitek/Arranger.cs | 10 +- .../IGameDescriptorReader.cs | 2 +- .../IGameDescriptorWriter.cs | 2 +- .../Serialization/ProjectTreeBuilder.cs | 189 ++++++++++++++++++ .../XmlGameDescriptorReader.cs | 102 ++-------- .../XmlGameDescriptorWriter.cs | 3 +- .../ArrangerElementModel.cs | 5 +- .../SerializationModels/DataFileModel.cs | 2 +- .../SerializationModels/ImageProjectModel.cs | 2 +- .../SerializationModels/PaletteModel.cs | 4 +- .../SerializationModels/ProjectNodeModel.cs | 2 +- .../ResourceFolderModel.cs | 2 +- .../ScatteredArrangerModel.cs | 18 +- ImageMagitek/ScatteredArranger.cs | 43 ++-- ImageMagitek/SequentialArranger.cs | 56 +++--- .../Imaging/IndexedBitmapAdapter.cs | 36 +++- 17 files changed, 308 insertions(+), 171 deletions(-) rename ImageMagitek/Project/{ => Serialization}/IGameDescriptorReader.cs (81%) rename ImageMagitek/Project/{ => Serialization}/IGameDescriptorWriter.cs (81%) create mode 100644 ImageMagitek/Project/Serialization/ProjectTreeBuilder.cs rename ImageMagitek/Project/{ => Serialization}/XmlGameDescriptorReader.cs (64%) rename ImageMagitek/Project/{ => Serialization}/XmlGameDescriptorWriter.cs (99%) diff --git a/ImageMagitek.Services/ProjectService.cs b/ImageMagitek.Services/ProjectService.cs index aaccdb21..1cc9c175 100644 --- a/ImageMagitek.Services/ProjectService.cs +++ b/ImageMagitek.Services/ProjectService.cs @@ -5,6 +5,7 @@ using System.Xml; using System.Xml.Schema; using ImageMagitek.Project; +using ImageMagitek.Project.Serialization; using Monaco.PathTree; namespace ImageMagitek.Services diff --git a/ImageMagitek/Arranger.cs b/ImageMagitek/Arranger.cs index 65c5e456..05d78285 100644 --- a/ImageMagitek/Arranger.cs +++ b/ImageMagitek/Arranger.cs @@ -83,6 +83,7 @@ public abstract class Arranger : IProjectResource public abstract bool ShouldBeSerialized { get; set; } public abstract void Resize(int arrangerWidth, int arrangerHeight); + public abstract void Resize(int arrangerWidth, int arrangerHeight, Func elementFactory); private readonly BlankDirectCodec _blankDirectCodec = new BlankDirectCodec(); private readonly BlankIndexedCodec _blankIndexedCodec = new BlankIndexedCodec(); @@ -139,10 +140,13 @@ public virtual void SetElement(in ArrangerElement element, int posX, int posY) if (posX >= ArrangerElementSize.Width || posY >= ArrangerElementSize.Height) throw new ArgumentOutOfRangeException($"{nameof(SetElement)} parameter was out of range: ({posX}, {posY})"); - if (element.Codec != null) + if (element.Codec is object) if (element.Codec.ColorType != ColorType) throw new ArgumentException($"{nameof(SetElement)} parameter '{nameof(element)}' did not match the Arranger's {nameof(PixelColorType)}"); + //if (ColorType == PixelColorType.Indexed && element.Palette is null && element.DataFile is object) + // throw new ArgumentException($"{nameof(SetElement)} parameter '{nameof(element)}' does not contain a palette"); + var relocatedElement = element.WithLocation(posX * ElementPixelSize.Width, posY * ElementPixelSize.Height); ElementGrid[posX, posY] = relocatedElement; } @@ -163,9 +167,9 @@ public virtual void ResetElement(int posX, int posY) var el = GetElement(posX, posY); if (ColorType == PixelColorType.Indexed) - el = new ArrangerElement(el.X1, el.Y1, null, 0, _blankIndexedCodec, null); + el = el.WithTarget(null, 0, _blankIndexedCodec, el.Palette); else if (ColorType == PixelColorType.Direct) - el = new ArrangerElement(el.X1, el.Y1, null, 0, _blankDirectCodec, null); + el = el = el.WithTarget(null, 0, _blankDirectCodec, null); SetElement(el, posX, posY); } diff --git a/ImageMagitek/Project/IGameDescriptorReader.cs b/ImageMagitek/Project/Serialization/IGameDescriptorReader.cs similarity index 81% rename from ImageMagitek/Project/IGameDescriptorReader.cs rename to ImageMagitek/Project/Serialization/IGameDescriptorReader.cs index 51192770..cff00af9 100644 --- a/ImageMagitek/Project/IGameDescriptorReader.cs +++ b/ImageMagitek/Project/Serialization/IGameDescriptorReader.cs @@ -1,6 +1,6 @@ using Monaco.PathTree; -namespace ImageMagitek.Project +namespace ImageMagitek.Project.Serialization { public interface IGameDescriptorReader { diff --git a/ImageMagitek/Project/IGameDescriptorWriter.cs b/ImageMagitek/Project/Serialization/IGameDescriptorWriter.cs similarity index 81% rename from ImageMagitek/Project/IGameDescriptorWriter.cs rename to ImageMagitek/Project/Serialization/IGameDescriptorWriter.cs index 6f658bd4..bfcaf7e6 100644 --- a/ImageMagitek/Project/IGameDescriptorWriter.cs +++ b/ImageMagitek/Project/Serialization/IGameDescriptorWriter.cs @@ -1,6 +1,6 @@ using Monaco.PathTree; -namespace ImageMagitek.Project +namespace ImageMagitek.Project.Serialization { public interface IGameDescriptorWriter { diff --git a/ImageMagitek/Project/Serialization/ProjectTreeBuilder.cs b/ImageMagitek/Project/Serialization/ProjectTreeBuilder.cs new file mode 100644 index 00000000..335eb16a --- /dev/null +++ b/ImageMagitek/Project/Serialization/ProjectTreeBuilder.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ImageMagitek.Codec; +using ImageMagitek.Colors; +using Monaco.PathTree; + +namespace ImageMagitek.Project.Serialization +{ + /// + /// Builds a ProjectTree and resolves resources from Serialization Models + /// + class ProjectTreeBuilder + { + public PathTree Tree { get; } = new PathTree(); + private readonly List _globalResources; + private readonly Palette _globalDefaultPalette; + private readonly ICodecFactory _codecFactory; + + public ProjectTreeBuilder(ICodecFactory codecFactory, IEnumerable globalResources) + { + _codecFactory = codecFactory; + _globalResources = globalResources.ToList(); + _globalDefaultPalette = _globalResources.OfType().FirstOrDefault(); + } + + public MagitekResult AddProject(ImageProjectModel projectModel) + { + if (Tree.Root is object) + return new MagitekResult.Failed($"Attempted to add a new project '{projectModel?.Name}' to an existing project"); + + var project = new ImageProject(projectModel.Name); + project.Root = projectModel.Root; + + Tree.Root = new ProjectNode(project.Name, project); + return MagitekResult.SuccessResult; + } + + public MagitekResult AddFolder(ResourceFolderModel folderModel, string parentNodePath) + { + var folder = new ResourceFolder(folderModel.Name); + + var folderNode = new FolderNode(folder.Name, folder); + Tree.TryGetNode(parentNodePath, out var parentNode); + parentNode.AttachChild(folderNode); + + return MagitekResult.SuccessResult; + } + + public MagitekResult AddDataFile(DataFileModel dfModel, string parentNodePath) + { + var df = new DataFile(dfModel.Name, dfModel.Location); + + var dfNode = new DataFileNode(df.Name, df); + Tree.TryGetNode(parentNodePath, out var parentNode); + parentNode.AttachChild(dfNode); + + if (!File.Exists(df.Location)) + return new MagitekResult.Failed($"DataFile '{df.Name}' does not exist at location '{df.Location}'"); + + return MagitekResult.SuccessResult; + } + + public MagitekResult AddPalette(PaletteModel paletteModel, string parentNodePath) + { + var pal = new Palette(paletteModel.Name, paletteModel.ColorModel, paletteModel.FileAddress, paletteModel.Entries, paletteModel.ZeroIndexTransparent, paletteModel.StorageSource); + + if (!Tree.TryGetValue(paletteModel.DataFileKey, out var df)) + return new MagitekResult.Failed($"Palette '{pal.Name}' could not locate DataFile with key '{paletteModel.DataFileKey}'"); + + pal.DataFile = df; + pal.LazyLoadPalette(pal.DataFile, pal.FileAddress, pal.ColorModel, pal.ZeroIndexTransparent, pal.Entries); + + var palNode = new PaletteNode(pal.Name, pal); + Tree.TryGetNode(parentNodePath, out var parentNode); + parentNode.AttachChild(palNode); + + return MagitekResult.SuccessResult; + } + + public MagitekResult AddScatteredArranger(ScatteredArrangerModel arrangerModel, string parentNodePath) + { + var arranger = new ScatteredArranger(arrangerModel.Name, arrangerModel.ColorType, arrangerModel.Layout, + arrangerModel.ArrangerElementSize.Width, arrangerModel.ArrangerElementSize.Height, arrangerModel.ElementPixelSize.Width, arrangerModel.ElementPixelSize.Height); + + for (int x = 0; x < arrangerModel.ElementGrid.GetLength(0); x++) + { + for (int y = 0; y < arrangerModel.ElementGrid.GetLength(1); y++) + { + var result = CreateElement(arrangerModel, x, y); + + if (result.IsT0) + { + arranger.SetElement(result.AsT0.Result, x, y); + } + else if (result.IsT1) + { + return new MagitekResult.Failed(result.AsT1.Reason); + } + } + } + + var arrangerNode = new ArrangerNode(arranger.Name, arranger); + Tree.TryGetNode(parentNodePath, out var parentNode); + parentNode.AttachChild(arrangerNode); + + return MagitekResult.SuccessResult; + } + + /// + /// Resolves a palette resource using the supplied project tree and falls back to a default palette if available + /// + /// + /// + /// + private Palette ResolvePalette(string paletteKey) + { + var pal = _globalDefaultPalette; + + if (string.IsNullOrEmpty(paletteKey)) + { + return _globalDefaultPalette; + } + else if (!Tree.TryGetValue(paletteKey, out pal)) + { + var name = paletteKey.Split(Tree.PathSeparators).Last(); + pal = _globalResources.OfType().FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); + } + + return pal; + } + + private MagitekResult CreateElement(ScatteredArrangerModel arrangerModel, int x, int y) + { + var elementModel = arrangerModel.ElementGrid[x, y]; + IGraphicsCodec codec = default; + Palette palette = default; + DataFile df = default; + FileBitAddress address = 0; + + if (elementModel is null) + { + if (arrangerModel.ColorType == PixelColorType.Indexed) + { + codec = new BlankIndexedCodec(); + palette = _globalDefaultPalette; + } + else if (arrangerModel.ColorType == PixelColorType.Direct) + { + codec = new BlankDirectCodec(); + } + } + else if (arrangerModel.ColorType == PixelColorType.Indexed) + { + if (!string.IsNullOrWhiteSpace(arrangerModel.ElementGrid[x, y].DataFileKey)) + Tree.TryGetValue(arrangerModel.ElementGrid[x, y].DataFileKey, out df); + + address = elementModel.FileAddress; + var paletteKey = elementModel.PaletteKey; + palette = ResolvePalette(paletteKey); + + if (palette is null) + { + return new MagitekResult.Failed($"Could not resolve palette '{paletteKey}' referenced by arranger '{arrangerModel.Name}'"); + } + + codec = _codecFactory.GetCodec(arrangerModel.ElementGrid[x, y].CodecName, arrangerModel.ElementPixelSize.Width, arrangerModel.ElementPixelSize.Height); + } + else if (arrangerModel.ColorType == PixelColorType.Direct) + { + if (!string.IsNullOrWhiteSpace(arrangerModel.ElementGrid[x, y].DataFileKey)) + Tree.TryGetValue(arrangerModel.ElementGrid[x, y].DataFileKey, out df); + + address = elementModel.FileAddress; + codec = _codecFactory.GetCodec(arrangerModel.ElementGrid[x, y].CodecName, arrangerModel.ElementPixelSize.Width, arrangerModel.ElementPixelSize.Height); + } + else + { + throw new InvalidOperationException($"{nameof(CreateElement)}: Arranger '{arrangerModel.Name}' has invalid {nameof(PixelColorType)} '{arrangerModel.ColorType}'"); + } + + var pixelX = x * arrangerModel.ElementPixelSize.Width; + var pixelY = y * arrangerModel.ElementPixelSize.Height; + var el = new ArrangerElement(pixelX, pixelY, df, address, codec, palette); + return new MagitekResult.Success(el); + } + } +} diff --git a/ImageMagitek/Project/XmlGameDescriptorReader.cs b/ImageMagitek/Project/Serialization/XmlGameDescriptorReader.cs similarity index 64% rename from ImageMagitek/Project/XmlGameDescriptorReader.cs rename to ImageMagitek/Project/Serialization/XmlGameDescriptorReader.cs index 69ba9e87..840ee2d0 100644 --- a/ImageMagitek/Project/XmlGameDescriptorReader.cs +++ b/ImageMagitek/Project/Serialization/XmlGameDescriptorReader.cs @@ -3,7 +3,6 @@ using System.Xml; using System.Xml.Linq; using System.Linq; -using ImageMagitek.Project.SerializationModels; using ImageMagitek.ExtensionMethods; using System.Drawing; using Monaco.PathTree; @@ -12,7 +11,7 @@ using System.Xml.Schema; using System.Collections.Generic; -namespace ImageMagitek.Project +namespace ImageMagitek.Project.Serialization { public class XmlGameDescriptorReader : IGameDescriptorReader { @@ -21,6 +20,7 @@ public class XmlGameDescriptorReader : IGameDescriptorReader private readonly XmlSchemaSet _schemaSet; private readonly ICodecFactory _codecFactory; private readonly List _globalResources; + private readonly Palette _globalDefaultPalette; private string _baseDirectory; public XmlGameDescriptorReader(ICodecFactory codecFactory) : @@ -38,6 +38,7 @@ public XmlGameDescriptorReader(XmlSchemaSet schemaSet, ICodecFactory codecFactor _schemaSet = schemaSet; _codecFactory = codecFactory; _globalResources = globalResources.ToList(); + _globalDefaultPalette = _globalResources.OfType().First(); } public MagitekResults ReadProject(string projectFileName) @@ -66,110 +67,41 @@ public MagitekResults ReadProject(string projectFileName) XElement projectNode = doc.Element("gdf").Element("project"); var projectErrors = new List(); - var projectResource = DeserializeImageProject(projectNode).ToImageProject(); - var tree = new PathTree(new ProjectNode(projectResource.Name, projectResource)); + var projectModel = DeserializeImageProject(projectNode); + var builder = new ProjectTreeBuilder(_codecFactory, _globalResources); + builder.AddProject(projectModel); foreach (var node in projectNode.Descendants("folder")) { - var res = DeserializeResourceFolder(node).ToResourceFolder(); - var path = Path.Combine(node.NodePath(), node.Attribute("name").Value); - - var folderNode = new FolderNode(res.Name, res); - tree.TryGetNode(node.NodePath(), out var parentNode); - parentNode.AttachChild(folderNode); + var folderModel = DeserializeResourceFolder(node); + builder.AddFolder(folderModel, node.NodePath()); } foreach (var node in projectNode.Descendants("datafile")) { - var res = DeserializeDataFile(node).ToDataFile(); - - var dfNode = new DataFileNode(res.Name, res); - tree.TryGetNode(node.NodePath(), out var parentNode); - parentNode.AttachChild(dfNode); + var dfModel = DeserializeDataFile(node); + builder.AddDataFile(dfModel, node.NodePath()); } foreach (var node in projectNode.Descendants("palette")) { - var model = DeserializePalette(node); - var pal = model.ToPalette(); - tree.TryGetValue(model.DataFileKey, out var df); - pal.DataFile = df; - pal.LazyLoadPalette(pal.DataFile, pal.FileAddress, pal.ColorModel, pal.ZeroIndexTransparent, pal.Entries); - - var palNode = new PaletteNode(pal.Name, pal); - tree.TryGetNode(node.NodePath(), out var parentNode); - parentNode.AttachChild(palNode); + var paletteModel = DeserializePalette(node); + builder.AddPalette(paletteModel, node.NodePath()); } foreach (var node in projectNode.Descendants("arranger")) { - var modelArranger = DeserializeScatteredArranger(node); - var arranger = modelArranger.ToScatteredArranger(); - - for (int x = 0; x < arranger.ArrangerElementSize.Width; x++) - { - for (int y = 0; y < arranger.ArrangerElementSize.Height; y++) - { - if (modelArranger.ElementGrid[x, y] is null) - { - var el = new ArrangerElement(x * arranger.ArrangerElementSize.Width, y * arranger.ArrangerElementSize.Height, - null, 0, new BlankIndexedCodec(), ResolvePalette(tree, null)); - arranger.SetElement(el, x, y); - continue; - } - - DataFile df = default; - if (!string.IsNullOrWhiteSpace(modelArranger.ElementGrid[x, y].DataFileKey)) - tree.TryGetValue(modelArranger.ElementGrid[x, y].DataFileKey, out df); - - var paletteKey = modelArranger.ElementGrid[x, y].PaletteKey; - Palette pal = ResolvePalette(tree, paletteKey); - if (pal is null) - { - projectErrors.Add($"Could not resolve palette '{paletteKey}' referenced by arranger '{arranger.Name}'"); - continue; - } - - var element = arranger.GetElement(x, y); - - var codec = _codecFactory.GetCodec(modelArranger.ElementGrid[x, y].CodecName, arranger.ElementPixelSize.Width, arranger.ElementPixelSize.Height); - element = element.WithTarget(df, element.FileAddress, codec, pal); - arranger.SetElement(element, x, y); - } - } - - var arrangerNode = new ArrangerNode(arranger.Name, arranger); - tree.TryGetNode(node.NodePath(), out var parentNode); - parentNode.AttachChild(arrangerNode); + var arrangerModel = DeserializeScatteredArranger(node); + builder.AddScatteredArranger(arrangerModel, node.NodePath()); } if (projectErrors.Count > 0) return new MagitekResults.Failed(projectErrors); - return new MagitekResults.Success(new ProjectTree(tree, projectFileName)); - } - - /// - /// Resolves a palette resource using the supplied project tree and falls back to a default palette if available - /// - /// - /// - /// - private Palette ResolvePalette(PathTree tree, string paletteKey) - { - Palette pal = default; - - if (string.IsNullOrEmpty(paletteKey)) - { - pal = _globalResources.OfType().FirstOrDefault(); - } - else if (!tree.TryGetValue(paletteKey, out pal)) - { - var name = paletteKey.Split(tree.PathSeparators).Last(); - pal = _globalResources.OfType().FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)); - } + var tree = builder.Tree; + var projectTree = new ProjectTree(tree, projectFileName); - return pal; + return new MagitekResults.Success(projectTree); } private ImageProjectModel DeserializeImageProject(XElement element) diff --git a/ImageMagitek/Project/XmlGameDescriptorWriter.cs b/ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs similarity index 99% rename from ImageMagitek/Project/XmlGameDescriptorWriter.cs rename to ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs index 1f7b1ad7..ec2ac8f9 100644 --- a/ImageMagitek/Project/XmlGameDescriptorWriter.cs +++ b/ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs @@ -4,11 +4,10 @@ using System.Linq; using System.Xml; using System.Xml.Linq; -using ImageMagitek.Project.SerializationModels; using ImageMagitek.Colors; using Monaco.PathTree; -namespace ImageMagitek.Project +namespace ImageMagitek.Project.Serialization { public class XmlGameDescriptorWriter : IGameDescriptorWriter { diff --git a/ImageMagitek/Project/SerializationModels/ArrangerElementModel.cs b/ImageMagitek/Project/SerializationModels/ArrangerElementModel.cs index b433d985..6cd12e44 100644 --- a/ImageMagitek/Project/SerializationModels/ArrangerElementModel.cs +++ b/ImageMagitek/Project/SerializationModels/ArrangerElementModel.cs @@ -1,4 +1,4 @@ -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { internal class ArrangerElementModel { @@ -11,9 +11,6 @@ internal class ArrangerElementModel public bool UsesGlobalDefaultPalette { get; set; } - public ArrangerElement ToArrangerElement() => - new ArrangerElement(PositionX, PositionY, null, FileAddress, null, null); - public static ArrangerElementModel FromArrangerElement(in ArrangerElement el) { return new ArrangerElementModel() diff --git a/ImageMagitek/Project/SerializationModels/DataFileModel.cs b/ImageMagitek/Project/SerializationModels/DataFileModel.cs index a1b8e8c1..64b97dfa 100644 --- a/ImageMagitek/Project/SerializationModels/DataFileModel.cs +++ b/ImageMagitek/Project/SerializationModels/DataFileModel.cs @@ -1,4 +1,4 @@ -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { internal class DataFileModel : ProjectNodeModel { diff --git a/ImageMagitek/Project/SerializationModels/ImageProjectModel.cs b/ImageMagitek/Project/SerializationModels/ImageProjectModel.cs index 6478f326..2cc7af62 100644 --- a/ImageMagitek/Project/SerializationModels/ImageProjectModel.cs +++ b/ImageMagitek/Project/SerializationModels/ImageProjectModel.cs @@ -1,4 +1,4 @@ -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { internal class ImageProjectModel : ProjectNodeModel { diff --git a/ImageMagitek/Project/SerializationModels/PaletteModel.cs b/ImageMagitek/Project/SerializationModels/PaletteModel.cs index 31726556..946c9eea 100644 --- a/ImageMagitek/Project/SerializationModels/PaletteModel.cs +++ b/ImageMagitek/Project/SerializationModels/PaletteModel.cs @@ -1,6 +1,6 @@ using ImageMagitek.Colors; -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { internal class PaletteModel : ProjectNodeModel { @@ -11,8 +11,6 @@ internal class PaletteModel : ProjectNodeModel public bool ZeroIndexTransparent { get; set; } public PaletteStorageSource StorageSource { get; set; } - public Palette ToPalette() => new Palette(Name, ColorModel, FileAddress, Entries, ZeroIndexTransparent, StorageSource); - public static PaletteModel FromPalette(Palette pal) { return new PaletteModel() diff --git a/ImageMagitek/Project/SerializationModels/ProjectNodeModel.cs b/ImageMagitek/Project/SerializationModels/ProjectNodeModel.cs index cee29629..de72e803 100644 --- a/ImageMagitek/Project/SerializationModels/ProjectNodeModel.cs +++ b/ImageMagitek/Project/SerializationModels/ProjectNodeModel.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { public abstract class ProjectNodeModel { diff --git a/ImageMagitek/Project/SerializationModels/ResourceFolderModel.cs b/ImageMagitek/Project/SerializationModels/ResourceFolderModel.cs index 0e39f97e..3e7f3a4c 100644 --- a/ImageMagitek/Project/SerializationModels/ResourceFolderModel.cs +++ b/ImageMagitek/Project/SerializationModels/ResourceFolderModel.cs @@ -1,4 +1,4 @@ -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { internal class ResourceFolderModel : ProjectNodeModel { diff --git a/ImageMagitek/Project/SerializationModels/ScatteredArrangerModel.cs b/ImageMagitek/Project/SerializationModels/ScatteredArrangerModel.cs index dc3b4069..7d0bfa21 100644 --- a/ImageMagitek/Project/SerializationModels/ScatteredArrangerModel.cs +++ b/ImageMagitek/Project/SerializationModels/ScatteredArrangerModel.cs @@ -5,7 +5,7 @@ using MoreLinq; using System.Collections.Generic; -namespace ImageMagitek.Project.SerializationModels +namespace ImageMagitek.Project.Serialization { internal class ScatteredArrangerModel : ProjectNodeModel { @@ -15,22 +15,6 @@ internal class ScatteredArrangerModel : ProjectNodeModel public ArrangerLayout Layout { get; set; } public PixelColorType ColorType { get; set; } - public ScatteredArranger ToScatteredArranger() - { - var arr = new ScatteredArranger(Name, ColorType, Layout, ArrangerElementSize.Width, ArrangerElementSize.Height, ElementPixelSize.Width, ElementPixelSize.Height); - - for(int x = 0; x < ElementGrid.GetLength(0); x++) - { - for (int y = 0; y < ElementGrid.GetLength(1); y++) - { - if (ElementGrid[x, y] is ArrangerElementModel model) - arr.SetElement(model.ToArrangerElement(), x, y); - } - } - - return arr; - } - public static ScatteredArrangerModel FromScatteredArranger(ScatteredArranger arranger) { var model = new ScatteredArrangerModel() diff --git a/ImageMagitek/ScatteredArranger.cs b/ImageMagitek/ScatteredArranger.cs index 116769e0..fe31a86d 100644 --- a/ImageMagitek/ScatteredArranger.cs +++ b/ImageMagitek/ScatteredArranger.cs @@ -50,7 +50,7 @@ public ScatteredArranger(string name, PixelColorType colorType, ArrangerLayout l for (int posX = 0; posX < arrangerWidth; posX++) { var el = new ArrangerElement(elX, elY, null, 0, codec, null); - SetElement(el, posX, posY); + ElementGrid[posX, posY] = el; elX += elementWidth; } @@ -64,6 +64,28 @@ public ScatteredArranger(string name, PixelColorType colorType, ArrangerLayout l /// Width of Arranger in Elements /// Height of Arranger in Elements public override void Resize(int arrangerWidth, int arrangerHeight) + { + int Width = ElementPixelSize.Width; + int Height = ElementPixelSize.Height; + + Resize(arrangerWidth, arrangerHeight, (x, y) => + { + IGraphicsCodec codec = null; + if (ColorType == PixelColorType.Direct) + codec = new BlankDirectCodec(); + else if (ColorType == PixelColorType.Indexed) + codec = new BlankIndexedCodec(); + return new ArrangerElement(x * Width, y * Height, null, 0, codec, null); + }); + } + + /// + /// Resizes a ScatteredArranger to the specified number of elements and default initializes any new elements + /// + /// Width of Arranger in Elements + /// Height of Arranger in Elements + /// Method to create new elements with given the x and y positions in element coordinates + public override void Resize(int arrangerWidth, int arrangerHeight, Func elementFactory) { if (arrangerWidth < 1 || arrangerHeight < 1) throw new ArgumentOutOfRangeException($"{nameof(Resize)}: {nameof(arrangerWidth)} ({arrangerWidth}) and {nameof(arrangerHeight)} ({arrangerHeight}) must be larger than 0"); @@ -71,7 +93,7 @@ public override void Resize(int arrangerWidth, int arrangerHeight) if (Mode != ArrangerMode.Scattered) throw new InvalidOperationException($"{nameof(Resize)} property '{nameof(Mode)}' is in invalid {nameof(ArrangerMode)} ({Mode.ToString()})"); - ArrangerElement[,] newList = new ArrangerElement[arrangerWidth, arrangerHeight]; + ArrangerElement[,] newGrid = new ArrangerElement[arrangerWidth, arrangerHeight]; int xCopy = Math.Min(arrangerWidth, ArrangerElementSize.Width); int yCopy = Math.Min(arrangerHeight, ArrangerElementSize.Height); @@ -82,23 +104,18 @@ public override void Resize(int arrangerWidth, int arrangerHeight) { for (int posX = 0; posX < arrangerWidth; posX++) { - if ((posY < ArrangerElementSize.Height) && (posX < ArrangerElementSize.Width)) // Copy from old arranger - newList[posX, posY] = ElementGrid[posX, posY]; + if ((posY < ArrangerElementSize.Height) && (posX < ArrangerElementSize.Width)) // Copy from old grid + { + newGrid[posX, posY] = ElementGrid[posX, posY]; + } else // Create new blank element { - IGraphicsCodec codec = null; - if (ColorType == PixelColorType.Direct) - codec = new BlankDirectCodec(); - else if (ColorType == PixelColorType.Indexed) - codec = new BlankIndexedCodec(); - - ArrangerElement el = new ArrangerElement(posX * Width, posY * Height, null, 0, codec, null); - newList[posX, posY] = el; + newGrid[posX, posY] = elementFactory(posX, posY); } } } - ElementGrid = newList; + ElementGrid = newGrid; ArrangerElementSize = new Size(arrangerWidth, arrangerHeight); } diff --git a/ImageMagitek/SequentialArranger.cs b/ImageMagitek/SequentialArranger.cs index 51eef4ad..bf7d8739 100644 --- a/ImageMagitek/SequentialArranger.cs +++ b/ImageMagitek/SequentialArranger.cs @@ -35,6 +35,11 @@ public class SequentialArranger : Arranger /// public DataFile ActiveDataFile { get; protected set; } + /// + /// Palette that is assigned to each ArrangerElement + /// + public Palette ActivePalette { get; protected set; } + private ICodecFactory _codecs; /// @@ -43,15 +48,16 @@ public class SequentialArranger : Arranger /// Width of arranger in elements /// Height of arranger in elements /// DataFile assigned to each Element - /// /// Palette assigned to each Element + /// Palette assigned to each Element /// Factory responsible for creating new codecs /// Name of codec each Element will be initialized to - public SequentialArranger(int arrangerWidth, int arrangerHeight, DataFile dataFile, Palette pal, ICodecFactory codecFactory, string codecName) + public SequentialArranger(int arrangerWidth, int arrangerHeight, DataFile dataFile, Palette palette, ICodecFactory codecFactory, string codecName) { Mode = ArrangerMode.Sequential; FileSize = dataFile.Stream.Length; Name = dataFile.Name; ActiveDataFile = dataFile; + ActivePalette = palette; _codecs = codecFactory; ActiveCodec = _codecs.GetCodec(codecName); @@ -59,7 +65,7 @@ public SequentialArranger(int arrangerWidth, int arrangerHeight, DataFile dataFi ElementPixelSize = new Size(ActiveCodec.Width, ActiveCodec.Height); - Resize(arrangerWidth, arrangerHeight, dataFile, pal); + Resize(arrangerWidth, arrangerHeight); } /// @@ -112,18 +118,29 @@ public override void Resize(int arrangerWidth, int arrangerHeight) if (Mode != ArrangerMode.Sequential) throw new InvalidOperationException($"{nameof(Resize)} property '{nameof(Mode)}' is in invalid {nameof(ArrangerMode)} ({Mode.ToString()})"); - Resize(arrangerWidth, arrangerHeight, ElementGrid[0, 0].DataFile, ElementGrid[0, 0].Palette); + var address = FileAddress; + Resize(arrangerWidth, arrangerHeight, (x, y) => + { + var el = new ArrangerElement(x * ElementPixelSize.Width, y * ElementPixelSize.Height, ActiveDataFile, address, ActiveCodec, ActivePalette); + + if (el.Codec.Layout == ImageLayout.Tiled) + address += ActiveCodec.StorageSize; + else if (el.Codec.Layout == ImageLayout.Single) + address += (ElementPixelSize.Width + ActiveCodec.RowStride) * ActiveCodec.ColorDepth / 4; // TODO: Fix sequential arranger offsets to be bit-wise + else + throw new NotSupportedException(); + + return el; + }); } /// - /// Resizes a Sequential Arranger with a new number of elements + /// Resizes a SequentialArranger to the specified number of elements and default initializes any new elements /// /// Width of Arranger in Elements /// Height of Arranger in Elements - /// DataFile key in IResourceManager - /// Codec name for encoding/decoding Elements - /// - private FileBitAddress Resize(int arrangerWidth, int arrangerHeight, DataFile dataFile, Palette pal) + /// Method to create new elements with given the x and y positions in element coordinates + public override void Resize(int arrangerWidth, int arrangerHeight, Func elementFactory) { if (Mode != ArrangerMode.Sequential) throw new InvalidOperationException($"{nameof(Resize)} property '{nameof(Mode)}' is in invalid {nameof(ArrangerMode)} ({Mode.ToString()})"); @@ -153,33 +170,17 @@ private FileBitAddress Resize(int arrangerWidth, int arrangerHeight, DataFile da ArrangerElementSize = new Size(arrangerWidth, arrangerHeight); ArrangerBitSize = arrangerWidth * arrangerHeight * ActiveCodec.StorageSize; - int elY = 0; - for (int posY = 0; posY < arrangerHeight; posY++) { - int elX = 0; for (int posX = 0; posX < arrangerWidth; posX++) { - ArrangerElement el = new ArrangerElement(elX, elY, dataFile, address, _codecs.CloneCodec(ActiveCodec), pal); - + var el = elementFactory(posX, posY); SetElement(el, posX, posY); - - if (el.Codec.Layout == ImageLayout.Tiled) - address += ActiveCodec.StorageSize; - else if (el.Codec.Layout == ImageLayout.Single) - address += (ElementPixelSize.Width + ActiveCodec.RowStride) * ActiveCodec.ColorDepth / 4; // TODO: Fix sequential arranger offsets to be bit-wise - else - throw new NotSupportedException(); - - elX += ElementPixelSize.Width; } - elY += ElementPixelSize.Height; } address = GetInitialSequentialFileAddress(); - address = this.Move(address); - - return address; + address = Move(address); } /// @@ -266,6 +267,7 @@ public void ChangeCodec(IGraphicsCodec codec, int arrangerWidth, int arrangerHei /// New palette public void ChangePalette(Palette pal) { + ActivePalette = pal; for (int y = 0; y < ArrangerElementSize.Height; y++) { for (int x = 0; x < ArrangerElementSize.Width; x++) diff --git a/TileShop.WPF/ViewExtenders/Imaging/IndexedBitmapAdapter.cs b/TileShop.WPF/ViewExtenders/Imaging/IndexedBitmapAdapter.cs index 9cdee7c2..97a832b8 100644 --- a/TileShop.WPF/ViewExtenders/Imaging/IndexedBitmapAdapter.cs +++ b/TileShop.WPF/ViewExtenders/Imaging/IndexedBitmapAdapter.cs @@ -2,6 +2,8 @@ using System.Drawing; using System.Windows.Media.Imaging; using ImageMagitek; +using ImageMagitek.Codec; +using ImageMagitek.Colors; namespace TileShop.WPF.Imaging { @@ -103,18 +105,30 @@ protected override void Render(int xStart, int yStart, int width, int height) for (int x = xStart; x < xStart + width; x++) { - var pal = Image.GetElementAtPixel(x + Left, y + Top).Palette; - var index = row[x + Left]; - var color = pal[index]; - - dest[x * 4] = color.B; - dest[x * 4 + 1] = color.G; - dest[x * 4 + 2] = color.R; - - if (index == 0 && pal.ZeroIndexTransparent) + var el = Image.GetElementAtPixel(x + Left, y + Top); + var pal = el.Palette; + + if (el.Codec is BlankIndexedCodec blankIndexedCodec) + { + dest[x * 4] = 0; + dest[x * 4 + 1] = 0; + dest[x * 4 + 2] = 0; dest[x * 4 + 3] = 0; - else - dest[x * 4 + 3] = color.A; + } + else if (pal is object) + { + var index = row[x + Left]; + var color = pal[index]; + + dest[x * 4] = color.B; + dest[x * 4 + 1] = color.G; + dest[x * 4 + 2] = color.R; + + if (index == 0 && pal.ZeroIndexTransparent) + dest[x * 4 + 3] = 0; + else + dest[x * 4 + 3] = color.A; + } } } } From 67d19a3367f1c3e0699fe8258afa62820a51558f Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 22:17:12 -0400 Subject: [PATCH 3/8] Widened the Width and Height TextBoxes --- .../Features/Dialogs/ResizeTiledScatteredArrangerView.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TileShop.WPF/Features/Dialogs/ResizeTiledScatteredArrangerView.xaml b/TileShop.WPF/Features/Dialogs/ResizeTiledScatteredArrangerView.xaml index 27033ad1..50b44adf 100644 --- a/TileShop.WPF/Features/Dialogs/ResizeTiledScatteredArrangerView.xaml +++ b/TileShop.WPF/Features/Dialogs/ResizeTiledScatteredArrangerView.xaml @@ -25,11 +25,11 @@ HorizontalAlignment="Center" Orientation="Horizontal"> From e3818f3fc64c3132dd34354dbf1705b3101c9f26 Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 22:35:28 -0400 Subject: [PATCH 4/8] Small XML optimization with fallback of defaultpalette with resized arrangers with many blank elements --- ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs b/ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs index ec2ac8f9..3dc97cd5 100644 --- a/ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs +++ b/ImageMagitek/Project/Serialization/XmlGameDescriptorWriter.cs @@ -225,6 +225,7 @@ private XElement Serialize(ScatteredArrangerModel arrangerModel) var mostUsedNonDefaultPalette = arrangerModel.EnumerateElements() .Where(x => !x.UsesGlobalDefaultPalette) + .Where(x => !string.IsNullOrEmpty(x.DataFileKey)) .GroupBy(x => x.PaletteKey) .Select(x => new { PaletteKey = x.Key, Count = x.Count() }) .OrderByDescending(x => x.Count) @@ -245,7 +246,6 @@ private XElement Serialize(ScatteredArrangerModel arrangerModel) mostUsedPaletteKey = mostUsedNonDefaultPalette.PaletteKey; } - var arrangerNode = new XElement("arranger"); arrangerNode.Add(new XAttribute("name", arrangerModel.Name)); arrangerNode.Add(new XAttribute("elementsx", arrangerModel.ArrangerElementSize.Width)); From 193435e659c59f0e5a3da97b2924d98c46e2258d Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 23:23:57 -0400 Subject: [PATCH 5/8] Updated --- ImageMagitek/_xmlprojectsamples/ctbossx.xml | 28 +- .../_xmlprojectsamples/ff2extended.xml | 4492 ++++++++--------- 2 files changed, 2260 insertions(+), 2260 deletions(-) diff --git a/ImageMagitek/_xmlprojectsamples/ctbossx.xml b/ImageMagitek/_xmlprojectsamples/ctbossx.xml index c1221f1d..0f4da364 100644 --- a/ImageMagitek/_xmlprojectsamples/ctbossx.xml +++ b/ImageMagitek/_xmlprojectsamples/ctbossx.xml @@ -1,32 +1,32 @@  - - - - - - - - - + - + - + - + - + - + + + + + + + + + \ No newline at end of file diff --git a/ImageMagitek/_xmlprojectsamples/ff2extended.xml b/ImageMagitek/_xmlprojectsamples/ff2extended.xml index 214034ed..dbb44682 100644 --- a/ImageMagitek/_xmlprojectsamples/ff2extended.xml +++ b/ImageMagitek/_xmlprojectsamples/ff2extended.xml @@ -1,971 +1,1198 @@  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1094,14 +1321,14 @@ - - - - - - - - + + + + + + + + @@ -1110,14 +1337,14 @@ - - - - - - - - + + + + + + + + @@ -1126,14 +1353,14 @@ - - - - - - - - + + + + + + + + @@ -1142,14 +1369,14 @@ - - - - - - - - + + + + + + + + @@ -1222,14 +1449,14 @@ - - - - - - - - + + + + + + + + @@ -1238,14 +1465,14 @@ - - - - - - - - + + + + + + + + @@ -1254,14 +1481,14 @@ - - - - - - - - + + + + + + + + @@ -1270,14 +1497,14 @@ - - - - - - - - + + + + + + + + @@ -1286,512 +1513,512 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1863,7 +2090,7 @@ - + @@ -1873,7 +2100,7 @@ - + @@ -2019,7 +2246,7 @@ - + @@ -2705,7 +2932,7 @@ - + @@ -3236,7 +3463,6 @@ - @@ -3264,15 +3490,15 @@ - - - - - - - - - + + + + + + + + + @@ -3283,24 +3509,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3311,24 +3537,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3339,24 +3565,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3367,24 +3593,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3395,24 +3621,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3423,24 +3649,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3451,24 +3677,24 @@ - - - - + + + + - - - - - - - - + + + + + + + + @@ -3479,915 +3705,689 @@ - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - + + - - - - - - + + + + + + - - + + - - - - + + + + - - + + - - - - - - + + + + + + - - + + - - - - + + + + - - + + - - - - - - + + + + + + - - + + - - - - + + + + - - + + - - - - - - + + + + + + - - + + - - - - + + + + - - + + - - - - - - + + + + + + - - + + - - - - + + + + - - + + - - - - - - + + + + + + - - + + - - - - + + + + - - + + - - - - - - + + + + + + - - + + - - + + - - + + - - - - - - + + + + + + - - + + - - + + - - - - + + + + - - + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - + + - - + + - - - - - - - - + + + + + + + + - - - - + + + + - - + + - - + + - - - - - - + + + + + + - - - - + + + + - - + + - - + + - - - - - - + + + + + + - - - - + + + + - - + + - - + + - - - - - - + + + + + + - - - - + + + + - - + + - - + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From 8900f4d148e8790e5c585607ab825c2012ef673b Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 23:24:39 -0400 Subject: [PATCH 6/8] Apply palette can now apply palettes to a selection --- .../ScatteredArrangerEditorViewModel.cs | 88 +++++++++++++++---- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/TileShop.WPF/Features/Arranger Editors/ScatteredArrangerEditorViewModel.cs b/TileShop.WPF/Features/Arranger Editors/ScatteredArrangerEditorViewModel.cs index a06eb1dc..bd8f1d8d 100644 --- a/TileShop.WPF/Features/Arranger Editors/ScatteredArrangerEditorViewModel.cs +++ b/TileShop.WPF/Features/Arranger Editors/ScatteredArrangerEditorViewModel.cs @@ -42,7 +42,7 @@ public ScatteredArrangerTool ActiveTool get => _activeTool; set { - if (value != ScatteredArrangerTool.Select) + if (value != ScatteredArrangerTool.Select && value != ScatteredArrangerTool.ApplyPalette) CancelOverlay(); SetAndNotify(ref _activeTool, value); } @@ -136,7 +136,27 @@ public override void OnMouseMove(object sender, MouseCaptureArgs e) int y = Math.Clamp((int)e.Y / Zoom, 0, _workingArranger.ArrangerPixelSize.Height - 1); if (ActiveTool == ScatteredArrangerTool.ApplyPalette && e.LeftButton) - TryApplyPalette(x, y, SelectedPalette.Palette); + { + if (Overlay.SelectionRect.ContainsPointSnapped(x, y)) + { + int top = Overlay.SelectionRect.SnappedTop / _workingArranger.ElementPixelSize.Height; + int bottom = Overlay.SelectionRect.SnappedBottom / _workingArranger.ElementPixelSize.Height; + int left = Overlay.SelectionRect.SnappedLeft / _workingArranger.ElementPixelSize.Width; + int right = Overlay.SelectionRect.SnappedRight / _workingArranger.ElementPixelSize.Width; + + for (int posY = top; posY < bottom; posY++) + { + for (int posX = left; posX < right; posX++) + { + TryApplyPalette(posX * _workingArranger.ElementPixelSize.Width, posY * _workingArranger.ElementPixelSize.Height, SelectedPalette.Palette); + } + } + } + else + { + TryApplyPalette(x, y, SelectedPalette.Palette); + } + } else if (ActiveTool == ScatteredArrangerTool.InspectElement) { var elX = x / _workingArranger.ElementPixelSize.Width; @@ -148,7 +168,9 @@ public override void OnMouseMove(object sender, MouseCaptureArgs e) _events.PublishOnUIThread(notifyEvent); } else if (ActiveTool == ScatteredArrangerTool.Select) + { base.OnMouseMove(sender, e); + } } public override void DragOver(IDropInfo dropInfo) @@ -215,24 +237,58 @@ protected override void Render() private void TryApplyPalette(int pixelX, int pixelY, Palette palette) { - if (pixelX >= _workingArranger.ArrangerPixelSize.Width || pixelY >= _workingArranger.ArrangerPixelSize.Height) - return; + bool needsRender = false; + if (Overlay.State == OverlayState.Selected && Overlay.SelectionRect.ContainsPointSnapped(pixelX, pixelY)) + { + int top = Overlay.SelectionRect.SnappedTop / _workingArranger.ElementPixelSize.Height; + int bottom = Overlay.SelectionRect.SnappedBottom / _workingArranger.ElementPixelSize.Height; + int left = Overlay.SelectionRect.SnappedLeft / _workingArranger.ElementPixelSize.Width; + int right = Overlay.SelectionRect.SnappedRight / _workingArranger.ElementPixelSize.Width; - var el = _workingArranger.GetElementAtPixel(pixelX, pixelY); + for (int posY = top; posY < bottom; posY++) + { + for (int posX = left; posX < right; posX++) + { + if (TryApplySinglePalette(posX * _workingArranger.ElementPixelSize.Width, posY * _workingArranger.ElementPixelSize.Height, SelectedPalette.Palette, false)) + needsRender = true; + } + } + } + else + { + if (TryApplySinglePalette(pixelX, pixelY, palette, true)) + needsRender = true; + } - if (ReferenceEquals(palette, el.Palette)) - return; + if (needsRender) + Render(); - var result = _indexedImage.TrySetPalette(pixelX, pixelY, palette); + bool TryApplySinglePalette(int pixelX, int pixelY, Palette palette, bool notify) + { + if (pixelX >= _workingArranger.ArrangerPixelSize.Width || pixelY >= _workingArranger.ArrangerPixelSize.Height) + return false; - result.Switch( - success => - { - Render(); - IsModified = true; - }, - fail => _events.PublishOnUIThread(new NotifyOperationEvent(fail.Reason)) - ); + var el = _workingArranger.GetElementAtPixel(pixelX, pixelY); + + if (ReferenceEquals(palette, el.Palette)) + return false; + + var result = _indexedImage.TrySetPalette(pixelX, pixelY, palette); + + return result.Match( + success => + { + Render(); + IsModified = true; + return true; + }, + fail => + { + if (notify) + _events.PublishOnUIThread(new NotifyOperationEvent(fail.Reason)); + return false; + }); + } } private bool TryPickPalette(int pixelX, int pixelY) From 730597c646655fd31b729e385da04f53d65204d1 Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 23:29:10 -0400 Subject: [PATCH 7/8] Fixed Jot persistence --- TileShop.WPF/Features/Shell/ShellViewModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TileShop.WPF/Features/Shell/ShellViewModel.cs b/TileShop.WPF/Features/Shell/ShellViewModel.cs index b6990297..50d6491b 100644 --- a/TileShop.WPF/Features/Shell/ShellViewModel.cs +++ b/TileShop.WPF/Features/Shell/ShellViewModel.cs @@ -88,6 +88,7 @@ public void Closing(CancelEventArgs e) if (Editors.RequestSaveAllUserChanges()) { _projectService.CloseProjects(); + _tracker.PersistAll(); } else { @@ -147,6 +148,7 @@ public void RequestApplicationExit() if (Editors.RequestSaveAllUserChanges()) { _projectService.CloseProjects(); + _tracker.PersistAll(); Environment.Exit(0); } } From ff142c96e6502399f0669b3e98ac2a0adfd64e2d Mon Sep 17 00:00:00 2001 From: Stephen Monaco Date: Mon, 28 Sep 2020 23:43:34 -0400 Subject: [PATCH 8/8] Version bump. Updated ModernWPF --- TileShop.WPF/Styles/NumberBox.xaml | 4 ++-- TileShop.WPF/TileShop.WPF.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TileShop.WPF/Styles/NumberBox.xaml b/TileShop.WPF/Styles/NumberBox.xaml index 1747a0d4..891657aa 100644 --- a/TileShop.WPF/Styles/NumberBox.xaml +++ b/TileShop.WPF/Styles/NumberBox.xaml @@ -188,7 +188,7 @@ - + @@ -200,7 +200,7 @@ - + diff --git a/TileShop.WPF/TileShop.WPF.csproj b/TileShop.WPF/TileShop.WPF.csproj index 6bd581ca..8e21a4dd 100644 --- a/TileShop.WPF/TileShop.WPF.csproj +++ b/TileShop.WPF/TileShop.WPF.csproj @@ -6,7 +6,7 @@ 8.0 true TileShop - 0.95 + 0.96 Steve Monaco https://github.com/stevemonaco/ImageMagitek https://github.com/stevemonaco/ImageMagitek @@ -28,7 +28,7 @@ - +