2
0
Эх сурвалжийг харах

Merge branch 'master' into release

Krzysztof Krysiński 1 жил өмнө
parent
commit
1a8cae605c

+ 18 - 18
src/ChunkyImageLib/Operations/OperationHelper.cs

@@ -120,25 +120,25 @@ public static class OperationHelper
         => CreateMatrixFromPoints((Point)corners.TopLeft, (Point)corners.TopRight, (Point)corners.BottomRight, (Point)corners.BottomLeft, (float)size.X, (float)size.Y);
         => CreateMatrixFromPoints((Point)corners.TopLeft, (Point)corners.TopRight, (Point)corners.BottomRight, (Point)corners.BottomLeft, (float)size.X, (float)size.Y);
 
 
     // see https://stackoverflow.com/questions/48416118/perspective-transform-in-skia/72364829#72364829
     // see https://stackoverflow.com/questions/48416118/perspective-transform-in-skia/72364829#72364829
-    public static Matrix3X3 CreateMatrixFromPoints(Point topLeft, Point topRight, Point botRight, Point botLeft, float width, float height)
+    public static Matrix3X3 CreateMatrixFromPoints(Point topLeft, Point topRight, Point botRight, Point botLeft, double width, double height)
     {
     {
-        (float x1, float y1) = (topLeft.X, topLeft.Y);
-        (float x2, float y2) = (topRight.X, topRight.Y);
-        (float x3, float y3) = (botRight.X, botRight.Y);
-        (float x4, float y4) = (botLeft.X, botLeft.Y);
-        (float w, float h) = (width, height);
-
-        float scaleX = (y1 * x2 * x4 - x1 * y2 * x4 + x1 * y3 * x4 - x2 * y3 * x4 - y1 * x2 * x3 + x1 * y2 * x3 - x1 * y4 * x3 + x2 * y4 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
-        float skewX = (-x1 * x2 * y3 - y1 * x2 * x4 + x2 * y3 * x4 + x1 * x2 * y4 + x1 * y2 * x3 + y1 * x4 * x3 - y2 * x4 * x3 - x1 * y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
-        float transX = x1;
-        float skewY = (-y1 * x2 * y3 + x1 * y2 * y3 + y1 * y3 * x4 - y2 * y3 * x4 + y1 * x2 * y4 - x1 * y2 * y4 - y1 * y4 * x3 + y2 * y4 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
-        float scaleY = (-y1 * x2 * y3 - y1 * y2 * x4 + y1 * y3 * x4 + x1 * y2 * y4 - x1 * y3 * y4 + x2 * y3 * y4 + y1 * y2 * x3 - y2 * y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
-        float transY = y1;
-        float persp0 = (x1 * y3 - x2 * y3 + y1 * x4 - y2 * x4 - x1 * y4 + x2 * y4 - y1 * x3 + y2 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
-        float persp1 = (-y1 * x2 + x1 * y2 - x1 * y3 - y2 * x4 + y3 * x4 + x2 * y4 + y1 * x3 - y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
-        float persp2 = 1;
-
-        return new Matrix3X3(scaleX, skewX, transX, skewY, scaleY, transY, persp0, persp1, persp2);
+        (double x1, double y1) = (topLeft.X, topLeft.Y);
+        (double x2, double y2) = (topRight.X, topRight.Y);
+        (double x3, double y3) = (botRight.X, botRight.Y);
+        (double x4, double y4) = (botLeft.X, botLeft.Y);
+        (double w, double h) = (width, height);
+
+        double scaleX = (y1 * x2 * x4 - x1 * y2 * x4 + x1 * y3 * x4 - x2 * y3 * x4 - y1 * x2 * x3 + x1 * y2 * x3 - x1 * y4 * x3 + x2 * y4 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
+        double skewX = (-x1 * x2 * y3 - y1 * x2 * x4 + x2 * y3 * x4 + x1 * x2 * y4 + x1 * y2 * x3 + y1 * x4 * x3 - y2 * x4 * x3 - x1 * y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
+        double transX = x1;
+        double skewY = (-y1 * x2 * y3 + x1 * y2 * y3 + y1 * y3 * x4 - y2 * y3 * x4 + y1 * x2 * y4 - x1 * y2 * y4 - y1 * y4 * x3 + y2 * y4 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
+        double scaleY = (-y1 * x2 * y3 - y1 * y2 * x4 + y1 * y3 * x4 + x1 * y2 * y4 - x1 * y3 * y4 + x2 * y3 * y4 + y1 * y2 * x3 - y2 * y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
+        double transY = y1;
+        double persp0 = (x1 * y3 - x2 * y3 + y1 * x4 - y2 * x4 - x1 * y4 + x2 * y4 - y1 * x3 + y2 * x3) / (x2 * y3 * w + y2 * x4 * w - y3 * x4 * w - x2 * y4 * w - y2 * w * x3 + y4 * w * x3);
+        double persp1 = (-y1 * x2 + x1 * y2 - x1 * y3 - y2 * x4 + y3 * x4 + x2 * y4 + y1 * x3 - y4 * x3) / (x2 * y3 * h + y2 * x4 * h - y3 * x4 * h - x2 * y4 * h - y2 * h * x3 + y4 * h * x3);
+        double persp2 = 1;
+
+        return new Matrix3X3((float)scaleX, (float)skewX, (float)transX, (float)skewY, (float)scaleY, (float)transY, (float)persp0, (float)persp1, (float)persp2);
     }
     }
 
 
     public static (ShapeCorners, ShapeCorners) CreateStretchedHexagon(VecD centerPos, double hexagonSide, double stretchX)
     public static (ShapeCorners, ShapeCorners) CreateStretchedHexagon(VecD centerPos, double hexagonSide, double stretchX)

+ 1 - 1
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaPathImplementation.cs

@@ -99,7 +99,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         public RectD GetBounds(VectorPath vectorPath)
         public RectD GetBounds(VectorPath vectorPath)
         {
         {
             SKRect rect = ManagedInstances[vectorPath.ObjectPointer].Bounds;
             SKRect rect = ManagedInstances[vectorPath.ObjectPointer].Bounds;
-            return new RectD(rect.Left, rect.Top, rect.Right, rect.Bottom);
+            return RectD.FromSides(rect.Left, rect.Right, rect.Top, rect.Bottom);
         }
         }
 
 
         public void Reset(VectorPath vectorPath)
         public void Reset(VectorPath vectorPath)

+ 2 - 4
src/PixiEditor/Helpers/Extensions/ServiceCollectionHelpers.cs

@@ -1,5 +1,4 @@
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
-using PixiEditor.Extensions;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.UserPreferences;
 using PixiEditor.Extensions.Common.UserPreferences;
 using PixiEditor.Extensions.Palettes;
 using PixiEditor.Extensions.Palettes;
@@ -10,15 +9,12 @@ using PixiEditor.Models.AppExtensions.Services;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataProviders;
 using PixiEditor.Models.DataProviders;
-using PixiEditor.Models.IO;
 using PixiEditor.Models.IO.PaletteParsers;
 using PixiEditor.Models.IO.PaletteParsers;
 using PixiEditor.Models.IO.PaletteParsers.JascPalFile;
 using PixiEditor.Models.IO.PaletteParsers.JascPalFile;
 using PixiEditor.Models.Localization;
 using PixiEditor.Models.Localization;
 using PixiEditor.Models.Preferences;
 using PixiEditor.Models.Preferences;
-using PixiEditor.ViewModels;
 using PixiEditor.ViewModels.SubViewModels.AdditionalContent;
 using PixiEditor.ViewModels.SubViewModels.AdditionalContent;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Document;
-using PixiEditor.ViewModels.SubViewModels.Main;
 using PixiEditor.ViewModels.SubViewModels.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 
 
@@ -75,6 +71,8 @@ internal static class ServiceCollectionHelpers
         // Palette Parsers
         // Palette Parsers
         .AddSingleton<PaletteFileParser, JascFileParser>()
         .AddSingleton<PaletteFileParser, JascFileParser>()
         .AddSingleton<PaletteFileParser, ClsFileParser>()
         .AddSingleton<PaletteFileParser, ClsFileParser>()
+        .AddSingleton<PaletteFileParser, CorelDrawPalParser>()
+        .AddSingleton<PaletteFileParser, DeluxePaintParser>()
         .AddSingleton<PaletteFileParser, PngPaletteParser>()
         .AddSingleton<PaletteFileParser, PngPaletteParser>()
         .AddSingleton<PaletteFileParser, PaintNetTxtParser>()
         .AddSingleton<PaletteFileParser, PaintNetTxtParser>()
         .AddSingleton<PaletteFileParser, HexPaletteParser>()
         .AddSingleton<PaletteFileParser, HexPaletteParser>()

+ 26 - 1
src/PixiEditor/Helpers/PaletteHelpers.cs

@@ -1,4 +1,6 @@
-using PixiEditor.Extensions.Palettes.Parsers;
+using System.IO;
+using PixiEditor.Extensions.Palettes.Parsers;
+using PixiEditor.Models.AppExtensions.Services;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
 
 
 namespace PixiEditor.Helpers;
 namespace PixiEditor.Helpers;
@@ -28,4 +30,27 @@ internal static class PaletteHelpers
 
 
         return filter.Remove(filter.Length - 1);
         return filter.Remove(filter.Length - 1);
     }
     }
+
+    public static async Task<PaletteFileData> GetValidParser(IList<PaletteFileParser> parsers, string fileName)
+    {
+        // check all parsers for formats with same file extension
+        var parserList = parsers.Where(x => x.SupportedFileExtensions.Contains(Path.GetExtension(fileName).ToLower())).ToList();
+
+        if (parserList != null)
+        {
+            int index = 0;
+            foreach (var parser in parserList)
+            {
+                var data = await parser.Parse(fileName);
+                index++;
+
+                if ((data.IsCorrupted || data.Colors.Length == 0) && index == parserList.Count) return null; // fail if none of the parsers in our list can read the file
+                if (data.IsCorrupted) continue; // skip to next parser if unable to read
+
+                return data;
+            }
+        }
+
+        return null;
+    }
 }
 }

+ 214 - 0
src/PixiEditor/Models/IO/PaletteParsers/CorelDrawPalParser.cs

@@ -0,0 +1,214 @@
+using System.IO;
+using System.Text;
+using PixiEditor.Extensions.Palettes;
+using PixiEditor.Extensions.Palettes.Parsers;
+
+namespace PixiEditor.Models.IO.PaletteParsers;
+
+internal class CorelDrawPalParser : PaletteFileParser
+{
+    public override string FileName { get; } = "CorelDRAW! 3.0 Palette";
+    public override string[] SupportedFileExtensions { get; } = { ".pal" };
+
+    // Default name to use for colors (color name is required by format)
+    private const string SWATCH_NAME = "PixiEditor Color";
+
+    public override async Task<PaletteFileData> Parse(string path)
+    {
+        try
+        {
+            return await ParseFile(path);
+        }
+        catch
+        {
+            return PaletteFileData.Corrupted;
+        }
+    }
+
+    private async Task<PaletteFileData> ParseFile(string path)
+    {
+        string name = Path.GetFileNameWithoutExtension(path);
+
+        List<PaletteColor> colors = new();
+
+        using (Stream stream = File.OpenRead(path))
+        {
+            using (TextReader reader = new StreamReader(stream, Encoding.ASCII))
+            {
+                string line;
+
+                while ((line = reader.ReadLine()) != null && (line.Length == 0 || line[0] != (char)26))
+                {
+                    if (!string.IsNullOrEmpty(line))
+                    {
+                        int lastQuote;
+                        int numberPosition;
+                        byte cyan;
+                        byte magenta;
+                        byte yellow;
+                        byte black;
+
+                        lastQuote = line.LastIndexOf('"');
+
+                        if (lastQuote == -1)
+                        {
+                            return PaletteFileData.Corrupted; // Unable to parse line
+                        }
+
+                        numberPosition = lastQuote + 1;
+                        cyan = this.NextNumber(line, ref numberPosition);
+                        magenta = this.NextNumber(line, ref numberPosition);
+                        yellow = this.NextNumber(line, ref numberPosition);
+                        black = this.NextNumber(line, ref numberPosition);
+
+                        colors.Add(this.ConvertCmykToRgb(cyan, magenta, yellow, black));
+                    }
+                }
+            }
+        }
+
+        return new PaletteFileData(name, colors.ToArray());
+    }
+
+    public override async Task<bool> Save(string path, PaletteFileData data)
+    {
+        StringBuilder sb = new StringBuilder(SWATCH_NAME.Length + 20);
+
+        try
+        {
+            await using (Stream stream = File.OpenWrite(path))
+            {
+                using (TextWriter writer = new StreamWriter(stream, Encoding.ASCII, 1024, true))
+                {
+                    foreach (var color in data.Colors)
+                    {
+                        this.ConvertRgbToCmyk(color, out byte c, out byte m, out byte y, out byte k);
+
+                        this.WriteName(sb, SWATCH_NAME);
+                        this.WriteNumber(sb, c);
+                        this.WriteNumber(sb, m);
+                        this.WriteNumber(sb, y);
+                        this.WriteNumber(sb, k);
+
+                        writer.WriteLine(sb.ToString());
+                        sb.Length = 0;
+                    }
+                }
+            }
+            return true;
+        }
+        catch
+        {
+            return false;
+        }
+    }
+
+    private static byte ClampCmyk(float value)
+    {
+        if (value < 0 || float.IsNaN(value))
+        {
+            value = 0;
+        }
+
+        return Convert.ToByte(value * 100);
+    }
+
+    private PaletteColor ConvertCmykToRgb(int c, int m, int y, int k)
+    {
+        int r, g, b;
+        float multiplier;
+
+        multiplier = 1 - k / 100F;
+
+        r = Convert.ToInt32(255 * (1 - c / 100F) * multiplier);
+        g = Convert.ToInt32(255 * (1 - m / 100F) * multiplier);
+        b = Convert.ToInt32(255 * (1 - y / 100F) * multiplier);
+
+        return new PaletteColor((byte)r, (byte)g, (byte)b);
+    }
+
+    private void ConvertRgbToCmyk(PaletteColor color, out byte c, out byte m, out byte y, out byte k)
+    {
+        float r, g, b;
+        float divisor;
+
+        r = color.R / 255F;
+        g = color.G / 255F;
+        b = color.B / 255F;
+
+        divisor = 1 - Math.Max(Math.Max(r, g), b);
+
+        c = ClampCmyk((1 - r - divisor) / (1 - divisor));
+        m = ClampCmyk((1 - g - divisor) / (1 - divisor));
+        y = ClampCmyk((1 - b - divisor) / (1 - divisor));
+        k = ClampCmyk(divisor);
+    }
+
+    private byte NextNumber(string line, ref int start)
+    {
+        int length;
+        int valueLength;
+        int maxLength;
+        byte result;
+
+        // skip any leading spaces
+        while (char.IsWhiteSpace(line[start]))
+        {
+            start++;
+        }
+
+        length = line.Length;
+        maxLength = Math.Min(3, length - start);
+        valueLength = 0;
+
+        for (int i = 0; i < maxLength; i++)
+        {
+            if (char.IsDigit(line[start + i]))
+            {
+                valueLength++;
+            }
+            else
+            {
+                break;
+            }
+        }
+
+        result = byte.Parse(line.Substring(start, valueLength));
+
+        start += valueLength;
+
+        return result;
+    }
+
+    private void WriteName(StringBuilder sb, string name)
+    {
+        sb.Append('"');
+        sb.Append(name);
+        sb.Append('"');
+
+        for (int j = (name ?? string.Empty).Length; j < name.Length; j++)
+        {
+            sb.Append(' ');
+        }
+    }
+
+    private void WriteNumber(StringBuilder sb, byte value)
+    {
+        if (value == 100)
+        {
+            sb.Append("100 ");
+        }
+        else
+        {
+            sb.Append(' ');
+            if (value < 10)
+            {
+                sb.Append(' ');
+            }
+
+            sb.Append(value);
+
+            sb.Append(' ');
+        }
+    }
+}

+ 122 - 0
src/PixiEditor/Models/IO/PaletteParsers/DeluxePaintParser.cs

@@ -0,0 +1,122 @@
+using System.IO;
+using System.Text;
+using PixiEditor.Extensions.Palettes;
+using PixiEditor.Extensions.Palettes.Parsers;
+
+namespace PixiEditor.Models.IO.PaletteParsers;
+
+// Reads 8-bit color palette data from Interleaved Bitmap format (LBM/BBM)
+// most commonly used by DeluxePaint on Amiga and MS DOS.
+//
+// https://en.wikipedia.org/wiki/ILBM
+
+// Note: A BBM file is essentially a LBM without a full image.
+
+internal class DeluxePaintParser : PaletteFileParser
+{
+    public override string FileName { get; } = "DeluxePaint Interleaved Bitmap Palette";
+    public override string[] SupportedFileExtensions { get; } = new string[] { ".bbm", ".lbm" };
+    public override async Task<PaletteFileData> Parse(string path)
+    {
+        try
+        {
+            return await ParseFile(path);
+        }
+        catch
+        {
+            return PaletteFileData.Corrupted;
+        }
+    }
+
+    private static async Task<PaletteFileData> ParseFile(string path)
+    {
+        List<PaletteColor> colorPalette = new();
+        string name = Path.GetFileNameWithoutExtension(path);
+
+        await using (Stream stream = File.OpenRead(path))
+        {
+            byte[] buffer;
+            string header;
+
+            // read the FORM header that identifies the document as an IFF file
+            buffer = new byte[4];
+            stream.Read(buffer, 0, buffer.Length);
+            if (Encoding.ASCII.GetString(buffer) != "FORM")
+                return PaletteFileData.Corrupted; // Form header not found
+
+            // the next value is the size of all the data in the FORM chunk
+            // We don't actually need this value, but we have to read it
+            // regardless to advance the stream
+            ReadInt(stream);
+
+            stream.Read(buffer, 0, buffer.Length);
+            header = Encoding.ASCII.GetString(buffer);
+            if (header != "PBM " && header != "ILBM")
+                return PaletteFileData.Corrupted; // Bitmap header not found
+
+            while (stream.Read(buffer, 0, buffer.Length) == buffer.Length)
+            {
+                int chunkLength;
+
+                chunkLength = ReadInt(stream);
+
+                if (Encoding.ASCII.GetString(buffer) != "CMAP")
+                {
+                    // some other LBM chunk, skip it
+                    if (stream.CanSeek)
+                    {
+                        stream.Seek(chunkLength, SeekOrigin.Current);
+                    }
+                    else
+                    {
+                        for (int i = 0; i < chunkLength; i++)
+                            stream.ReadByte();
+                    }
+                }
+                else
+                {
+                    // color map chunk
+                    for (int i = 0; i < chunkLength / 3; i++)
+                    {
+                        int[] rgb = new int[3];
+
+                        rgb[0] = stream.ReadByte();
+                        rgb[1] = stream.ReadByte();
+                        rgb[2] = stream.ReadByte();
+
+                        colorPalette.Add(new PaletteColor((byte)rgb[0], (byte)rgb[1], (byte)rgb[2]));
+                    }
+
+                    // all done so stop reading the rest of the file
+                    break;
+                }
+
+                // chunks always contain an even number of bytes even if the recorded length is odd
+                // if the length is odd, then there's a padding byte in the file - just read and discard
+                if (chunkLength % 2 != 0)
+                    stream.ReadByte();
+            }
+        }
+
+        return new PaletteFileData(name, colorPalette.ToArray());
+    }
+
+    public override bool CanSave => false;
+
+    public override async Task<bool> Save(string path, PaletteFileData data)
+    {
+        throw new SavingNotSupportedException("Saving palette as .bbm or .lbm is not supported.");
+    }
+
+    private static int ReadInt(Stream stream)
+    {
+        byte[] buffer;
+
+        // big endian conversion: http://stackoverflow.com/a/14401341/148962
+
+        buffer = new byte[4];
+        stream.Read(buffer, 0, buffer.Length);
+
+        return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+    }
+}

+ 1 - 2
src/PixiEditor/Models/IO/PaletteParsers/PaintNetTxtParser.cs

@@ -36,7 +36,6 @@ internal class PaintNetTxtParser : PaletteFileParser
         List<PaletteColor> colors = new();
         List<PaletteColor> colors = new();
         for (int i = 0; i < lines.Length; i++)
         for (int i = 0; i < lines.Length; i++)
         {
         {
-            // Color format aarrggbb
             string colorLine = lines[i];
             string colorLine = lines[i];
             if(colorLine.Length < 8)
             if(colorLine.Length < 8)
                 continue;
                 continue;
@@ -62,7 +61,7 @@ internal class PaintNetTxtParser : PaletteFileParser
         sb.AppendLine($"; {data.Colors.Length} colors");
         sb.AppendLine($"; {data.Colors.Length} colors");
         foreach (PaletteColor color in data.Colors)
         foreach (PaletteColor color in data.Colors)
         {
         {
-            sb.AppendLine(color.Hex);
+            sb.AppendLine($"FF{color.R:X2}{color.G:X2}{color.B:X2}");
         }
         }
 
 
         try
         try

+ 3 - 3
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -10,7 +10,7 @@ using System.Windows;
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("PixiEditor")]
 [assembly: AssemblyCompany("PixiEditor")]
 [assembly: AssemblyProduct("PixiEditor")]
 [assembly: AssemblyProduct("PixiEditor")]
-[assembly: AssemblyCopyright("Copyright PixiEditor © 2017 - 2023")]
+[assembly: AssemblyCopyright("Copyright PixiEditor © 2017 - 2024")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 [assembly: AssemblyCulture("")]
 
 
@@ -50,5 +50,5 @@ using System.Windows;
 // You can specify all the values or you can default the Build and Revision Numbers
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.2.3.0")]
-[assembly: AssemblyFileVersion("1.2.3.0")]
+[assembly: AssemblyVersion("1.2.4.0")]
+[assembly: AssemblyFileVersion("1.2.4.0")]

+ 7 - 14
src/PixiEditor/Views/Dialogs/PalettesBrowser.xaml.cs

@@ -1,19 +1,16 @@
-using System.ComponentModel;
+using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.IO;
 using System.IO;
 using System.Windows;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Controls;
-using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Input;
 using System.Windows.Navigation;
 using System.Windows.Navigation;
 using Microsoft.Win32;
 using Microsoft.Win32;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.Extensions;
 using PixiEditor.Extensions;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.Common.UserPreferences;
 using PixiEditor.Extensions.Common.UserPreferences;
 using PixiEditor.Extensions.Palettes;
 using PixiEditor.Extensions.Palettes;
 using PixiEditor.Extensions.Palettes.Parsers;
 using PixiEditor.Extensions.Palettes.Parsers;
-using PixiEditor.Extensions.Windowing;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.AppExtensions.Services;
 using PixiEditor.Models.AppExtensions.Services;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
@@ -22,8 +19,6 @@ using PixiEditor.Models.DataProviders;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
-using PixiEditor.Models.Localization;
-using PixiEditor.Platform;
 using PixiEditor.Views.UserControls;
 using PixiEditor.Views.UserControls;
 using PixiEditor.Views.UserControls.Palettes;
 using PixiEditor.Views.UserControls.Palettes;
 using PaletteColor = PixiEditor.Extensions.Palettes.PaletteColor;
 using PaletteColor = PixiEditor.Extensions.Palettes.PaletteColor;
@@ -181,7 +176,7 @@ internal partial class PalettesBrowser : Window, IPopupWindow
         localizationProvider = ViewModelMain.Current.LocalizationProvider;
         localizationProvider = ViewModelMain.Current.LocalizationProvider;
         localizationProvider.OnLanguageChanged += LocalizationProviderOnOnLanguageChanged;
         localizationProvider.OnLanguageChanged += LocalizationProviderOnOnLanguageChanged;
         MinWidth = DetermineWidth();
         MinWidth = DetermineWidth();
-        
+
         PaletteProvider = provider;
         PaletteProvider = provider;
         InitializeComponent();
         InitializeComponent();
         Title = new LocalizedString("PALETTE_BROWSER");
         Title = new LocalizedString("PALETTE_BROWSER");
@@ -216,7 +211,7 @@ internal partial class PalettesBrowser : Window, IPopupWindow
             "ru" or "uk" => 900,
             "ru" or "uk" => 900,
             _ => 850
             _ => 850
         };
         };
-    } 
+    }
 
 
     private bool CanAddFromPalette(object param)
     private bool CanAddFromPalette(object param)
     {
     {
@@ -427,7 +422,7 @@ internal partial class PalettesBrowser : Window, IPopupWindow
 
 
         foreach (var pal in srcPalettes)
         foreach (var pal in srcPalettes)
         {
         {
-            if(PaletteEquals(pal, PaletteList.Palettes)) continue;
+            if (PaletteEquals(pal, PaletteList.Palettes)) continue;
             PaletteList.Palettes.Add(pal);
             PaletteList.Palettes.Add(pal);
         }
         }
     }
     }
@@ -652,14 +647,12 @@ internal partial class PalettesBrowser : Window, IPopupWindow
 
 
     private async Task ImportPalette(string fileName, IList<PaletteFileParser> parsers)
     private async Task ImportPalette(string fileName, IList<PaletteFileParser> parsers)
     {
     {
-        var parser = parsers.FirstOrDefault(x => x.SupportedFileExtensions.Contains(Path.GetExtension(fileName)));
+        // check if valid parser found
+        var parser = await PaletteHelpers.GetValidParser(parsers, fileName);
         if (parser != null)
         if (parser != null)
         {
         {
-            var data = await parser.Parse(fileName);
-
-            if (data.IsCorrupted) return;
             string name = LocalPalettesFetcher.GetNonExistingName(Path.GetFileName(fileName), true);
             string name = LocalPalettesFetcher.GetNonExistingName(Path.GetFileName(fileName), true);
-            await LocalPalettesFetcher.SavePalette(name, data.Colors.ToArray());
+            await LocalPalettesFetcher.SavePalette(name, parser.Colors.ToArray());
         }
         }
     }
     }
 
 

+ 26 - 16
src/PixiEditor/Views/UserControls/Palettes/PaletteViewer.xaml.cs

@@ -9,10 +9,10 @@ using PixiEditor.Extensions.Palettes.Parsers;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.AppExtensions.Services;
 using PixiEditor.Models.AppExtensions.Services;
 using PixiEditor.Models.DataHolders;
 using PixiEditor.Models.DataHolders;
-using PixiEditor.Models.DataProviders;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.IO;
 using PixiEditor.Models.IO;
+using PixiEditor.Models.IO.PaletteParsers;
 using PixiEditor.Views.Dialogs;
 using PixiEditor.Views.Dialogs;
 
 
 namespace PixiEditor.Views.UserControls.Palettes;
 namespace PixiEditor.Views.UserControls.Palettes;
@@ -81,7 +81,7 @@ internal partial class PaletteViewer : UserControl
         get { return (PaletteProvider)GetValue(PaletteProviderProperty); }
         get { return (PaletteProvider)GetValue(PaletteProviderProperty); }
         set { SetValue(PaletteProviderProperty, value); }
         set { SetValue(PaletteProviderProperty, value); }
     }
     }
-    
+
     public PaletteViewer()
     public PaletteViewer()
     {
     {
         InitializeComponent();
         InitializeComponent();
@@ -111,32 +111,42 @@ internal partial class PaletteViewer : UserControl
 
 
     private async Task ImportPalette(string fileName)
     private async Task ImportPalette(string fileName)
     {
     {
-        var parser = PaletteProvider.AvailableParsers.FirstOrDefault(x => x.SupportedFileExtensions.Contains(Path.GetExtension(fileName)));
-        if (parser == null) return;
-        var data = await parser.Parse(fileName);
-        if (data.IsCorrupted || data.Colors.Length == 0) return;
-        Colors.Clear();
-        Colors.AddRange(data.Colors);
+        // check if valid parser found
+        var parser = await PaletteHelpers.GetValidParser(PaletteProvider.AvailableParsers, fileName);
+        if (parser != null)
+        {
+            Colors.Clear();
+            Colors.AddRange(parser.Colors);
+        }
     }
     }
 
 
     private async void SavePalette_OnClick(object sender, RoutedEventArgs e)
     private async void SavePalette_OnClick(object sender, RoutedEventArgs e)
     {
     {
+        List<PaletteFileParser> availableParsers = PaletteProvider.AvailableParsers.Where(x => x.CanSave).ToList();
         SaveFileDialog saveFileDialog = new SaveFileDialog
         SaveFileDialog saveFileDialog = new SaveFileDialog
         {
         {
-            Filter = PaletteHelpers.GetFilter(PaletteProvider.AvailableParsers.Where(x => x.CanSave).ToList(), false)
+            Filter = PaletteHelpers.GetFilter(availableParsers, false)
         };
         };
 
 
         if (saveFileDialog.ShowDialog() == true)
         if (saveFileDialog.ShowDialog() == true)
         {
         {
-            string fileName = saveFileDialog.FileName;
-            var foundParser = PaletteProvider.AvailableParsers.First(x => x.SupportedFileExtensions.Contains(Path.GetExtension(fileName)));
+            int filterIndex = saveFileDialog.FilterIndex;
+            var foundParser = availableParsers[filterIndex - 1];
             if (Colors == null || Colors.Count == 0)
             if (Colors == null || Colors.Count == 0)
             {
             {
                 NoticeDialog.Show("NO_COLORS_TO_SAVE", "ERROR");
                 NoticeDialog.Show("NO_COLORS_TO_SAVE", "ERROR");
                 return;
                 return;
             }
             }
-            bool saved = await foundParser.Save(fileName, new PaletteFileData(Colors.ToArray()));
-            if (!saved)
+
+            try
+            {
+                bool saved = await foundParser.Save(saveFileDialog.FileName, new PaletteFileData(Colors.ToArray()));
+                if (!saved)
+                {
+                    NoticeDialog.Show("COULD_NOT_SAVE_PALETTE", "ERROR");
+                }
+            }
+            catch (SavingNotSupportedException savingNotSupportedException)
             {
             {
                 NoticeDialog.Show("COULD_NOT_SAVE_PALETTE", "ERROR");
                 NoticeDialog.Show("COULD_NOT_SAVE_PALETTE", "ERROR");
             }
             }
@@ -167,7 +177,7 @@ internal partial class PaletteViewer : UserControl
     private async void Grid_Drop(object sender, DragEventArgs e)
     private async void Grid_Drop(object sender, DragEventArgs e)
     {
     {
         ViewModelMain.Current.ActionDisplays[nameof(PaletteViewer)] = null;
         ViewModelMain.Current.ActionDisplays[nameof(PaletteViewer)] = null;
-        
+
         if (!IsSupportedFilePresent(e, out string filePath))
         if (!IsSupportedFilePresent(e, out string filePath))
         {
         {
             if (!ColorHelper.ParseAnyFormatList(e.Data, out var colors))
             if (!ColorHelper.ParseAnyFormatList(e.Data, out var colors))
@@ -176,7 +186,7 @@ internal partial class PaletteViewer : UserControl
             }
             }
 
 
             List<PaletteColor> paletteColors = colors.Select(x => new PaletteColor(x.R, x.G, x.B)).ToList();
             List<PaletteColor> paletteColors = colors.Select(x => new PaletteColor(x.R, x.G, x.B)).ToList();
-            
+
             e.Effects = DragDropEffects.Copy;
             e.Effects = DragDropEffects.Copy;
             Colors.AddRange(paletteColors.Where(x => !Colors.Contains(new PaletteColor(x.R, x.G, x.B))).ToList());
             Colors.AddRange(paletteColors.Where(x => !Colors.Contains(new PaletteColor(x.R, x.G, x.B))).ToList());
             e.Handled = true;
             e.Handled = true;
@@ -255,7 +265,7 @@ internal partial class PaletteViewer : UserControl
 
 
     private void DiscardPalette_OnClick(object sender, RoutedEventArgs e)
     private void DiscardPalette_OnClick(object sender, RoutedEventArgs e)
     {
     {
-        if(ConfirmationDialog.Show("DISCARD_PALETTE_CONFIRMATION", "DISCARD_PALETTE") == ConfirmationType.Yes)
+        if (ConfirmationDialog.Show("DISCARD_PALETTE_CONFIRMATION", "DISCARD_PALETTE") == ConfirmationType.Yes)
         {
         {
             Colors.Clear();
             Colors.Clear();
         }
         }