RectangleOperation.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using ChunkyImageLib.DataHolders;
  2. using Drawie.Backend.Core.Numerics;
  3. using Drawie.Backend.Core.Surfaces;
  4. using Drawie.Backend.Core.Surfaces.PaintImpl;
  5. using Drawie.Numerics;
  6. namespace ChunkyImageLib.Operations;
  7. internal class RectangleOperation : IMirroredDrawOperation
  8. {
  9. public ShapeData Data { get; }
  10. public bool IgnoreEmptyChunks => false;
  11. private Paint paint = new();
  12. public RectangleOperation(ShapeData rect)
  13. {
  14. Data = rect;
  15. paint.StrokeWidth = Data.StrokeWidth;
  16. paint.IsAntiAliased = Data.AntiAliasing;
  17. paint.BlendMode = Data.BlendMode;
  18. }
  19. public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
  20. {
  21. var surf = targetChunk.Surface.DrawingSurface;
  22. var rect = RectD.FromCenterAndSize(Data.Center, Data.Size.Abs());
  23. var innerRect = rect.Inflate(-Data.StrokeWidth);
  24. if (innerRect.IsZeroOrNegativeArea)
  25. innerRect = RectD.Empty;
  26. int initial = surf.Canvas.Save();
  27. surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
  28. surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);
  29. surf.Canvas.RotateRadians((float)Data.Angle, (float)rect.Center.X, (float)rect.Center.Y);
  30. double maxRadiusInPx = Math.Min(Data.Size.X, Data.Size.Y) / 2;
  31. double radiusInPx = Data.CornerRadius * maxRadiusInPx;
  32. if (Data.AntiAliasing)
  33. {
  34. DrawAntiAliased(surf, rect, radiusInPx);
  35. }
  36. else
  37. {
  38. DrawPixelPerfect(surf, rect, innerRect, radiusInPx);
  39. }
  40. surf.Canvas.RestoreToCount(initial);
  41. }
  42. private void DrawPixelPerfect(DrawingSurface surf, RectD rect, RectD innerRect, double radius)
  43. {
  44. // draw fill
  45. if (Data.FillPaintable.AnythingVisible)
  46. {
  47. int saved = surf.Canvas.Save();
  48. if (radius == 0)
  49. {
  50. surf.Canvas.ClipRect(innerRect);
  51. }
  52. else
  53. {
  54. surf.Canvas.ClipRoundRect(innerRect, new VecD(radius), ClipOperation.Intersect);
  55. }
  56. surf.Canvas.DrawPaintable(Data.FillPaintable, Data.BlendMode);
  57. surf.Canvas.RestoreToCount(saved);
  58. }
  59. // draw stroke
  60. surf.Canvas.Save();
  61. if (radius == 0)
  62. {
  63. surf.Canvas.ClipRect(rect);
  64. surf.Canvas.ClipRect(innerRect, ClipOperation.Difference);
  65. }
  66. else
  67. {
  68. VecD vecRadius = new VecD(radius);
  69. surf.Canvas.ClipRoundRect(rect, vecRadius, ClipOperation.Intersect);
  70. surf.Canvas.ClipRoundRect(innerRect, vecRadius, ClipOperation.Difference);
  71. }
  72. surf.Canvas.DrawPaintable(Data.Stroke, Data.BlendMode);
  73. }
  74. private void DrawAntiAliased(DrawingSurface surf, RectD rect, double radius)
  75. {
  76. // draw fill
  77. if (Data.FillPaintable.AnythingVisible)
  78. {
  79. int saved = surf.Canvas.Save();
  80. paint.StrokeWidth = 0;
  81. paint.SetPaintable(Data.FillPaintable);
  82. paint.Style = PaintStyle.Fill;
  83. if (radius == 0)
  84. {
  85. surf.Canvas.DrawRect((float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height, paint);
  86. }
  87. else
  88. {
  89. surf.Canvas.DrawRoundRect((float)rect.Left, (float)rect.Top, (float)rect.Width,
  90. (float)rect.Height, (float)radius, (float)radius, paint);
  91. }
  92. surf.Canvas.RestoreToCount(saved);
  93. }
  94. // draw stroke
  95. surf.Canvas.Save();
  96. paint.StrokeWidth = Data.StrokeWidth > 0 ? Data.StrokeWidth : 1;
  97. paint.SetPaintable(Data.StrokeWidth > 0 ? Data.Stroke : Data.FillPaintable);
  98. paint.Style = PaintStyle.Stroke;
  99. RectD innerRect = rect.Inflate(-Data.StrokeWidth / 2f);
  100. if (radius == 0)
  101. {
  102. surf.Canvas.DrawRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width,
  103. (float)innerRect.Height, paint);
  104. }
  105. else
  106. {
  107. surf.Canvas.DrawRoundRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width,
  108. (float)innerRect.Height, (float)radius, (float)radius, paint);
  109. }
  110. }
  111. public AffectedArea FindAffectedArea(VecI imageSize)
  112. {
  113. if (Math.Abs(Data.Size.X) < 1 || Math.Abs(Data.Size.Y) < 1 ||
  114. (!Data.Stroke.AnythingVisible && !Data.FillPaintable.AnythingVisible))
  115. return new();
  116. RectI affRect = (RectI)new ShapeCorners(Data.Center, Data.Size).AsRotated(Data.Angle, Data.Center).AABBBounds
  117. .RoundOutwards();
  118. if (Data.FillPaintable.AnythingVisible || Math.Abs(Data.Size.X) == 1 || Math.Abs(Data.Size.Y) == 1)
  119. return new(
  120. OperationHelper.FindChunksTouchingRectangle(Data.Center, Data.Size.Abs(), Data.Angle,
  121. ChunkPool.FullChunkSize), affRect);
  122. var chunks =
  123. OperationHelper.FindChunksTouchingRectangle(Data.Center, Data.Size.Abs(), Data.Angle,
  124. ChunkPool.FullChunkSize);
  125. chunks.ExceptWith(
  126. OperationHelper.FindChunksFullyInsideRectangle(
  127. Data.Center,
  128. Data.Size.Abs() - new VecD(Data.StrokeWidth * 2, Data.StrokeWidth * 2),
  129. Data.Angle,
  130. ChunkPool.FullChunkSize));
  131. return new(chunks, affRect);
  132. }
  133. public void Dispose()
  134. {
  135. paint.Dispose();
  136. }
  137. public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
  138. {
  139. if (verAxisX is not null && horAxisY is not null)
  140. return new RectangleOperation(Data.AsMirroredAcrossHorAxis((double)horAxisY)
  141. .AsMirroredAcrossVerAxis((double)verAxisX));
  142. else if (verAxisX is not null)
  143. return new RectangleOperation(Data.AsMirroredAcrossVerAxis((double)verAxisX));
  144. else if (horAxisY is not null)
  145. return new RectangleOperation(Data.AsMirroredAcrossHorAxis((double)horAxisY));
  146. return new RectangleOperation(Data);
  147. }
  148. }