EllipseOperation.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using ChunkyImageLib.DataHolders;
  2. using PixiEditor.DrawingApi.Core.ColorsImpl;
  3. using PixiEditor.DrawingApi.Core.Numerics;
  4. using PixiEditor.DrawingApi.Core.Surfaces;
  5. using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
  6. using PixiEditor.DrawingApi.Core.Surfaces.Vector;
  7. using PixiEditor.Numerics;
  8. namespace ChunkyImageLib.Operations;
  9. internal class EllipseOperation : IMirroredDrawOperation
  10. {
  11. public bool IgnoreEmptyChunks => false;
  12. private readonly RectI location;
  13. private readonly Color strokeColor;
  14. private readonly Color fillColor;
  15. private readonly int strokeWidth;
  16. private readonly double rotation;
  17. private readonly Paint paint;
  18. private bool init = false;
  19. private VectorPath? outerPath;
  20. private VectorPath? innerPath;
  21. private VectorPath? ellipseOutline;
  22. private Point[]? ellipse;
  23. private Point[]? ellipseFill;
  24. private RectI? ellipseFillRect;
  25. public EllipseOperation(RectI location, Color strokeColor, Color fillColor, int strokeWidth, double rotationRad, Paint? paint = null)
  26. {
  27. this.location = location;
  28. this.strokeColor = strokeColor;
  29. this.fillColor = fillColor;
  30. this.strokeWidth = strokeWidth;
  31. this.rotation = rotationRad;
  32. this.paint = paint?.Clone() ?? new Paint();
  33. }
  34. private void Init()
  35. {
  36. init = true;
  37. if (strokeWidth == 1)
  38. {
  39. if (Math.Abs(rotation) < 0.001)
  40. {
  41. var ellipseList = EllipseHelper.GenerateEllipseFromRect(location);
  42. ellipse = ellipseList.Select(a => new Point(a)).ToArray();
  43. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  44. {
  45. (var fill, ellipseFillRect) = EllipseHelper.SplitEllipseFillIntoRegions(ellipseList.ToList(), location);
  46. ellipseFill = fill.Select(a => new Point(a)).ToArray();
  47. }
  48. }
  49. else
  50. {
  51. ellipseOutline = EllipseHelper.GenerateEllipseVectorFromRect(location);
  52. }
  53. }
  54. else
  55. {
  56. outerPath = new VectorPath();
  57. outerPath.ArcTo(location, 0, 359, true);
  58. innerPath = new VectorPath();
  59. innerPath.ArcTo(location.Inflate(-strokeWidth), 0, 359, true);
  60. }
  61. }
  62. public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
  63. {
  64. if (!init)
  65. Init();
  66. var surf = targetChunk.Surface.DrawingSurface;
  67. surf.Canvas.Save();
  68. surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
  69. surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);
  70. paint.IsAntiAliased = targetChunk.Resolution != ChunkResolution.Full;
  71. if (strokeWidth == 1)
  72. {
  73. if (Math.Abs(rotation) < 0.001)
  74. {
  75. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  76. {
  77. paint.Color = fillColor;
  78. surf.Canvas.DrawPoints(PointMode.Lines, ellipseFill!, paint);
  79. surf.Canvas.DrawRect((RectD)ellipseFillRect!.Value, paint);
  80. }
  81. paint.Color = strokeColor;
  82. paint.StrokeWidth = 1f;
  83. surf.Canvas.DrawPoints(PointMode.Points, ellipse!, paint);
  84. }
  85. else
  86. {
  87. surf.Canvas.Save();
  88. surf.Canvas.RotateRadians((float)rotation, (float)location.Center.X, (float)location.Center.Y);
  89. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  90. {
  91. paint.Color = fillColor;
  92. paint.Style = PaintStyle.Fill;
  93. surf.Canvas.DrawPath(ellipseOutline!, paint);
  94. }
  95. paint.Color = strokeColor;
  96. paint.Style = PaintStyle.Stroke;
  97. paint.StrokeWidth = 1f;
  98. surf.Canvas.DrawPath(ellipseOutline!, paint);
  99. surf.Canvas.Restore();
  100. }
  101. }
  102. else
  103. {
  104. if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
  105. {
  106. surf.Canvas.Save();
  107. surf.Canvas.RotateRadians((float)rotation, (float)location.Center.X, (float)location.Center.Y);
  108. surf.Canvas.ClipPath(innerPath!);
  109. surf.Canvas.DrawColor(fillColor, paint.BlendMode);
  110. surf.Canvas.Restore();
  111. }
  112. surf.Canvas.Save();
  113. surf.Canvas.RotateRadians((float)rotation, (float)location.Center.X, (float)location.Center.Y);
  114. surf.Canvas.ClipPath(outerPath!);
  115. surf.Canvas.ClipPath(innerPath!, ClipOperation.Difference);
  116. surf.Canvas.DrawColor(strokeColor, paint.BlendMode);
  117. surf.Canvas.Restore();
  118. }
  119. surf.Canvas.Restore();
  120. }
  121. public AffectedArea FindAffectedArea(VecI imageSize)
  122. {
  123. ShapeCorners corners = new((RectD)location);
  124. corners = corners.AsRotated(rotation, (VecD)location.Center);
  125. RectI bounds = (RectI)corners.AABBBounds.RoundOutwards();
  126. var chunks = OperationHelper.FindChunksTouchingRectangle(bounds, ChunkyImage.FullChunkSize);
  127. if (fillColor.A == 0)
  128. {
  129. chunks.ExceptWith(OperationHelper.FindChunksFullyInsideEllipse
  130. (location.Center, location.Width / 2.0 - strokeWidth * 2, location.Height / 2.0 - strokeWidth * 2, ChunkyImage.FullChunkSize, rotation));
  131. }
  132. return new AffectedArea(chunks, bounds);
  133. }
  134. public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
  135. {
  136. RectI newLocation = location;
  137. if (verAxisX is not null)
  138. newLocation = (RectI)newLocation.ReflectX((double)verAxisX).Round();
  139. if (horAxisY is not null)
  140. newLocation = (RectI)newLocation.ReflectY((double)horAxisY).Round();
  141. return new EllipseOperation(newLocation, strokeColor, fillColor, strokeWidth, rotation, paint);
  142. }
  143. public void Dispose()
  144. {
  145. paint.Dispose();
  146. outerPath?.Dispose();
  147. innerPath?.Dispose();
  148. }
  149. }