Selaa lähdekoodia

Added anti aliased rectangle

flabbet 10 kuukautta sitten
vanhempi
commit
5440b83831

+ 3 - 0
src/ChunkyImageLib/DataHolders/ShapeData.cs

@@ -27,6 +27,9 @@ public record struct ShapeData
     public double Angle { get; }
     public int StrokeWidth { get; }
 
+    public bool AntiAliasing { get; set; } = false;
+    
+
     public ShapeData AsMirroredAcrossHorAxis(double horAxisY)
         => new ShapeData(Center.ReflectY(horAxisY), new(Size.X, -Size.Y), -Angle, StrokeWidth, StrokeColor, FillColor, BlendMode);
     public ShapeData AsMirroredAcrossVerAxis(double verAxisX)

+ 75 - 24
src/ChunkyImageLib/Operations/RectangleOperation.cs

@@ -1,25 +1,30 @@
 using ChunkyImageLib.DataHolders;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 
 internal class RectangleOperation : IMirroredDrawOperation
 {
+    public ShapeData Data { get; }
+
+    public bool IgnoreEmptyChunks => false;
+
+    private Paint paint = new();
+
     public RectangleOperation(ShapeData rect)
     {
         Data = rect;
+        paint.StrokeWidth = Data.StrokeWidth;
+        paint.IsAntiAliased = Data.AntiAliasing;
+        paint.BlendMode = Data.BlendMode;
     }
 
-    public ShapeData Data { get; }
-
-    public bool IgnoreEmptyChunks => false;
 
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
-        var skiaSurf = targetChunk.Surface.DrawingSurface;
-
         var surf = targetChunk.Surface.DrawingSurface;
 
         var rect = RectD.FromCenterAndSize(Data.Center, Data.Size.Abs());
@@ -27,56 +32,102 @@ internal class RectangleOperation : IMirroredDrawOperation
         if (innerRect.IsZeroOrNegativeArea)
             innerRect = RectD.Empty;
 
-        surf.Canvas.Save();
+        int initial = surf.Canvas.Save();
+
+
         surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
         surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);
-        skiaSurf.Canvas.RotateRadians((float)Data.Angle, (float)rect.Center.X, (float)rect.Center.Y);
+        surf.Canvas.RotateRadians((float)Data.Angle, (float)rect.Center.X, (float)rect.Center.Y);
+
+        if (Data.AntiAliasing)
+        {
+            DrawAntiAliased(surf, rect, innerRect);
+        }
+        else
+        {
+            DrawPixelPerfect(surf, rect, innerRect);
+        }
 
+        surf.Canvas.RestoreToCount(initial);
+    }
+
+    private void DrawPixelPerfect(DrawingSurface surf, RectD rect, RectD innerRect)
+    {
         // draw fill
         if (Data.FillColor.A > 0)
         {
-            skiaSurf.Canvas.Save();
-            skiaSurf.Canvas.ClipRect(innerRect);
-            skiaSurf.Canvas.DrawColor(Data.FillColor, Data.BlendMode);
-            skiaSurf.Canvas.Restore();
+            int saved = surf.Canvas.Save();
+            surf.Canvas.ClipRect(innerRect);
+            surf.Canvas.DrawColor(Data.FillColor, Data.BlendMode);
+            surf.Canvas.RestoreToCount(saved);
         }
 
         // draw stroke
-        skiaSurf.Canvas.Save();
-        skiaSurf.Canvas.ClipRect(rect);
-        skiaSurf.Canvas.ClipRect(innerRect, ClipOperation.Difference);
-        skiaSurf.Canvas.DrawColor(Data.StrokeColor, Data.BlendMode);
-        skiaSurf.Canvas.Restore();
+        surf.Canvas.Save();
+        surf.Canvas.ClipRect(rect);
+        surf.Canvas.ClipRect(innerRect, ClipOperation.Difference);
+        surf.Canvas.DrawColor(Data.StrokeColor, Data.BlendMode);
+    }
 
