|
@@ -8,14 +8,41 @@ using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
|
|
|
|
|
|
namespace PixiEditor.ChangeableDocument.Changes.Root;
|
|
namespace PixiEditor.ChangeableDocument.Changes.Root;
|
|
|
|
|
|
-internal sealed class RotateImage_Change : ResizeBasedChangeBase
|
|
|
|
|
|
+internal sealed class RotateImage_Change : Change
|
|
{
|
|
{
|
|
private readonly RotationAngle rotation;
|
|
private readonly RotationAngle rotation;
|
|
|
|
+ private List<Guid> membersToRotate;
|
|
|
|
+
|
|
|
|
+ private VecI originalSize;
|
|
|
|
+ private int originalHorAxisY;
|
|
|
|
+ private int originalVerAxisX;
|
|
|
|
+ private Dictionary<Guid, CommittedChunkStorage> deletedChunks = new();
|
|
|
|
+ private Dictionary<Guid, CommittedChunkStorage> deletedMaskChunks = new();
|
|
|
|
|
|
[GenerateMakeChangeAction]
|
|
[GenerateMakeChangeAction]
|
|
- public RotateImage_Change(RotationAngle rotation)
|
|
|
|
|
|
+ public RotateImage_Change(RotationAngle rotation, List<Guid>? membersToRotate)
|
|
{
|
|
{
|
|
this.rotation = rotation;
|
|
this.rotation = rotation;
|
|
|
|
+ membersToRotate ??= new List<Guid>();
|
|
|
|
+ this.membersToRotate = membersToRotate;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public override bool InitializeAndValidate(Document target)
|
|
|
|
+ {
|
|
|
|
+ if (membersToRotate.Count > 0)
|
|
|
|
+ {
|
|
|
|
+ membersToRotate = target.ExtractLayers(membersToRotate);
|
|
|
|
+
|
|
|
|
+ foreach (var layer in membersToRotate)
|
|
|
|
+ {
|
|
|
|
+ if (!target.HasMember(layer)) return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ originalSize = target.Size;
|
|
|
|
+ originalHorAxisY = target.HorizontalSymmetryAxisY;
|
|
|
|
+ originalVerAxisX = target.VerticalSymmetryAxisX;
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
|
|
|
|
public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
|
|
public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
|
|
@@ -26,21 +53,41 @@ internal sealed class RotateImage_Change : ResizeBasedChangeBase
|
|
return changes;
|
|
return changes;
|
|
}
|
|
}
|
|
|
|
|
|
- protected override void Resize(ChunkyImage img, Guid memberGuid, VecI size, VecI offset, Dictionary<Guid, CommittedChunkStorage> deletedChunksDict)
|
|
|
|
|
|
+ public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
|
|
{
|
|
{
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void Resize(ChunkyImage img, Guid memberGuid, Dictionary<Guid, CommittedChunkStorage> deletedChunksDict)
|
|
|
|
+ {
|
|
|
|
+ RectI bounds = new RectI(VecI.Zero, img.LatestSize);
|
|
|
|
+ if (membersToRotate.Count > 0)
|
|
|
|
+ {
|
|
|
|
+ var preciseBounds = img.FindPreciseCommittedBounds();
|
|
|
|
+ if (preciseBounds.HasValue)
|
|
|
|
+ {
|
|
|
|
+ bounds = preciseBounds.Value;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int newWidth = rotation == RotationAngle.D180 ? bounds.Size.X : bounds.Size.Y;
|
|
|
|
+ int newHeight = rotation == RotationAngle.D180 ? bounds.Size.Y : bounds.Size.X;
|
|
|
|
+
|
|
|
|
+ VecI size = new VecI(newWidth, newHeight)''
|
|
|
|
+
|
|
using Paint paint = new()
|
|
using Paint paint = new()
|
|
{
|
|
{
|
|
BlendMode = DrawingApi.Core.Surface.BlendMode.Src
|
|
BlendMode = DrawingApi.Core.Surface.BlendMode.Src
|
|
};
|
|
};
|
|
|
|
|
|
- using Surface originalSurface = new(_originalSize);
|
|
|
|
|
|
+ using Surface originalSurface = new(img.LatestSize);
|
|
img.DrawMostUpToDateRegionOn(
|
|
img.DrawMostUpToDateRegionOn(
|
|
- new RectI(VecI.Zero, _originalSize),
|
|
|
|
|
|
+ new RectI(VecI.Zero, img.LatestSize),
|
|
ChunkResolution.Full,
|
|
ChunkResolution.Full,
|
|
originalSurface.DrawingSurface,
|
|
originalSurface.DrawingSurface,
|
|
VecI.Zero);
|
|
VecI.Zero);
|
|
|
|
|
|
- using Surface flipped = new Surface(size);
|
|
|
|
|
|
+ using Surface flipped = new Surface(img.LatestSize);
|
|
|
|
|
|
float translationX = size.X;
|
|
float translationX = size.X;
|
|
float translationY = size.Y;
|
|
float translationY = size.Y;
|
|
@@ -64,21 +111,59 @@ internal sealed class RotateImage_Change : ResizeBasedChangeBase
|
|
img.EnqueueDrawImage(VecI.Zero, flipped);
|
|
img.EnqueueDrawImage(VecI.Zero, flipped);
|
|
|
|
|
|
deletedChunksDict.Add(memberGuid, new CommittedChunkStorage(img, img.FindAffectedChunks()));
|
|
deletedChunksDict.Add(memberGuid, new CommittedChunkStorage(img, img.FindAffectedChunks()));
|
|
- img.CommitChanges();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- private float RotationAngleToRadians(RotationAngle rotationAngle)
|
|
|
|
|
|
+ private OneOf<None, IChangeInfo, List<IChangeInfo>> Rotate(Document target)
|
|
{
|
|
{
|
|
- return rotationAngle switch
|
|
|
|
|
|
+ if (membersToRotate.Count == 0)
|
|
{
|
|
{
|
|
- RotationAngle.D90 => 90f * Matrix3X3.DegreesToRadians,
|
|
|
|
- RotationAngle.D180 => 180f * Matrix3X3.DegreesToRadians,
|
|
|
|
- RotationAngle.D270 => 270f * Matrix3X3.DegreesToRadians,
|
|
|
|
- _ => throw new ArgumentOutOfRangeException(nameof(rotationAngle), rotationAngle, null)
|
|
|
|
- };
|
|
|
|
|
|
+ return RotateWholeImage(target);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return RotateMembers(target, membersToRotate);
|
|
}
|
|
}
|
|
-
|
|
|
|
- private OneOf<None, IChangeInfo, List<IChangeInfo>> Rotate(Document target)
|
|
|
|
|
|
+
|
|
|
|
+ private OneOf<None, IChangeInfo, List<IChangeInfo>> RotateMembers(Document target, List<Guid> guids)
|
|
|
|
+ {
|
|
|
|
+ List<IChangeInfo> changes = new List<IChangeInfo>();
|
|
|
|
+
|
|
|
|
+ target.ForEveryMember((member) =>
|
|
|
|
+ {
|
|
|
|
+ if (guids.Contains(member.GuidValue))
|
|
|
|
+ {
|
|
|
|
+ int newWidth;
|
|
|
|
+ int newHeight;
|
|
|
|
+
|
|
|
|
+ if (member is Layer layer)
|
|
|
|
+ {
|
|
|
|
+ Resize(layer.LayerImage, layer.GuidValue,
|
|
|
|
+ deletedChunks);
|
|
|
|
+ changes.Add(
|
|
|
|
+ new LayerImageChunks_ChangeInfo(member.GuidValue, layer.LayerImage.FindAffectedChunks()));
|
|
|
|
+ layer.LayerImage.CommitChanges();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (member.Mask is null)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ var maskBounds = member.Mask.FindPreciseCommittedBounds();
|
|
|
|
+ if (maskBounds.HasValue)
|
|
|
|
+ {
|
|
|
|
+ newWidth = rotation == RotationAngle.D180 ? maskBounds.Value.Size.X : maskBounds.Value.Size.Y;
|
|
|
|
+ newHeight = rotation == RotationAngle.D180 ? maskBounds.Value.Size.Y : maskBounds.Value.Size.X;
|
|
|
|
+
|
|
|
|
+ Resize(member.Mask, member.GuidValue, new VecI(newWidth, newHeight), VecI.Zero, deletedMaskChunks);
|
|
|
|
+ changes.Add(
|
|
|
|
+ new LayerImageChunks_ChangeInfo(member.GuidValue, member.Mask.FindAffectedChunks()));
|
|
|
|
+ member.Mask.CommitChanges();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ return changes;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private OneOf<None, IChangeInfo, List<IChangeInfo>> RotateWholeImage(Document target)
|
|
{
|
|
{
|
|
int newWidth = rotation == RotationAngle.D180 ? target.Size.X : target.Size.Y;
|
|
int newWidth = rotation == RotationAngle.D180 ? target.Size.X : target.Size.Y;
|
|
int newHeight = rotation == RotationAngle.D180 ? target.Size.Y : target.Size.X;
|
|
int newHeight = rotation == RotationAngle.D180 ? target.Size.Y : target.Size.X;
|
|
@@ -87,23 +172,37 @@ internal sealed class RotateImage_Change : ResizeBasedChangeBase
|
|
|
|
|
|
float normalizedSymmX = _originalVerAxisX / Math.Max(target.Size.X, 0.1f);
|
|
float normalizedSymmX = _originalVerAxisX / Math.Max(target.Size.X, 0.1f);
|
|
float normalizedSymmY = _originalHorAxisY / Math.Max(target.Size.Y, 0.1f);
|
|
float normalizedSymmY = _originalHorAxisY / Math.Max(target.Size.Y, 0.1f);
|
|
-
|
|
|
|
|
|
+
|
|
target.Size = newSize;
|
|
target.Size = newSize;
|
|
target.VerticalSymmetryAxisX = (int)(newSize.X * normalizedSymmX);
|
|
target.VerticalSymmetryAxisX = (int)(newSize.X * normalizedSymmX);
|
|
target.HorizontalSymmetryAxisY = (int)(newSize.Y * normalizedSymmY);
|
|
target.HorizontalSymmetryAxisY = (int)(newSize.Y * normalizedSymmY);
|
|
-
|
|
|
|
|
|
+
|
|
target.ForEveryMember((member) =>
|
|
target.ForEveryMember((member) =>
|
|
{
|
|
{
|
|
if (member is Layer layer)
|
|
if (member is Layer layer)
|
|
{
|
|
{
|
|
Resize(layer.LayerImage, layer.GuidValue, newSize, VecI.Zero, deletedChunks);
|
|
Resize(layer.LayerImage, layer.GuidValue, newSize, VecI.Zero, deletedChunks);
|
|
|
|
+ layer.LayerImage.CommitChanges();
|
|
}
|
|
}
|
|
|
|
+
|
|
if (member.Mask is null)
|
|
if (member.Mask is null)
|
|
return;
|
|
return;
|
|
|
|
|
|
Resize(member.Mask, member.GuidValue, newSize, VecI.Zero, deletedMaskChunks);
|
|
Resize(member.Mask, member.GuidValue, newSize, VecI.Zero, deletedMaskChunks);
|
|
|
|
+ member.Mask.CommitChanges();
|
|
});
|
|
});
|
|
-
|
|
|
|
|
|
+
|
|
return new Size_ChangeInfo(newSize, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);
|
|
return new Size_ChangeInfo(newSize, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ private float RotationAngleToRadians(RotationAngle rotationAngle)
|
|
|
|
+ {
|
|
|
|
+ return rotationAngle switch
|
|
|
|
+ {
|
|
|
|
+ RotationAngle.D90 => 90f * Matrix3X3.DegreesToRadians,
|
|
|
|
+ RotationAngle.D180 => 180f * Matrix3X3.DegreesToRadians,
|
|
|
|
+ RotationAngle.D270 => 270f * Matrix3X3.DegreesToRadians,
|
|
|
|
+ _ => throw new ArgumentOutOfRangeException(nameof(rotationAngle), rotationAngle, null)
|
|
|
|
+ };
|
|
|
|
+ }
|
|
}
|
|
}
|