diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 927f502..0c153ac 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -199,24 +199,20 @@ For a 64×64 image, 1 document, 1 layer, 16-color palette: | Interface | Methods (key) | |------------------------|--------------------------------------------------| | `ICompositor` | `uint[] Composite(MinintDocument, int w, int h)` | -| `IPaletteService` | `int EnsureColor(doc, RgbaColor)`, `void CompactPalette(doc)` | | `IDrawingService` | `void ApplyBrush(layer, x, y, radius, colorIdx, w, h)` | | `IFloodFillService` | `void Fill(layer, x, y, newColorIdx, w, h)` | | `IImageEffectService` | `void AdjustContrast(doc, float factor)`, `void ToGrayscale(doc)` | | `IPatternGenerator` | `MinintDocument Generate(PatternParams)` | -| `IFragmentService` | `void CopyFragment(src, dst, rect, destPoint)` | ### 4.3 Core — Service Implementations | Class | Implements | Notes | |------------------------|-------------------------|------------------------------------| | `Compositor` | `ICompositor` | Alpha-blends layers bottom→top | -| `PaletteService` | `IPaletteService` | Color lookup, add, compact | | `DrawingService` | `IDrawingService` | Circle mask brush/eraser | | `FloodFillService` | `IFloodFillService` | BFS flood fill on index array | | `ImageEffectService` | `IImageEffectService` | Palette-based contrast/grayscale | | `PatternGenerator` | `IPatternGenerator` | Checkerboard, gradient, stripes… | -| `FragmentService` | `IFragmentService` | Copy rect with palette merging | ### 4.4 Infrastructure @@ -294,7 +290,7 @@ while queue not empty: ### 5.4 Palette Compaction -On save (or on demand): +Optional optimization (not implemented as a separate service; could be applied on save or on demand): 1. Scan all layers, collect used indices into a `HashSet`. 2. Build new palette = only used colors (keep index 0 = transparent). 3. Build old→new index mapping. @@ -306,12 +302,12 @@ On save (or on demand): for each pixel (sx, sy) in source rect: srcIdx = srcLayer.Pixels[sy * W + sx] srcColor = srcDoc.Palette[srcIdx] - dstIdx = dstDoc.PaletteService.EnsureColor(srcColor) + dstIdx = dstDoc.EnsureColorCached(srcColor) dstLayer.Pixels[destY * W + destX] = dstIdx destX++; ... ``` -`EnsureColor` checks if color exists in target palette; if not, appends it and returns the new index. +`EnsureColorCached` checks if color exists in target palette; if not, appends it and returns the new index. ### 5.6 Contrast (A1) diff --git a/Minint.Core/Services/IFragmentService.cs b/Minint.Core/Services/IFragmentService.cs deleted file mode 100644 index c3ddf98..0000000 --- a/Minint.Core/Services/IFragmentService.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Minint.Core.Models; - -namespace Minint.Core.Services; - -public interface IFragmentService -{ - /// - /// Copies a rectangular region from one document/layer to another. - /// Palette colors are merged: missing colors are added to the destination palette. - /// - /// Source document. - /// Index of the source layer. - /// Source rectangle X origin. - /// Source rectangle Y origin. - /// Width of the region to copy. - /// Height of the region to copy. - /// Destination document. - /// Index of the destination layer. - /// Destination X origin. - /// Destination Y origin. - /// Container width (shared by both docs). - /// Container height (shared by both docs). - void CopyFragment( - MinintDocument srcDoc, int srcLayerIndex, - int srcX, int srcY, int regionWidth, int regionHeight, - MinintDocument dstDoc, int dstLayerIndex, - int dstX, int dstY, - int containerWidth, int containerHeight); -} diff --git a/Minint.Core/Services/IImageEffectService.cs b/Minint.Core/Services/IImageEffectService.cs deleted file mode 100644 index c96c255..0000000 --- a/Minint.Core/Services/IImageEffectService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Minint.Core.Models; - -namespace Minint.Core.Services; - -public interface IImageEffectService -{ - /// - /// Adjusts contrast of the document by modifying palette colors. - /// > 1 increases contrast, < 1 decreases. - /// Index 0 (transparent) is not modified. - /// - void AdjustContrast(MinintDocument document, float factor); - - /// - /// Converts the document to grayscale by modifying palette colors. - /// Uses ITU-R BT.601 luminance: gray = 0.299R + 0.587G + 0.114B. - /// Index 0 (transparent) is not modified. - /// - void ToGrayscale(MinintDocument document); -} diff --git a/Minint.Core/Services/IPaletteService.cs b/Minint.Core/Services/IPaletteService.cs deleted file mode 100644 index 9abdfb6..0000000 --- a/Minint.Core/Services/IPaletteService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Minint.Core.Models; - -namespace Minint.Core.Services; - -public interface IPaletteService -{ - /// - /// Returns the index of in the document palette. - /// If the color is not present, appends it and returns the new index. - /// - int EnsureColor(MinintDocument document, RgbaColor color); - - /// - /// Finds index of an exact color match, or returns -1 if not found. - /// - int FindColor(MinintDocument document, RgbaColor color); - - /// - /// Removes unused colors from the palette and remaps all layer pixel indices. - /// Index 0 (transparent) is always preserved. - /// - void CompactPalette(MinintDocument document); -} diff --git a/Minint.Core/Services/Impl/FragmentService.cs b/Minint.Core/Services/Impl/FragmentService.cs deleted file mode 100644 index eed0f07..0000000 --- a/Minint.Core/Services/Impl/FragmentService.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Minint.Core.Models; - -namespace Minint.Core.Services.Impl; - -public sealed class FragmentService : IFragmentService -{ - public void CopyFragment( - MinintDocument srcDoc, int srcLayerIndex, - int srcX, int srcY, int regionWidth, int regionHeight, - MinintDocument dstDoc, int dstLayerIndex, - int dstX, int dstY, - int containerWidth, int containerHeight) - { - ArgumentOutOfRangeException.ThrowIfNegative(srcLayerIndex); - ArgumentOutOfRangeException.ThrowIfNegative(dstLayerIndex); - if (srcLayerIndex >= srcDoc.Layers.Count) - throw new ArgumentOutOfRangeException(nameof(srcLayerIndex)); - if (dstLayerIndex >= dstDoc.Layers.Count) - throw new ArgumentOutOfRangeException(nameof(dstLayerIndex)); - - var srcLayer = srcDoc.Layers[srcLayerIndex]; - var dstLayer = dstDoc.Layers[dstLayerIndex]; - - int clippedSrcX = Math.Max(srcX, 0); - int clippedSrcY = Math.Max(srcY, 0); - int clippedEndX = Math.Min(srcX + regionWidth, containerWidth); - int clippedEndY = Math.Min(srcY + regionHeight, containerHeight); - - for (int sy = clippedSrcY; sy < clippedEndY; sy++) - { - int dy = dstY + (sy - srcY); - if (dy < 0 || dy >= containerHeight) continue; - - for (int sx = clippedSrcX; sx < clippedEndX; sx++) - { - int dx = dstX + (sx - srcX); - if (dx < 0 || dx >= containerWidth) continue; - - int srcIdx = srcLayer.Pixels[sy * containerWidth + sx]; - if (srcIdx == 0) continue; // skip transparent - - RgbaColor color = srcDoc.Palette[srcIdx]; - int dstIdx = dstDoc.EnsureColorCached(color); - dstLayer.Pixels[dy * containerWidth + dx] = dstIdx; - } - } - } -} diff --git a/Minint.Core/Services/Impl/PaletteService.cs b/Minint.Core/Services/Impl/PaletteService.cs deleted file mode 100644 index ecf62f7..0000000 --- a/Minint.Core/Services/Impl/PaletteService.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Minint.Core.Models; - -namespace Minint.Core.Services.Impl; - -public sealed class PaletteService : IPaletteService -{ - public int FindColor(MinintDocument document, RgbaColor color) - => document.FindColorCached(color); - - public int EnsureColor(MinintDocument document, RgbaColor color) - => document.EnsureColorCached(color); - - public void CompactPalette(MinintDocument document) - { - var palette = document.Palette; - if (palette.Count <= 1) - return; - - var usedIndices = new HashSet { 0 }; - foreach (var layer in document.Layers) - { - foreach (int idx in layer.Pixels) - usedIndices.Add(idx); - } - - var oldToNew = new int[palette.Count]; - var newPalette = new List(usedIndices.Count); - - newPalette.Add(palette[0]); - oldToNew[0] = 0; - - for (int i = 1; i < palette.Count; i++) - { - if (usedIndices.Contains(i)) - { - oldToNew[i] = newPalette.Count; - newPalette.Add(palette[i]); - } - } - - if (newPalette.Count == palette.Count) - return; - - palette.Clear(); - palette.AddRange(newPalette); - - foreach (var layer in document.Layers) - { - var px = layer.Pixels; - for (int i = 0; i < px.Length; i++) - px[i] = oldToNew[px[i]]; - } - - document.InvalidatePaletteCache(); - } -} diff --git a/Minint.Tests/FragmentServiceTests.cs b/Minint.Tests/FragmentServiceTests.cs deleted file mode 100644 index 6337835..0000000 --- a/Minint.Tests/FragmentServiceTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Minint.Core.Models; -using Minint.Core.Services.Impl; - -namespace Minint.Tests; - -public class FragmentServiceTests -{ - private readonly FragmentService _fragment = new(); - - [Fact] - public void CopyFragment_SameDocument_CopiesPixels() - { - var doc = new MinintDocument("test"); - var red = new RgbaColor(255, 0, 0, 255); - doc.EnsureColorCached(red); - - var src = new MinintLayer("src", 16); - src.Pixels[0] = 1; // (0,0) = red - src.Pixels[1] = 1; // (1,0) = red - doc.Layers.Add(src); - - var dst = new MinintLayer("dst", 16); - doc.Layers.Add(dst); - - _fragment.CopyFragment(doc, 0, 0, 0, 2, 1, doc, 1, 2, 2, 4, 4); - - Assert.Equal(1, dst.Pixels[2 * 4 + 2]); // (2,2) - Assert.Equal(1, dst.Pixels[2 * 4 + 3]); // (3,2) - } - - [Fact] - public void CopyFragment_DifferentDocuments_MergesPalette() - { - var srcDoc = new MinintDocument("src"); - var blue = new RgbaColor(0, 0, 255, 255); - int blueIdx = srcDoc.EnsureColorCached(blue); - var srcLayer = new MinintLayer("L1", 4); - srcLayer.Pixels[0] = blueIdx; - srcDoc.Layers.Add(srcLayer); - - var dstDoc = new MinintDocument("dst"); - var dstLayer = new MinintLayer("L1", 4); - dstDoc.Layers.Add(dstLayer); - - _fragment.CopyFragment(srcDoc, 0, 0, 0, 1, 1, dstDoc, 0, 0, 0, 2, 2); - - int dstBlueIdx = dstDoc.FindColorCached(blue); - Assert.True(dstBlueIdx > 0); - Assert.Equal(dstBlueIdx, dstLayer.Pixels[0]); - } - - [Fact] - public void CopyFragment_TransparentPixels_Skipped() - { - var doc = new MinintDocument("test"); - var src = new MinintLayer("src", 4); // all zeros (transparent) - doc.Layers.Add(src); - - var dst = new MinintLayer("dst", 4); - Array.Fill(dst.Pixels, 0); - dst.Pixels[0] = 0; // explicitly 0 - doc.Layers.Add(dst); - - _fragment.CopyFragment(doc, 0, 0, 0, 2, 2, doc, 1, 0, 0, 2, 2); - - Assert.Equal(0, dst.Pixels[0]); // stays transparent - } -} diff --git a/Minint/Program.cs b/Minint/Program.cs index 26aeca2..4538328 100644 --- a/Minint/Program.cs +++ b/Minint/Program.cs @@ -1,9 +1,5 @@ -using Avalonia; using System; -using System.IO; -using Minint.Core.Models; -using Minint.Core.Services.Impl; -using Minint.Infrastructure.Serialization; +using Avalonia; namespace Minint; @@ -11,18 +7,7 @@ sealed class Program { [STAThread] public static void Main(string[] args) - { - // TODO: remove --test branch after verification - if (args.Length > 0 && args[0] == "--test") - { - RunRoundTripTest(); - RunCompositorTest(); - RunPaletteServiceTest(); - return; - } - - BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - } + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() @@ -30,231 +15,4 @@ sealed class Program .WithInterFont() .LogToTrace() .With(new X11PlatformOptions { OverlayPopups = true }); - - // TODO: temporary tests — remove after verification stages. - - private static void RunRoundTripTest() - { - Console.WriteLine("=== Minint Round-Trip Test ===\n"); - - var container = new MinintContainer(8, 4); - - var doc1 = container.AddNewDocument("Frame 1"); - doc1.FrameDelayMs = 200; - doc1.Palette.Add(new RgbaColor(255, 0, 0, 255)); // idx 1 = red - doc1.Palette.Add(new RgbaColor(0, 255, 0, 255)); // idx 2 = green - doc1.Palette.Add(new RgbaColor(0, 0, 255, 128)); // idx 3 = semi-transparent blue - var layer1 = doc1.Layers[0]; - for (int i = 0; i < layer1.Pixels.Length; i++) - layer1.Pixels[i] = i % 4; // cycle 0,1,2,3 - - doc1.Layers.Add(new MinintLayer("Overlay", container.PixelCount)); - var layer2 = doc1.Layers[1]; - layer2.Opacity = 128; - layer2.Pixels[0] = 3; - layer2.Pixels[5] = 2; - - var doc2 = container.AddNewDocument("Frame 2"); - doc2.FrameDelayMs = 150; - doc2.Palette.Add(new RgbaColor(255, 255, 0, 255)); // idx 1 = yellow - var layer3 = doc2.Layers[0]; - for (int i = 0; i < layer3.Pixels.Length; i++) - layer3.Pixels[i] = i % 2; - - Console.WriteLine($"Original: {container.Width}x{container.Height}, {container.Documents.Count} docs"); - Console.WriteLine($" Doc1: palette={doc1.Palette.Count} colors, layers={doc1.Layers.Count}, indexWidth={doc1.IndexByteWidth}"); - Console.WriteLine($" Doc2: palette={doc2.Palette.Count} colors, layers={doc2.Layers.Count}, indexWidth={doc2.IndexByteWidth}"); - - var serializer = new MinintSerializer(); - - using var ms = new MemoryStream(); - serializer.Write(ms, container); - byte[] data = ms.ToArray(); - Console.WriteLine($"\nSerialized: {data.Length} bytes"); - Console.WriteLine($" Signature: {System.Text.Encoding.ASCII.GetString(data, 0, 6)}"); - - ms.Position = 0; - var loaded = serializer.Read(ms); - - Assert(loaded.Width == container.Width, "Width mismatch"); - Assert(loaded.Height == container.Height, "Height mismatch"); - Assert(loaded.Documents.Count == container.Documents.Count, "Document count mismatch"); - - for (int d = 0; d < container.Documents.Count; d++) - { - var orig = container.Documents[d]; - var copy = loaded.Documents[d]; - Assert(copy.Name == orig.Name, $"Doc[{d}] name mismatch"); - Assert(copy.FrameDelayMs == orig.FrameDelayMs, $"Doc[{d}] frameDelay mismatch"); - Assert(copy.Palette.Count == orig.Palette.Count, $"Doc[{d}] palette count mismatch"); - - for (int c = 0; c < orig.Palette.Count; c++) - Assert(copy.Palette[c] == orig.Palette[c], $"Doc[{d}] palette[{c}] mismatch"); - - Assert(copy.Layers.Count == orig.Layers.Count, $"Doc[{d}] layer count mismatch"); - - for (int l = 0; l < orig.Layers.Count; l++) - { - var oLayer = orig.Layers[l]; - var cLayer = copy.Layers[l]; - Assert(cLayer.Name == oLayer.Name, $"Doc[{d}].Layer[{l}] name mismatch"); - Assert(cLayer.IsVisible == oLayer.IsVisible, $"Doc[{d}].Layer[{l}] visibility mismatch"); - Assert(cLayer.Opacity == oLayer.Opacity, $"Doc[{d}].Layer[{l}] opacity mismatch"); - Assert(cLayer.Pixels.Length == oLayer.Pixels.Length, $"Doc[{d}].Layer[{l}] pixel count mismatch"); - - for (int p = 0; p < oLayer.Pixels.Length; p++) - Assert(cLayer.Pixels[p] == oLayer.Pixels[p], - $"Doc[{d}].Layer[{l}].Pixels[{p}] mismatch: expected {oLayer.Pixels[p]}, got {cLayer.Pixels[p]}"); - } - } - - Console.WriteLine("\n✓ All assertions passed — round-trip is correct!"); - - // Test invalid signature - Console.Write("\nTest: invalid signature... "); - data[0] = (byte)'X'; - try - { - serializer.Read(new MemoryStream(data)); - Console.WriteLine("FAIL (no exception)"); - } - catch (InvalidDataException) - { - Console.WriteLine("OK (InvalidDataException)"); - } - data[0] = (byte)'M'; // restore - - // Test truncated stream - Console.Write("Test: truncated stream... "); - try - { - serializer.Read(new MemoryStream(data, 0, 10)); - Console.WriteLine("FAIL (no exception)"); - } - catch (Exception ex) when (ex is InvalidDataException or EndOfStreamException) - { - Console.WriteLine($"OK ({ex.GetType().Name})"); - } - - Console.WriteLine("\n=== All tests passed ==="); - } - - private static void RunCompositorTest() - { - Console.WriteLine("\n=== Compositor Test ===\n"); - - var compositor = new Compositor(); - const int W = 2, H = 2; - - // Document: 2 layers on a 2x2 canvas - var doc = new MinintDocument("test"); - doc.Palette.Add(new RgbaColor(255, 0, 0, 255)); // idx 1 = opaque red - doc.Palette.Add(new RgbaColor(0, 0, 255, 128)); // idx 2 = semi-transparent blue - - // Bottom layer: all red - var bottom = new MinintLayer("bottom", W * H); - for (int i = 0; i < bottom.Pixels.Length; i++) - bottom.Pixels[i] = 1; - doc.Layers.Add(bottom); - - // Top layer: pixel[0] = semi-blue, rest transparent - var top = new MinintLayer("top", W * H); - top.Pixels[0] = 2; - doc.Layers.Add(top); - - uint[] result = compositor.Composite(doc, W, H); - - // Pixel [1],[2],[3]: only bottom visible → opaque red → 0xFFFF0000 (ARGB) - uint opaqueRed = 0xFF_FF_00_00; - Assert(result[1] == opaqueRed, $"Pixel[1]: expected {opaqueRed:X8}, got {result[1]:X8}"); - Assert(result[2] == opaqueRed, $"Pixel[2]: expected {opaqueRed:X8}, got {result[2]:X8}"); - Assert(result[3] == opaqueRed, $"Pixel[3]: expected {opaqueRed:X8}, got {result[3]:X8}"); - - // Pixel [0]: red(255,0,0,255) under blue(0,0,255,128) - // srcA=128, dstA=255 → outA = 128 + 255*(255-128)/255 = 128+127 = 255 - // outR = (0*128 + 255*255*(127)/255) / 255 = (0 + 255*127)/255 = 127 - // outG = 0 - // outB = (255*128 + 0) / 255 = 128 - uint blended = result[0]; - byte bA = (byte)(blended >> 24); - byte bR = (byte)((blended >> 16) & 0xFF); - byte bG = (byte)((blended >> 8) & 0xFF); - byte bB = (byte)(blended & 0xFF); - - Console.WriteLine($" Blended pixel[0]: A={bA} R={bR} G={bG} B={bB}"); - Assert(bA == 255, $"Pixel[0] A: expected 255, got {bA}"); - Assert(bR >= 125 && bR <= 129, $"Pixel[0] R: expected ~127, got {bR}"); - Assert(bG == 0, $"Pixel[0] G: expected 0, got {bG}"); - Assert(bB >= 126 && bB <= 130, $"Pixel[0] B: expected ~128, got {bB}"); - - // Test hidden layer: hide top, result should be all red - top.IsVisible = false; - uint[] result2 = compositor.Composite(doc, W, H); - Assert(result2[0] == opaqueRed, $"Hidden top: Pixel[0] should be red, got {result2[0]:X8}"); - - // Test layer opacity=0: make top visible but opacity=0 - top.IsVisible = true; - top.Opacity = 0; - uint[] result3 = compositor.Composite(doc, W, H); - Assert(result3[0] == opaqueRed, $"Opacity 0: Pixel[0] should be red, got {result3[0]:X8}"); - - // Test single transparent layer - var emptyDoc = new MinintDocument("empty"); - emptyDoc.Layers.Add(new MinintLayer("bg", W * H)); - uint[] result4 = compositor.Composite(emptyDoc, W, H); - Assert(result4[0] == 0, $"Empty layer: Pixel[0] should be 0x00000000, got {result4[0]:X8}"); - - Console.WriteLine("✓ Compositor tests passed!"); - } - - private static void RunPaletteServiceTest() - { - Console.WriteLine("\n=== PaletteService Test ===\n"); - - var svc = new PaletteService(); - var doc = new MinintDocument("test"); - - // Palette starts with [Transparent] - Assert(doc.Palette.Count == 1, "Initial palette should have 1 entry"); - - // EnsureColor: new color - var red = new RgbaColor(255, 0, 0, 255); - int redIdx = svc.EnsureColor(doc, red); - Assert(redIdx == 1, $"Red index: expected 1, got {redIdx}"); - Assert(doc.Palette.Count == 2, $"Palette count after red: expected 2, got {doc.Palette.Count}"); - - // EnsureColor: same color → same index - int redIdx2 = svc.EnsureColor(doc, red); - Assert(redIdx2 == 1, $"Red re-ensure: expected 1, got {redIdx2}"); - Assert(doc.Palette.Count == 2, "Palette should not grow on duplicate"); - - // FindColor - Assert(svc.FindColor(doc, red) == 1, "FindColor red"); - Assert(svc.FindColor(doc, new RgbaColor(0, 0, 0, 255)) == -1, "FindColor missing"); - - // Compact: add unused color, then compact - var green = new RgbaColor(0, 255, 0, 255); - int greenIdx = svc.EnsureColor(doc, green); // idx 2 - doc.Layers.Add(new MinintLayer("L", 4)); - doc.Layers[0].Pixels[0] = 1; // red used - doc.Layers[0].Pixels[1] = 0; // transparent used - // green (idx 2) is NOT used by any pixel - - Console.WriteLine($" Before compact: palette has {doc.Palette.Count} colors (green at idx {greenIdx})"); - svc.CompactPalette(doc); - Console.WriteLine($" After compact: palette has {doc.Palette.Count} colors"); - - Assert(doc.Palette.Count == 2, $"After compact: expected 2 colors, got {doc.Palette.Count}"); - Assert(doc.Palette[0] == RgbaColor.Transparent, "Palette[0] should be transparent"); - Assert(doc.Palette[1] == red, $"Palette[1] should be red, got {doc.Palette[1]}"); - Assert(doc.Layers[0].Pixels[0] == 1, "Pixel[0] should still map to red (idx 1)"); - - Console.WriteLine("✓ PaletteService tests passed!"); - } - - private static void Assert(bool condition, string message) - { - if (!condition) - throw new Exception($"Assertion failed: {message}"); - } } diff --git a/Minint/ViewModels/EditorViewModel.cs b/Minint/ViewModels/EditorViewModel.cs index 4d3f3ed..bbca4b5 100644 --- a/Minint/ViewModels/EditorViewModel.cs +++ b/Minint/ViewModels/EditorViewModel.cs @@ -21,10 +21,8 @@ public sealed record ClipboardFragment(int Width, int Height, RgbaColor[] Pixels public partial class EditorViewModel : ViewModelBase { private readonly ICompositor _compositor = new Compositor(); - private readonly IPaletteService _paletteService = new PaletteService(); private readonly IDrawingService _drawingService = new DrawingService(); private readonly IFloodFillService _floodFillService = new FloodFillService(); - private readonly IFragmentService _fragmentService = new FragmentService(); [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasContainer))] @@ -740,7 +738,5 @@ public partial class EditorViewModel : ViewModelBase #endregion public ICompositor Compositor => _compositor; - public IPaletteService PaletteService => _paletteService; public IDrawingService DrawingService => _drawingService; - public IFragmentService FragmentService => _fragmentService; } diff --git a/README.md b/README.md index 5267c25..c535b67 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ dotnet run --project Minint dotnet test ``` -37 тестов покрывают: сериализацию (round-trip), композитинг, инструменты рисования, flood fill, эффекты (контраст, grayscale), генерацию паттернов, копирование фрагмента, экспорт BMP/GIF. +34 теста покрывают: сериализацию (round-trip), композитинг, инструменты рисования, flood fill, эффекты (контраст, grayscale), генерацию паттернов, экспорт BMP/GIF. ## Управление