-        surf.Canvas.Restore();
+    private void DrawAntiAliased(DrawingSurface surf, RectD rect, RectD innerRect)
+    {
+        // draw fill
+        if (Data.FillColor.A > 0)
+        {
+            int saved = surf.Canvas.Save();
+
+            paint.StrokeWidth = 0;
+            paint.Color = Data.FillColor;
+            paint.Style = PaintStyle.Fill;
+            surf.Canvas.DrawRect((float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height, paint);
+
+            surf.Canvas.RestoreToCount(saved);
+        }
+
+        // draw stroke
+        surf.Canvas.Save();
+        paint.StrokeWidth = (float)Data.StrokeWidth;
+        paint.Color = Data.StrokeColor;
+        paint.Style = PaintStyle.Stroke;
+        surf.Canvas.DrawRect((float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height, paint);
     }
 
     public AffectedArea FindAffectedArea(VecI imageSize)
     {
-        if (Math.Abs(Data.Size.X) < 1 || Math.Abs(Data.Size.Y) < 1 || (Data.StrokeColor.A == 0 && Data.FillColor.A == 0))
+        if (Math.Abs(Data.Size.X) < 1 || Math.Abs(Data.Size.Y) < 1 ||
+            (Data.StrokeColor.A == 0 && Data.FillColor.A == 0))
             return new();
 
-        RectI affRect = (RectI)new ShapeCorners(Data.Center, Data.Size).AsRotated(Data.Angle, Data.Center).AABBBounds.RoundOutwards();
+        RectI affRect = (RectI)new ShapeCorners(Data.Center, Data.Size).AsRotated(Data.Angle, Data.Center).AABBBounds
+            .RoundOutwards();
 
         if (Data.FillColor.A != 0 || Math.Abs(Data.Size.X) == 1 || Math.Abs(Data.Size.Y) == 1)
-            return new (OperationHelper.FindChunksTouchingRectangle(Data.Center, Data.Size.Abs(), Data.Angle, ChunkPool.FullChunkSize), affRect);
+            return new(
+                OperationHelper.FindChunksTouchingRectangle(Data.Center, Data.Size.Abs(), Data.Angle,
+                    ChunkPool.FullChunkSize), affRect);
 
-        var chunks = OperationHelper.FindChunksTouchingRectangle(Data.Center, Data.Size.Abs(), Data.Angle, ChunkPool.FullChunkSize);
+        var chunks =
+            OperationHelper.FindChunksTouchingRectangle(Data.Center, Data.Size.Abs(), Data.Angle,
+                ChunkPool.FullChunkSize);
         chunks.ExceptWith(
             OperationHelper.FindChunksFullyInsideRectangle(
                 Data.Center,
                 Data.Size.Abs() - new VecD(Data.StrokeWidth * 2, Data.StrokeWidth * 2),
                 Data.Angle,
                 ChunkPool.FullChunkSize));
-        return new (chunks, affRect);
+        return new(chunks, affRect);
     }
 
-    public void Dispose() { }
+    public void Dispose()
+    {
+        paint.Dispose();
+    }
 
     public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
     {
         if (verAxisX is not null && horAxisY is not null)
-            return new RectangleOperation(Data.AsMirroredAcrossHorAxis((double)horAxisY).AsMirroredAcrossVerAxis((double)verAxisX));
+            return new RectangleOperation(Data.AsMirroredAcrossHorAxis((double)horAxisY)
+                .AsMirroredAcrossVerAxis((double)verAxisX));
         else if (verAxisX is not null)
             return new RectangleOperation(Data.AsMirroredAcrossVerAxis((double)verAxisX));
         else if (horAxisY is not null)

+ 2 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ComplexShapeToolExecutor.cs

@@ -29,7 +29,7 @@ internal abstract class ComplexShapeToolExecutor<T> : SimpleShapeToolExecutor wh
     protected double lastRadians;
 
     private bool noMovement = true;
-    private IBasicShapeToolbar toolbar;
+    protected IBasicShapeToolbar toolbar;
     private IColorsHandler? colorsVM;
 
     public override bool CanUndo => document.TransformHandler.HasUndo;
@@ -141,7 +141,7 @@ internal abstract class ComplexShapeToolExecutor<T> : SimpleShapeToolExecutor wh
 
         var rect = RectD.FromCenterAndSize(corners.RectCenter, corners.RectSize);
         ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, StrokeWidth, StrokeColor,
-            FillColor);
+            FillColor) { AntiAliasing = toolbar.AntiAliasing };
         IAction drawAction = TransformMovedAction(shapeData, corners);
 
         internals!.ActionAccumulator.AddActions(drawAction);

+ 8 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs

@@ -25,7 +25,10 @@ internal class RasterRectangleToolExecutor : ComplexShapeToolExecutor<IRasterRec
         lastRect = rect;
         lastRadians = rotationRad;
         
-        lastData = new ShapeData(rect.Center, rect.Size, rotationRad, StrokeWidth, StrokeColor, FillColor);
+        lastData = new ShapeData(rect.Center, rect.Size, rotationRad, StrokeWidth, StrokeColor, FillColor)
+        {
+            AntiAliasing = toolbar.AntiAliasing
+        };
 
         internals!.ActionAccumulator.AddActions(new DrawRasterRectangle_Action(memberId, lastData, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
     }
@@ -33,7 +36,10 @@ internal class RasterRectangleToolExecutor : ComplexShapeToolExecutor<IRasterRec
     protected override void DrawShape(VecI currentPos, double rotationRad, bool first) => DrawRectangle(currentPos, rotationRad, first);
     protected override IAction SettingsChangedAction()
     {
-        lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, StrokeWidth, StrokeColor, FillColor);
+        lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, StrokeWidth, StrokeColor, FillColor)
+        {
+            AntiAliasing = toolbar.AntiAliasing
+        };
         return new DrawRasterRectangle_Action(memberId, lastData, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);   
     }