Skip to content

Commit

Permalink
Merge branch 'quickfill' of github.com:PixiEditor/PixiEditor into qui…
Browse files Browse the repository at this point in the history
…ckfill
  • Loading branch information
CPKreu committed Dec 2, 2021
2 parents 55f3377 + 8cb2576 commit 9396971
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 96 deletions.
1 change: 0 additions & 1 deletion PixiEditor/Models/Controllers/BitmapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ public void HighlightPixels(Coordinates newPosition)
_highlightPen.Draw(previewLayer, cords, cords, _highlightColor, _tools.ToolSize);

AdjustOffset(newPosition, previewLayer);

}

previewLayer.InvokeLayerBitmapChange();
Expand Down
6 changes: 6 additions & 0 deletions PixiEditor/Models/Position/Coordinates.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using SkiaSharp;
using System;
using System.Diagnostics;
using System.Windows;

namespace PixiEditor.Models.Position
{
Expand All @@ -26,6 +27,11 @@ public Coordinates(int x, int y)
return new Coordinates(tuple.width, tuple.height);
}

public static Coordinates operator -(Coordinates coordiantes, Thickness thickness)
{
return new Coordinates(coordiantes.X - (int)thickness.Left, coordiantes.Y - (int)thickness.Top);
}

public static Coordinates operator -(Coordinates coordiantes, int size)
{
return new Coordinates(coordiantes.X - size, coordiantes.Y - size);
Expand Down
13 changes: 13 additions & 0 deletions PixiEditor/Models/Tools/Brushes/Brush.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using PixiEditor.Models.Layers;
using PixiEditor.Models.Position;
using SkiaSharp;
using System;
using System.Collections.Generic;

namespace PixiEditor.Models.Tools.Brushes
{
public abstract class Brush
{
public abstract void Draw(Layer layer, Tool tool, Coordinates coordinates, SKPaint paint);
}
}
23 changes: 23 additions & 0 deletions PixiEditor/Models/Tools/Brushes/CircleBrush.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using PixiEditor.Models.Layers;
using PixiEditor.Models.Position;
using PixiEditor.Models.Tools.Tools;
using PixiEditor.Models.Tools.ToolSettings.Settings;
using SkiaSharp;
using System;

namespace PixiEditor.Models.Tools.Brushes
{
public class CircleBrush : Brush
{
public override void Draw(Layer layer, Tool tool, Coordinates coordinates, SKPaint paint)
{
int toolSize = tool.Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
int halfSize = (int)Math.Ceiling(toolSize / 2f);
int modifier = toolSize % 2 != 0 ? 1 : 0;
Coordinates topLeft = new Coordinates(coordinates.X - halfSize + modifier, coordinates.Y - halfSize + modifier);
Coordinates bottomRight = new Coordinates(coordinates.X + halfSize - 1, coordinates.Y + halfSize - 1);

CircleTool.DrawEllipseFromRect(layer, topLeft, bottomRight, paint.Color, paint.Color, 1, true);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace PixiEditor.Models.Tools.Brushes
{
public class InterestingShapeBrush : MatrixBrush
{
public static readonly int[,] InterestingShapeMatrix = new int[,]
{
{ 1, 1, 1 },
{ 0, 1, 0 },
{ 0, 1, 0 }
};

public override int[,] BrushMatrix => InterestingShapeMatrix;
}
}
23 changes: 0 additions & 23 deletions PixiEditor/Models/Tools/Brushes/CrossBrush.cs

This file was deleted.

18 changes: 0 additions & 18 deletions PixiEditor/Models/Tools/Brushes/PointBrush.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using PixiEditor.Models.Layers;
using PixiEditor.Models.Position;
using PixiEditor.Models.Tools.Brushes;
using SkiaSharp;
using System;
using System.Collections.Generic;

namespace PixiEditor.Models.Tools.Brushes
namespace PixiEditor.Models.Tools
{
public abstract class CustomBrush
public abstract class MatrixBrush : Brush
{
public abstract int[,] BrushMatrix { get; }

Expand All @@ -16,11 +17,16 @@ public abstract class CustomBrush
private SKPoint[] _cachedBakedMatrix;
private SKPoint[] _cachedGetAtPointBakedMatrix;

public CustomBrush()
public MatrixBrush()
{
InitMatrix();
}

public override void Draw(Layer layer, Tool tool, Coordinates coordinates, SKPaint paint)
{
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoints(SKPointMode.Points, GetAtPoint(coordinates, layer.OffsetX, layer.OffsetY), paint);
}

//We can easily handle .pixi to brush parsing

/// <summary>
Expand Down Expand Up @@ -63,7 +69,7 @@ public SKPoint[] GetAtPoint(Coordinates point, int offsetX, int offsetY)
InitMatrix();
}

for(int i = 0; i < _cachedGetAtPointBakedMatrix.Length; i++)
for (int i = 0; i < _cachedGetAtPointBakedMatrix.Length; i++)
{
_cachedGetAtPointBakedMatrix[i] = new SKPoint(
_cachedBakedMatrix[i].X + point.X - offsetX,
Expand All @@ -83,5 +89,6 @@ private void InitMatrix()
Array.Copy(_cachedBakedMatrix, _cachedGetAtPointBakedMatrix, BakedMatrix.Length);
}
}

}
}
2 changes: 1 addition & 1 deletion PixiEditor/Models/Tools/ShapeTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public ShapeTool()
// TODO: Add cache for lines 31, 32 (hopefully it would speed up calculation)
public abstract override void Use(Layer layer, List<Coordinates> coordinates, SKColor color);

protected static void ThickenShape(Layer layer, SKColor color, IEnumerable<Coordinates> shape, int thickness)
public static void ThickenShape(Layer layer, SKColor color, IEnumerable<Coordinates> shape, int thickness)
{
foreach (Coordinates item in shape)
{
Expand Down
168 changes: 157 additions & 11 deletions PixiEditor/Models/Tools/Tools/CircleTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
Expand Down Expand Up @@ -39,7 +40,15 @@ public override void Use(Layer layer, List<Coordinates> coordinates, SKColor col
{
int thickness = Toolbar.GetSetting<SizeSetting>("ToolSize").Value;
var hasFillColor = Toolbar.GetSetting<BoolSetting>("Fill").Value;
DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(coordinates[^1], coordinates[0]);
Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
SKColor fill = new SKColor(temp.R, temp.G, temp.B, temp.A);
DrawEllipseFromRect(layer, coordinates[^1], coordinates[0], color, fill, thickness, hasFillColor);
}

public static void DrawEllipseFromRect(Layer layer, Coordinates first, Coordinates second,
SKColor color, SKColor fillColor, int thickness, bool hasFillColor)
{
DoubleCords fixedCoordinates = CalculateCoordinatesForShapeRotation(first, second);

int halfThickness = (int)Math.Ceiling(thickness / 2.0);
Int32Rect dirtyRect = new Int32Rect(
Expand All @@ -53,28 +62,165 @@ public override void Use(Layer layer, List<Coordinates> coordinates, SKColor col
{
float radiusX = (fixedCoordinates.Coords2.X - fixedCoordinates.Coords1.X) / 2.0f;
float radiusY = (fixedCoordinates.Coords2.Y - fixedCoordinates.Coords1.Y) / 2.0f;
float centerX = (fixedCoordinates.Coords1.X + fixedCoordinates.Coords2.X + 1) / 2.0f - layer.OffsetX;
float centerY = (fixedCoordinates.Coords1.Y + fixedCoordinates.Coords2.Y + 1) / 2.0f - layer.OffsetY;
float centerX = (fixedCoordinates.Coords1.X + fixedCoordinates.Coords2.X + 1) / 2.0f;
float centerY = (fixedCoordinates.Coords1.Y + fixedCoordinates.Coords2.Y + 1) / 2.0f;
paint.BlendMode = SKBlendMode.Src;

paint.Color = color;
paint.StrokeWidth = thickness;
paint.Style = SKPaintStyle.Stroke;
var outline = DrawEllipse(layer, color, centerX, centerY, radiusX, radiusY, thickness);

if (hasFillColor)
{
Color temp = Toolbar.GetSetting<ColorSetting>("FillColor").Value;
SKColor fillColor = new SKColor(temp.R, temp.G, temp.B, temp.A);

paint.Color = fillColor;
paint.Style = SKPaintStyle.StrokeAndFill;
layer.LayerBitmap.SkiaSurface.Canvas.DrawOval(centerX, centerY, radiusX, radiusY, paint);
DrawEllipseFill(layer, fillColor, outline);
}

paint.Color = color;
paint.StrokeWidth = thickness;
paint.Style = SKPaintStyle.Stroke;
layer.LayerBitmap.SkiaSurface.Canvas.DrawOval(centerX, centerY, radiusX, radiusY, paint);
// An Idea, use Skia DrawOval for bigger sizes.
}

layer.InvokeLayerBitmapChange(dirtyRect);
}

/// <summary>
/// Calculates ellipse points for specified coordinates and thickness.
/// </summary>
/// <param name="thickness">Thickness of ellipse.</param>
public static IEnumerable<Coordinates> DrawEllipse(Layer layer, SKColor color, float centerX, float centerY, float radiusX, float radiusY, int thickness)
{

IEnumerable<Coordinates> ellipse = GenerateMidpointEllipse(layer, color, radiusX, radiusY, centerX, centerY);
if (thickness > 1)
{
ShapeTool.ThickenShape(layer, color, ellipse, thickness);
}

return ellipse;
}

public static List<Coordinates> GenerateMidpointEllipse(Layer layer, SKColor color, double halfWidth, double halfHeight, double centerX, double centerY)
{
if (halfWidth < 1 || halfHeight < 1)
{
return DrawFallbackRectangle(layer, color, halfWidth, halfHeight, centerX, centerY);
}

// ellipse formula: halfHeight^2 * x^2 + halfWidth^2 * y^2 - halfHeight^2 * halfWidth^2 = 0

// Make sure we are always at the center of a pixel
double currentX = Math.Ceiling(centerX - 0.5) + 0.5;
double currentY = centerY + halfHeight;

List<Coordinates> outputCoordinates = new List<Coordinates>();

double currentSlope;

// from PI/2 to middle
do
{
outputCoordinates.AddRange(DrawRegionPoints(layer, color, currentX, centerX, currentY, centerY));

// calculate next pixel coords
currentX++;

if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX, 2)) +
(Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY - 0.5, 2)) -
(Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2)) >= 0)
{
currentY--;
}

// calculate how far we've advanced
double derivativeX = 2 * Math.Pow(halfHeight, 2) * (currentX - centerX);
double derivativeY = 2 * Math.Pow(halfWidth, 2) * (currentY - centerY);
currentSlope = -(derivativeX / derivativeY);
}
while (currentSlope > -1 && currentY - centerY > 0.5);

// from middle to 0
while (currentY - centerY >= 0)
{
outputCoordinates.AddRange(DrawRegionPoints(layer, color, currentX, centerX, currentY, centerY));

currentY--;
if ((Math.Pow(halfHeight, 2) * Math.Pow(currentX - centerX + 0.5, 2)) +
(Math.Pow(halfWidth, 2) * Math.Pow(currentY - centerY, 2)) -
(Math.Pow(halfWidth, 2) * Math.Pow(halfHeight, 2)) < 0)
{
currentX++;
}
}

return outputCoordinates;
}

public static void DrawEllipseFill(Layer layer, SKColor color, IEnumerable<Coordinates> outlineCoordinates)
{
if (!outlineCoordinates.Any())
{
return;
}

int bottom = outlineCoordinates.Max(x => x.Y);
int top = outlineCoordinates.Min(x => x.Y);
for (int i = top + 1; i < bottom; i++)
{
IEnumerable<Coordinates> rowCords = outlineCoordinates.Where(x => x.Y == i);
int right = rowCords.Max(x => x.X);
int left = rowCords.Min(x => x.X);
for (int j = left + 1; j < right; j++)
{
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(j - layer.OffsetX, i - layer.OffsetY), color);
}
}
}

private static List<Coordinates> DrawFallbackRectangle(Layer layer, SKColor color, double halfWidth, double halfHeight, double centerX, double centerY)
{
List<Coordinates> coordinates = new List<Coordinates>();

for (double x = centerX - halfWidth; x <= centerX + halfWidth; x++)
{
var cords = new Coordinates((int)x, (int)(centerY - halfHeight));
coordinates.Add(cords);
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(cords.X - layer.OffsetX, cords.Y - layer.OffsetY), color);

cords = new Coordinates((int)x, (int)(centerY + halfHeight));
coordinates.Add(cords);
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(cords.X - layer.OffsetX, cords.Y - layer.OffsetY), color);

}

for (double y = centerY - halfHeight + 1; y <= centerY + halfHeight - 1; y++)
{
var cords = new Coordinates((int)(centerX - halfWidth), (int)y);
coordinates.Add(cords);
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(cords.X - layer.OffsetX, cords.Y - layer.OffsetY), color);

cords = new Coordinates((int)(centerX + halfWidth), (int)y);
coordinates.Add(cords);
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(cords.X - layer.OffsetX, cords.Y - layer.OffsetY), color);

}

