Browse Source

Line anti aliasing

flabbet 10 months ago
parent
commit
0310b1a2d1

+ 10 - 0
src/ChunkyImageLib/ChunkyImage.cs

@@ -738,6 +738,16 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         }
     }
 
+    public void EnqueueDrawSkiaLine(VecI from, VecI to, Paint paint)
+    {
+        lock (lockObject)
+        {
+            ThrowIfDisposed();
+            DrawingSurfaceLineOperation operation = new(from, to, paint);
+            EnqueueOperation(operation);
+        }
+    }
+
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
     public void EnqueueDrawPixels(IEnumerable<VecI> pixels, Color color, BlendMode blendMode)
     {

+ 10 - 1
src/ChunkyImageLib/Operations/DrawingSurfaceLineOperation.cs

@@ -13,6 +13,7 @@ internal class DrawingSurfaceLineOperation : IMirroredDrawOperation
     private Paint paint;
     private readonly VecI from;
     private readonly VecI to;
+    private bool isAntiAliased;
 
     public DrawingSurfaceLineOperation(VecI from, VecI to, StrokeCap strokeCap, float strokeWidth, Color color, BlendMode blendMode)
     {
@@ -27,10 +28,18 @@ internal class DrawingSurfaceLineOperation : IMirroredDrawOperation
         this.from = from;
         this.to = to;
     }
+    
+    public DrawingSurfaceLineOperation(VecI from, VecI to, Paint paint)
+    {
+        this.paint = paint.Clone();
+        this.from = from;
+        this.to = to;
+        isAntiAliased = paint.IsAntiAliased;
+    }
 
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
-        paint.IsAntiAliased = targetChunk.Resolution != ChunkResolution.Full;
+        paint.IsAntiAliased = isAntiAliased || targetChunk.Resolution != ChunkResolution.Full;
         var surf = targetChunk.Surface.DrawingSurface;
         surf.Canvas.Save();
         surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());

+ 18 - 3
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRasterLine_UpdateableChange.cs

@@ -16,10 +16,12 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
     private readonly bool drawOnMask;
     private CommittedChunkStorage? savedChunks;
     private int frame;
+    private bool antiAliasing;
+    private Paint paint;
 
     [GenerateUpdateableChangeActions]
     public DrawRasterLine_UpdateableChange
-        (Guid memberGuid, VecI from, VecI to, int strokeWidth, Color color, StrokeCap caps, bool drawOnMask, int frame)
+        (Guid memberGuid, VecI from, VecI to, int strokeWidth, Color color, StrokeCap caps, bool antiAliasing, bool drawOnMask, int frame)
     {
         this.memberGuid = memberGuid;
         this.from = from;
@@ -29,6 +31,10 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
         this.caps = caps;
         this.drawOnMask = drawOnMask;
         this.frame = frame;
+        this.antiAliasing = antiAliasing;
+
+        paint = new Paint() { Color = color, 
+            StrokeWidth = strokeWidth, StrokeCap = caps, IsAntiAliased = antiAliasing, BlendMode = BlendMode.SrcOver };
     }
 
     [UpdateChangeMethod]
@@ -39,6 +45,10 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
         this.color = color;
         this.caps = caps;
         this.strokeWidth = strokeWidth;
+        
+        paint.Color = color;
+        paint.StrokeWidth = strokeWidth;
+        paint.StrokeCap = caps;
     }
 
     public override bool InitializeAndValidate(Document target)
@@ -54,10 +64,14 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
         if (from != to)
         {
             DrawingChangeHelper.ApplyClipsSymmetriesEtc(target, image, memberGuid, drawOnMask);
-            if (strokeWidth == 1)
+            if (strokeWidth == 1 && !antiAliasing)
+            {
                 image.EnqueueDrawBresenhamLine(from, to, color, BlendMode.SrcOver);
+            }
             else
-                image.EnqueueDrawSkiaLine(from, to, caps, strokeWidth, color, BlendMode.SrcOver);
+            {
+                image.EnqueueDrawSkiaLine(from, to, paint);
+            }
         }
         var totalAffected = image.FindAffectedArea();
         totalAffected.UnionWith(oldAffected);
@@ -98,5 +112,6 @@ internal class DrawRasterLine_UpdateableChange : UpdateableChange
     public override void Dispose()
     {
         savedChunks?.Dispose();
+        paint?.Dispose();
     }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -24,7 +24,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     private bool startedDrawing = false;
     private T? toolViewModel;
     private IColorsHandler? colorsVM;
-    private ILineToolbar? toolbar;
+    protected ILineToolbar? toolbar;
 
     public override bool CanUndo => document.LineToolOverlayHandler.HasUndo; 
     public override bool CanRedo => document.LineToolOverlayHandler.HasRedo;

+ 6 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterLineToolExecutor.cs

