EllipseOperation.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using ChunkyImageLib.DataHolders;
  2. using PixiEditor.DrawingApi.Core.ColorsImpl;
  3. using PixiEditor.DrawingApi.Core.Numerics;
  4. using PixiEditor.DrawingApi.Core.Surface;
  5. using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
  6. using PixiEditor.DrawingApi.Core.Surface.Vector;
  7. namespace ChunkyImageLib.Operations;
  8. internal class EllipseOperation : IMirroredDrawOperation
  9. {
  10. public bool IgnoreEmptyChunks => false;
  11. private readonly RectI location;
  12. private readonly Color strokeColor;
  13. private readonly Color fillColor;
  14. private readonly int strokeWidth;
  15. private readonly Paint paint;
  16. private bool init = false;
  17. private VectorPath? outerPath;
  18. private VectorPath? innerPath;
  19. private Point[]? ellipse;
  20. private Point[]? ellipseFill;
  21. private RectI? ellipseFillRect;
  22. public EllipseOperation(RectI location, Color strokeColor, Color fillColor, int strokeWidth, Paint? paint = null)
  23. {
  24. this.location = location;
  25. this.strokeColor = strokeColor;
  26. this.fillColor = fillColor;
  27. this.strokeWidth = strokeWidth;
  28. this.paint = paint?.Clone() ?? new Paint();
  29. }
  30. private void Init()
  31. {
  32. init = true;
  33. if (strokeWidth == 1)
  34. {
  35. var ellipseList = EllipseHelper.GenerateEllipseFromRect(location);
  36. ellipse = ellipseList.Select(a => new Point(a)).ToArray();
  37. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  38. {
  39. (var fill, ellipseFillRect) = EllipseHelper.SplitEllipseFillIntoRegions(ellipseList, location);
  40. ellipseFill = fill.Select(a => new Point(a)).ToArray();
  41. }
  42. }
  43. else
  44. {
  45. outerPath = new VectorPath();
  46. outerPath.ArcTo(location, 0, 359, true);
  47. innerPath = new VectorPath();
  48. innerPath.ArcTo(location.Inflate(-strokeWidth), 0, 359, true);
  49. }
  50. }
  51. public void DrawOnChunk(Chunk chunk, VecI chunkPos)
  52. {
  53. if (!init)
  54. Init();
  55. var surf = chunk.Surface.DrawingSurface;
  56. surf.Canvas.Save();
  57. surf.Canvas.Scale((float)chunk.Resolution.Multiplier());
  58. surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);
  59. paint.IsAntiAliased = chunk.Resolution != ChunkResolution.Full;
  60. if (strokeWidth == 1)
  61. {
  62. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  63. {
  64. paint.Color = fillColor;
  65. surf.Canvas.DrawPoints(PointMode.Lines, ellipseFill!, paint);
  66. surf.Canvas.DrawRect(ellipseFillRect!.Value, paint);
  67. }
  68. paint.Color = strokeColor;
  69. surf.Canvas.DrawPoints(PointMode.Points, ellipse!, paint);
  70. }
  71. else
  72. {
  73. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  74. {
  75. surf.Canvas.Save();
  76. surf.Canvas.ClipPath(innerPath!);
  77. surf.Canvas.DrawColor(fillColor, paint.BlendMode);
  78. surf.Canvas.Restore();
  79. }
  80. surf.Canvas.Save();
  81. surf.Canvas.ClipPath(outerPath!);
  82. surf.Canvas.ClipPath(innerPath!, ClipOperation.Difference);
  83. surf.Canvas.DrawColor(strokeColor, paint.BlendMode);
  84. surf.Canvas.Restore();
  85. }
  86. surf.Canvas.Restore();
  87. }
  88. public AffectedArea FindAffectedArea(VecI imageSize)
  89. {
  90. var chunks = OperationHelper.FindChunksTouchingEllipse
  91. (location.Center, location.Width / 2.0, location.Height / 2.0, ChunkyImage.FullChunkSize);
  92. if (fillColor.A == 0)
  93. {
  94. chunks.ExceptWith(OperationHelper.FindChunksFullyInsideEllipse
  95. (location.Center, location.Width / 2.0 - strokeWidth * 2, location.Height / 2.0 - strokeWidth * 2, ChunkyImage.FullChunkSize));
  96. }
  97. return new AffectedArea(chunks, location);
  98. }
  99. public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
  100. {
  101. RectI newLocation = location;
  102. if (verAxisX is not null)
  103. newLocation = (RectI)newLocation.ReflectX((double)verAxisX).Round();
  104. if (horAxisY is not null)
  105. newLocation = (RectI)newLocation.ReflectY((double)horAxisY).Round();
  106. return new EllipseOperation(newLocation, strokeColor, fillColor, strokeWidth, paint);
  107. }
  108. public void Dispose()
  109. {
  110. paint.Dispose();
  111. outerPath?.Dispose();
  112. innerPath?.Dispose();
  113. }
  114. }