Skip to content

Commit

Permalink
Merge pull request #515 from PixiEditor/new-palette-formats
Browse files Browse the repository at this point in the history
New palette formats parsers
  • Loading branch information
flabbet authored May 5, 2023
2 parents ae24c35 + be821c6 commit 6eeb98e
Show file tree
Hide file tree
Showing 20 changed files with 497 additions and 27 deletions.
9 changes: 9 additions & 0 deletions src/PixiEditor.DrawingApi.Core/ColorsImpl/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,14 @@ public static bool TryParse(string hexString, out Color color)
return false;
}
}

/// <summary>
/// Returns hex string representation of the color.
/// </summary>
/// <returns>Color string in format: AARRGGBB</returns>
public string? ToHex()
{
return this == Empty ? null : $"{this._colorValue:X8}";
}
}
}
2 changes: 2 additions & 0 deletions src/PixiEditor/Data/Localization/Languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@
"BROWSE_PALETTES": "Browse palettes",
"LOAD_PALETTE": "Load palette",
"SAVE_PALETTE": "Save palette",
"DISCARD_PALETTE": "Discard palette",
"DISCARD_PALETTE_CONFIRMATION": "Are you sure you want to discard current palette? This cannot be undone.",
"FAVORITES": "Favorites",
"ADD_FROM_CURRENT_PALETTE": "Add from current palette",
"OPEN_PALETTES_DIR_TOOLTIP": "Open palettes directory in explorer",
Expand Down
9 changes: 7 additions & 2 deletions src/PixiEditor/Helpers/Extensions/ServiceCollectionHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using PixiEditor.Models.Controllers;
using PixiEditor.Models.DataProviders;
using PixiEditor.Models.IO;
using PixiEditor.Models.IO.ClsFile;
using PixiEditor.Models.IO.JascPalFile;
using PixiEditor.Models.IO.PaletteParsers;
using PixiEditor.Models.IO.PaletteParsers.JascPalFile;
using PixiEditor.Models.UserPreferences;
using PixiEditor.ViewModels;
using PixiEditor.ViewModels.SubViewModels.Document;
Expand Down Expand Up @@ -64,6 +64,11 @@ public static IServiceCollection AddPixiEditor(this IServiceCollection collectio
// Palette Parsers
.AddSingleton<PaletteFileParser, JascFileParser>()
.AddSingleton<PaletteFileParser, ClsFileParser>()
.AddSingleton<PaletteFileParser, PngPaletteParser>()
.AddSingleton<PaletteFileParser, PaintNetTxtParser>()
.AddSingleton<PaletteFileParser, HexPaletteParser>()
.AddSingleton<PaletteFileParser, GimpGplParser>()
.AddSingleton<PaletteFileParser, PixiPaletteParser>()
// Palette data sources
.AddSingleton<PaletteListDataSource, LocalPalettesFetcher>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.DataHolders.Palettes;
using PixiEditor.Models.IO;
using PixiEditor.Models.IO.JascPalFile;
using PixiEditor.Models.IO.PaletteParsers.JascPalFile;
using PixiEditor.Models.UserPreferences;

namespace PixiEditor.Models.DataProviders;
Expand Down
1 change: 1 addition & 0 deletions src/PixiEditor/Models/IO/PaletteFileData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ internal class PaletteFileData
public string Title { get; set; }
public Color[] Colors { get; set; }
public bool IsCorrupted { get; set; } = false;
public static PaletteFileData Corrupted => new ("Corrupted", Array.Empty<Color>()) { IsCorrupted = true };

public PaletteFileData(Color[] colors)
{
Expand Down
16 changes: 14 additions & 2 deletions src/PixiEditor/Models/IO/PaletteFileParser.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
namespace PixiEditor.Models.IO;
using System.IO;

namespace PixiEditor.Models.IO;

internal abstract class PaletteFileParser
{
public abstract Task<PaletteFileData> Parse(string path);
public abstract Task Save(string path, PaletteFileData data);
public abstract Task<bool> Save(string path, PaletteFileData data);
public abstract string FileName { get; }
public abstract string[] SupportedFileExtensions { get; }

public virtual bool CanSave => true;

protected static async Task<string[]> ReadTextLines(string path)
{
using var stream = File.OpenText(path);
string fileContent = await stream.ReadToEndAsync();
string[] lines = fileContent.Split('\n');
return lines;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using CLSEncoderDecoder;
using PixiEditor.DrawingApi.Core.ColorsImpl;

namespace PixiEditor.Models.IO.ClsFile;
namespace PixiEditor.Models.IO.PaletteParsers;

internal class ClsFileParser : PaletteFileParser
{
Expand All @@ -21,7 +21,7 @@ public override async Task<PaletteFileData> Parse(string path)
}
catch
{
return new PaletteFileData("Corrupted", Array.Empty<Color>()) { IsCorrupted = true };
return PaletteFileData.Corrupted;
}
PaletteFileData data = new(
Expand Down
91 changes: 91 additions & 0 deletions src/PixiEditor/Models/IO/PaletteParsers/GimpGplParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.IO;
using System.Text;
using PixiEditor.DrawingApi.Core.ColorsImpl;

namespace PixiEditor.Models.IO.PaletteParsers;

internal class GimpGplParser : PaletteFileParser
{
public override string FileName { get; } = "GIMP Palette";
public override string[] SupportedFileExtensions { get; } = { ".gpl" };

public override async Task<PaletteFileData> Parse(string path)
{
try
{
return await ParseFile(path);
}
catch
{
return PaletteFileData.Corrupted;
}
}

private async Task<PaletteFileData> ParseFile(string path)
{
var lines = await ReadTextLines(path);
string name = Path.GetFileNameWithoutExtension(path);

lines = lines.Where(x => !x.StartsWith("#") && !String.Equals(x.Trim(), "GIMP Palette", StringComparison.CurrentCultureIgnoreCase)).ToArray();

if(lines.Length == 0) return PaletteFileData.Corrupted;

List<Color> colors = new();
char[] separators = new[] { '\t', ' ' };
foreach (var colorLine in lines)
{
var colorParts = colorLine.Split(separators, StringSplitOptions.RemoveEmptyEntries);

if (colorParts.Length < 3)
{
continue;
}

if(colorParts.Length < 3) continue;

bool parsed = false;

parsed = byte.TryParse(colorParts[0], out byte r);
if(!parsed) continue;

parsed = byte.TryParse(colorParts[1], out byte g);
if(!parsed) continue;

parsed = byte.TryParse(colorParts[2], out byte b);
if(!parsed) continue;

var color = new Color(r, g, b, 255); // alpha is ignored in PixiEditor
if (colors.Contains(color)) continue;

colors.Add(color);
}

return new PaletteFileData(name, colors.ToArray());
}

public override async Task<bool> Save(string path, PaletteFileData data)
{
StringBuilder sb = new();
string name = string.IsNullOrEmpty(data.Title) ? Path.GetFileNameWithoutExtension(path) : data.Title;
sb.AppendLine("GIMP Palette");
sb.AppendLine($"#Name: {name}");
sb.AppendLine($"#Colors {data.Colors.Length}");
sb.AppendLine("#Made with PixiEditor");
sb.AppendLine("#");
foreach (var color in data.Colors)
{
string hex = $"{color.R:X}{color.G:X}{color.B:X}";
sb.AppendLine($"{color.R}\t{color.G}\t{color.B}\t{hex}");
}

try
{
await File.WriteAllTextAsync(path, sb.ToString());
return true;
}
catch
{
return false;
}
}
}
66 changes: 66 additions & 0 deletions src/PixiEditor/Models/IO/PaletteParsers/HexPaletteParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Globalization;
using System.IO;
using System.Text;
using PixiEditor.DrawingApi.Core.ColorsImpl;

namespace PixiEditor.Models.IO.PaletteParsers;

internal class HexPaletteParser : PaletteFileParser
{
public override string FileName { get; } = "Hex Palette";
public override string[] SupportedFileExtensions { get; } = { ".hex" };
public override async Task<PaletteFileData> Parse(string path)
{
try
{
return await ParseFile(path);
}
catch
{
return PaletteFileData.Corrupted;
}
}

private async Task<PaletteFileData> ParseFile(string path)
{
var lines = await ReadTextLines(path);
string name = Path.GetFileNameWithoutExtension(path);

List<Color> colors = new();
foreach (var colorLine in lines)
{
if (colorLine.Length < 6)
continue;

byte r = byte.Parse(colorLine.Substring(0, 2), NumberStyles.HexNumber);
byte g = byte.Parse(colorLine.Substring(2, 2), NumberStyles.HexNumber);
byte b = byte.Parse(colorLine.Substring(4, 2), NumberStyles.HexNumber);
var color = new Color(r, g, b, 255); // alpha is ignored in PixiEditor
if (colors.Contains(color)) continue;

colors.Add(color);
}

return new PaletteFileData(name, colors.ToArray());
}

public override async Task<bool> Save(string path, PaletteFileData data)
{
StringBuilder sb = new();
foreach (var color in data.Colors)
{
string hex = $"{color.R:X}{color.G:X}{color.B:X}";
sb.AppendLine(hex);
}

try
{
await File.WriteAllTextAsync(path, sb.ToString());
return true;
}
catch
{
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace PixiEditor.Models.IO.JascPalFile;
namespace PixiEditor.Models.IO.PaletteParsers.JascPalFile;

internal class JascFileException : Exception
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.IO;
using PixiEditor.DrawingApi.Core.ColorsImpl;

namespace PixiEditor.Models.IO.JascPalFile;
namespace PixiEditor.Models.IO.PaletteParsers.JascPalFile;

/// <summary>
/// This class is responsible for parsing JASC-PAL files. Which holds the color palette data.
Expand All @@ -14,10 +14,7 @@ internal class JascFileParser : PaletteFileParser

private static async Task<PaletteFileData> ParseFile(string path)
{
using var stream = File.OpenText(path);

string fileContent = await stream.ReadToEndAsync();
string[] lines = fileContent.Split('\n');
string[] lines = await ReadTextLines(path);
string name = Path.GetFileNameWithoutExtension(path);
string fileType = lines[0];
string magicBytes = lines[1];
Expand Down Expand Up @@ -60,11 +57,11 @@ public override async Task<PaletteFileData> Parse(string path)
}
catch
{
return new PaletteFileData("Corrupted", Array.Empty<Color>()) { IsCorrupted = true };
return PaletteFileData.Corrupted;
}
}

public override async Task Save(string path, PaletteFileData data) => await SaveFile(path, data);
public override async Task<bool> Save(string path, PaletteFileData data) => await SaveFile(path, data);

private static bool ValidateFile(string fileType, string magicBytes)
{
Expand Down
76 changes: 76 additions & 0 deletions src/PixiEditor/Models/IO/PaletteParsers/PaintNetTxtParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System.Globalization;
using System.IO;
using System.Text;
using PixiEditor.DrawingApi.Core.ColorsImpl;
using PixiEditor.Helpers;

namespace PixiEditor.Models.IO.PaletteParsers;

// https://www.getpaint.net/doc/latest/WorkingWithPalettes.html

internal class PaintNetTxtParser : PaletteFileParser
{
public override string FileName { get; } = "Paint.NET Palette";
public override string[] SupportedFileExtensions { get; } = new string[] { ".txt" };
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)
{
var lines = await ReadTextLines(path);
string name = Path.GetFileNameWithoutExtension(path);

lines = lines.Where(x => !x.StartsWith(";")).ToArray();

List<Color> colors = new();
for (int i = 0; i < lines.Length; i++)
{
// Color format aarrggbb
string colorLine = lines[i];
if(colorLine.Length < 8)
continue;

byte a = byte.Parse(colorLine.Substring(0, 2), NumberStyles.HexNumber);
byte r = byte.Parse(colorLine.Substring(2, 2), NumberStyles.HexNumber);
byte g = byte.Parse(colorLine.Substring(4, 2), NumberStyles.HexNumber);
byte b = byte.Parse(colorLine.Substring(6, 2), NumberStyles.HexNumber);
var color = new Color(r, g, b, 255); // alpha is ignored in PixiEditor
if(colors.Contains(color)) continue;

colors.Add(color);
}

return new PaletteFileData(name, colors.ToArray());
}

public override async Task<bool> Save(string path, PaletteFileData data)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("; Paint.NET Palette File");
sb.AppendLine($"; Made using PixiEditor {VersionHelpers.GetCurrentAssemblyVersion().ToString()}");
sb.AppendLine($"; {data.Colors.Length} colors");
foreach (Color color in data.Colors)
{
sb.AppendLine(color.ToHex());
}

try
{
await File.WriteAllTextAsync(path, sb.ToString());
return true;
}
catch
{
return false;
}
}
}
Loading

0 comments on commit 6eeb98e

Please sign in to comment.