return coordinates;
}

private static Coordinates[] DrawRegionPoints(Layer layer, SKColor color, double x, double xc, double y, double yc)
{
Coordinates[] outputCoordinates = new Coordinates[4];
outputCoordinates[0] = new Coordinates((int)Math.Floor(x), (int)Math.Floor(y));
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(outputCoordinates[0].X - layer.OffsetX, outputCoordinates[0].Y - layer.OffsetY), color);
outputCoordinates[1] = new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(y));
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(outputCoordinates[1].X - layer.OffsetX, outputCoordinates[1].Y - layer.OffsetY), color);
outputCoordinates[2] = new Coordinates((int)Math.Floor(x), (int)Math.Floor(-(y - yc) + yc));
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(outputCoordinates[2].X - layer.OffsetX, outputCoordinates[2].Y - layer.OffsetY), color);
outputCoordinates[3] = new Coordinates((int)Math.Floor(-(x - xc) + xc), (int)Math.Floor(-(y - yc) + yc));
layer.LayerBitmap.SkiaSurface.Canvas.DrawPoint(new SKPoint(outputCoordinates[3].X - layer.OffsetX, outputCoordinates[3].Y - layer.OffsetY), color);

return outputCoordinates;
}
}
}

0 comments on commit 9396971

Please sign in to comment.