From 1b236e03afdaf09df5c39fcae86f534271c8aeb0 Mon Sep 17 00:00:00 2001 From: Fritiof Hedman Date: Sun, 7 Jun 2026 14:10:43 +0200 Subject: [PATCH] 1.6 DitherAlgorithm value type Co-Authored-By: Claude Opus 4.7 (1M context) --- IMPLEMENTATION.md | 2 +- src/FrameProcessor/Domain/DitherAlgorithm.cs | 40 +++++++++++++ .../DitherAlgorithmTests.cs | 57 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/FrameProcessor/Domain/DitherAlgorithm.cs create mode 100644 tests/FrameProcessor.Tests/DitherAlgorithmTests.cs diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index ba55fbc..ad4eb46 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -59,7 +59,7 @@ Each type lives in `src/FrameProcessor/Domain/`. Tests in `tests/FrameProcessor. - `record(string Name, Color DisplayColor, Color DeviceColor)` using `SixLabors.ImageSharp.Color`. - JSON converter parses hex strings into `Color` at deserialize time (don't store hex strings on the type). -### [ ] 1.6 `DitherAlgorithm` +### [x] 1.6 `DitherAlgorithm` - `enum { FloydSteinberg, Atkinson, Stucki, Jarvis }`. - JSON converter reading kebab-case (`floyd-steinberg`, etc.). diff --git a/src/FrameProcessor/Domain/DitherAlgorithm.cs b/src/FrameProcessor/Domain/DitherAlgorithm.cs new file mode 100644 index 0000000..d4d42c3 --- /dev/null +++ b/src/FrameProcessor/Domain/DitherAlgorithm.cs @@ -0,0 +1,40 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace FrameProcessor.Domain; + +[JsonConverter(typeof(DitherAlgorithmJsonConverter))] +public enum DitherAlgorithm +{ + FloydSteinberg, + Atkinson, + Stucki, + Jarvis, +} + +internal sealed class DitherAlgorithmJsonConverter : JsonConverter +{ + public override DitherAlgorithm Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var s = reader.GetString(); + return s switch + { + "floyd-steinberg" => DitherAlgorithm.FloydSteinberg, + "atkinson" => DitherAlgorithm.Atkinson, + "stucki" => DitherAlgorithm.Stucki, + "jarvis" => DitherAlgorithm.Jarvis, + _ => throw new JsonException( + $"'{s}' is not a valid dither algorithm. Expected 'floyd-steinberg', 'atkinson', 'stucki', or 'jarvis'."), + }; + } + + public override void Write(Utf8JsonWriter writer, DitherAlgorithm value, JsonSerializerOptions options) + => writer.WriteStringValue(value switch + { + DitherAlgorithm.FloydSteinberg => "floyd-steinberg", + DitherAlgorithm.Atkinson => "atkinson", + DitherAlgorithm.Stucki => "stucki", + DitherAlgorithm.Jarvis => "jarvis", + _ => throw new JsonException($"Unknown dither algorithm: {value}."), + }); +} diff --git a/tests/FrameProcessor.Tests/DitherAlgorithmTests.cs b/tests/FrameProcessor.Tests/DitherAlgorithmTests.cs new file mode 100644 index 0000000..c94d331 --- /dev/null +++ b/tests/FrameProcessor.Tests/DitherAlgorithmTests.cs @@ -0,0 +1,57 @@ +using System.Text.Json; +using FrameProcessor.Domain; + +namespace FrameProcessor.Tests; + +public class DitherAlgorithmTests +{ + [Theory] + [InlineData("\"floyd-steinberg\"", DitherAlgorithm.FloydSteinberg)] + [InlineData("\"atkinson\"", DitherAlgorithm.Atkinson)] + [InlineData("\"stucki\"", DitherAlgorithm.Stucki)] + [InlineData("\"jarvis\"", DitherAlgorithm.Jarvis)] + public void JsonDeserialize_KebabCaseLowercase(string json, DitherAlgorithm expected) + { + var value = JsonSerializer.Deserialize(json); + Assert.Equal(expected, value); + } + + [Theory] + [InlineData(DitherAlgorithm.FloydSteinberg, "\"floyd-steinberg\"")] + [InlineData(DitherAlgorithm.Atkinson, "\"atkinson\"")] + [InlineData(DitherAlgorithm.Stucki, "\"stucki\"")] + [InlineData(DitherAlgorithm.Jarvis, "\"jarvis\"")] + public void JsonSerialize_KebabCaseLowercase(DitherAlgorithm value, string expected) + { + var json = JsonSerializer.Serialize(value); + Assert.Equal(expected, json); + } + + [Theory] + [InlineData("\"FloydSteinberg\"")] + [InlineData("\"floyd_steinberg\"")] + [InlineData("\"FLOYD-STEINBERG\"")] + [InlineData("\"sierra\"")] + [InlineData("\"\"")] + public void JsonDeserialize_RejectsInvalid(string json) + { + Assert.Throws(() => JsonSerializer.Deserialize(json)); + } + + [Fact] + public void JsonRoundTrip_PreservesValue() + { + foreach (var value in new[] + { + DitherAlgorithm.FloydSteinberg, + DitherAlgorithm.Atkinson, + DitherAlgorithm.Stucki, + DitherAlgorithm.Jarvis, + }) + { + var json = JsonSerializer.Serialize(value); + var roundTripped = JsonSerializer.Deserialize(json); + Assert.Equal(value, roundTripped); + } + } +}