@@ -19,7 +19,7 @@ internal class RasterLineToolExecutor : LineExecutor<ILineToolHandler>
         VecD dir = GetSignedDirection(startDrawingPos, pos);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
         return new DrawRasterLine_Action(memberId, ToPixelPos(startDrawingPos, oppositeDir), ToPixelPos(pos, dir), StrokeWidth,
-            StrokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction TransformOverlayMoved(VecD start, VecD end)
@@ -27,7 +27,7 @@ internal class RasterLineToolExecutor : LineExecutor<ILineToolHandler>
         VecD dir = GetSignedDirection(start, end);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
         return new DrawRasterLine_Action(memberId, ToPixelPos(start, oppositeDir), ToPixelPos(end, dir), 
-            StrokeWidth, StrokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            StrokeWidth, StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction SettingsChange()
@@ -35,15 +35,18 @@ internal class RasterLineToolExecutor : LineExecutor<ILineToolHandler>
         VecD dir = GetSignedDirection(startDrawingPos, curPos);
         VecD oppositeDir = new VecD(-dir.X, -dir.Y);
         return new DrawRasterLine_Action(memberId, ToPixelPos(startDrawingPos, oppositeDir), ToPixelPos(curPos, dir), StrokeWidth,
-            StrokeColor, StrokeCap.Butt, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            StrokeColor, StrokeCap.Butt, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     private VecI ToPixelPos(VecD pos, VecD dir)
     {
+        if (StrokeWidth > 1) return (VecI)pos.Round();
+        
         double xAdjustment = dir.X > 0 ? 0.5 : -0.5;
         double yAdjustment = dir.Y > 0 ? 0.5 : -0.5;
         
         VecD adjustment = new VecD(xAdjustment, yAdjustment);
+
         
         VecI finalPos = (VecI)(pos - adjustment);
 

+ 1 - 0
src/PixiEditor/Models/Handlers/Toolbars/IBasicShapeToolbar.cs

@@ -8,4 +8,5 @@ internal interface IBasicShapeToolbar : IBasicToolbar
     public bool Fill { get; set; }
     public Color FillColor { get; set; }
     public bool SyncWithPrimaryColor { get; set; }
+    public bool AntiAliasing { get; set; }
 }

+ 1 - 0
src/PixiEditor/Models/Handlers/Toolbars/ILineToolbar.cs

@@ -5,4 +5,5 @@ namespace PixiEditor.Models.Handlers.Toolbars;
 internal interface ILineToolbar : IBasicToolbar
 {
     public Color StrokeColor { get; set; }
+    public bool AntiAliasing { get; set; }
 }

+ 14 - 1
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/BasicShapeToolbar.cs

@@ -52,7 +52,19 @@ internal class BasicShapeToolbar : BasicToolbar, IBasicShapeToolbar
         {
             GetSetting<BoolSettingViewModel>(nameof(SyncWithPrimaryColor)).Value = value;
         }
-    } 
+    }
+
+    public bool AntiAliasing
+    {
+        get
+        {
+            return GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value;
+        }
+        set
+        {
+            GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value = value;
+        }
+    }
 
     public BasicShapeToolbar()
     {
@@ -60,5 +72,6 @@ internal class BasicShapeToolbar : BasicToolbar, IBasicShapeToolbar
         AddSetting(new BoolSettingViewModel(nameof(Fill), "FILL_SHAPE_LABEL") { Value = true });
         AddSetting(new ColorSettingViewModel(nameof(FillColor), "FILL_COLOR_LABEL"));
         AddSetting(new BoolSettingViewModel(nameof(SyncWithPrimaryColor), "SYNC_WITH_PRIMARY_COLOR_LABEL") { Value = true });
+        AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL") { Value = false, IsExposed = false});
     }
 }

+ 13 - 0
src/PixiEditor/ViewModels/Tools/ToolSettings/Toolbars/LineToolbar.cs

@@ -19,8 +19,21 @@ internal class LineToolbar : BasicToolbar, ILineToolbar
         }
     }
 
+    public bool AntiAliasing
+    {
+        get
+        {
+            return GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value;
+        }
+        set
+        {
+            GetSetting<BoolSettingViewModel>(nameof(AntiAliasing)).Value = value;
+        }
+    }
+
     public LineToolbar()
     {
         AddSetting(new ColorSettingViewModel(nameof(StrokeColor), "STROKE_COLOR_LABEL"));
+        AddSetting(new BoolSettingViewModel(nameof(AntiAliasing), "ANTI_ALIASING_LABEL") { IsExposed = false, Value = false });
     }
 }

+ 8 - 7
src/PixiEditor/ViewModels/Tools/ToolViewModel.cs

@@ -148,7 +148,7 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
 
     public virtual void UseTool(VecD pos) { }
     public virtual void OnSelected(bool restoring) { }
-    
+
     protected virtual void OnSelectedLayersChanged(IStructureMemberHandler[] layers) { }
 
     public virtual void OnDeselecting(bool transient)
@@ -169,19 +169,20 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
                 settings[valueSetting.Key] = Convert.ToSingle(valueSetting.Value);
             }
         }
+
         ToolSetSettings[toolset] = settings;
     }
 
     public void ApplyToolSetSettings(IToolSetHandler toolset)
     {
-        if (!ToolSetSettings.TryGetValue(toolset, out var settings))
+        foreach (var toolbarSetting in Toolbar.Settings)
         {
-            return;
+            toolbarSetting.ResetOverwrite();
         }
 
-        foreach (var toolbarSetting in Toolbar.Settings)
+        if (!ToolSetSettings.TryGetValue(toolset, out var settings))
         {
-            toolbarSetting.ResetOverwrite();
+            return;
         }
 
         foreach (var setting in settings)
@@ -249,11 +250,11 @@ internal abstract class ToolViewModel : ObservableObject, IToolHandler
         }
         catch (InvalidCastException)
         {
-            if(typeof(T) == typeof(float) || typeof(T) == typeof(double) || typeof(T) == typeof(int))
+            if (typeof(T) == typeof(float) || typeof(T) == typeof(double) || typeof(T) == typeof(int))
             {
                 return (T)(object)Convert.ToSingle(setting.Value);
             }
-            
+
             throw;
         }
     }