123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 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 void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
- {
- var surf = targetChunk.Surface.DrawingSurface;
- var rect = RectD.FromCenterAndSize(Data.Center, Data.Size.Abs());
- var innerRect = rect.Inflate(-Data.StrokeWidth);
- if (innerRect.IsZeroOrNegativeArea)
- innerRect = RectD.Empty;
- int initial = surf.Canvas.Save();
- surf.Canvas.Scale((float)targetChunk.Resolution.Multiplier());
- surf.Canvas.Translate(-chunkPos * ChunkyImage.FullChunkSize);
- surf.Canvas.RotateRadians((float)Data.Angle, (float)rect.Center.X, (float)rect.Center.Y);
- double maxRadiusInPx = Math.Min(Data.Size.X, Data.Size.Y) / 2;
- double radiusInPx = Data.CornerRadius * maxRadiusInPx;
- if (Data.AntiAliasing)
- {
- DrawAntiAliased(surf, rect, radiusInPx);
- }
- else
- {
- DrawPixelPerfect(surf, rect, innerRect, radiusInPx);
- }
- surf.Canvas.RestoreToCount(initial);
- }
- private void DrawPixelPerfect(DrawingSurface surf, RectD rect, RectD innerRect, double radius)
- {
- // draw fill
- if (Data.FillPaintable.AnythingVisible)
- {
- int saved = surf.Canvas.Save();
- if (radius == 0)
- {
- surf.Canvas.ClipRect(innerRect);
- }
- else
- {
- surf.Canvas.ClipRoundRect(innerRect, new VecD(radius), ClipOperation.Intersect);
- }
- surf.Canvas.DrawPaintable(Data.FillPaintable, Data.BlendMode);
- surf.Canvas.RestoreToCount(saved);
- }
- // draw stroke
- surf.Canvas.Save();
- if (radius == 0)
- {
- surf.Canvas.ClipRect(rect);
- surf.Canvas.ClipRect(innerRect, ClipOperation.Difference);
- }
- else
- {
- VecD vecRadius = new VecD(radius);
- surf.Canvas.ClipRoundRect(rect, vecRadius, ClipOperation.Intersect);
- surf.Canvas.ClipRoundRect(innerRect, vecRadius, ClipOperation.Difference);
- }
- surf.Canvas.DrawPaintable(Data.Stroke, Data.BlendMode);
- }
- private void DrawAntiAliased(DrawingSurface surf, RectD rect, double radius)
- {
- // draw fill
- if (Data.FillPaintable.AnythingVisible)
- {
- int saved = surf.Canvas.Save();
- paint.StrokeWidth = 0;
- paint.SetPaintable(Data.FillPaintable);
- paint.Style = PaintStyle.Fill;
- if (radius == 0)
- {
- surf.Canvas.DrawRect((float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height, paint);
- }
- else
- {
- surf.Canvas.DrawRoundRect((float)rect.Left, (float)rect.Top, (float)rect.Width,
- (float)rect.Height, (float)radius, (float)radius, paint);
- }
- surf.Canvas.RestoreToCount(saved);
- }
- // draw stroke
- surf.Canvas.Save();
- paint.StrokeWidth = Data.StrokeWidth > 0 ? Data.StrokeWidth : 1;
- paint.SetPaintable(Data.StrokeWidth > 0 ? Data.Stroke : Data.FillPaintable);
- paint.Style = PaintStyle.Stroke;
- RectD innerRect = rect.Inflate(-Data.StrokeWidth / 2f);
- if (radius == 0)
- {
- surf.Canvas.DrawRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width,
- (float)innerRect.Height, paint);
- }
- else
- {
- surf.Canvas.DrawRoundRect((float)innerRect.Left, (float)innerRect.Top, (float)innerRect.Width,
- (float)innerRect.Height, (float)radius, (float)radius, paint);
- }
- }
- public AffectedArea FindAffectedArea(VecI imageSize)
- {
- if (Math.Abs(Data.Size.X) < 1 || Math.Abs(Data.Size.Y) < 1 ||
- (!Data.Stroke.AnythingVisible && !Data.FillPaintable.AnythingVisible))
- return new();
- RectI affRect = (RectI)new ShapeCorners(Data.Center, Data.Size).AsRotated(Data.Angle, Data.Center).AABBBounds
- .RoundOutwards();
- if (Data.FillPaintable.AnythingVisible || 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);
- 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);
- }
- 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));
- else if (verAxisX is not null)
- return new RectangleOperation(Data.AsMirroredAcrossVerAxis((double)verAxisX));
- else if (horAxisY is not null)
- return new RectangleOperation(Data.AsMirroredAcrossHorAxis((double)horAxisY));
- return new RectangleOperation(Data);
- }
- }
|