| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
- using PixiEditor.ChangeableDocument.ChangeInfos.Root;
- using PixiEditor.ChangeableDocument.Enums;
- using Drawie.Backend.Core;
- using Drawie.Backend.Core.Numerics;
- using Drawie.Backend.Core.Surfaces;
- using Drawie.Backend.Core.Surfaces.PaintImpl;
- using Drawie.Numerics;
- using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
- using BlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
- namespace PixiEditor.ChangeableDocument.Changes.Root;
- internal class ResizeImage_Change : Change
- {
- private readonly VecI newSize;
- private readonly ResamplingMethod method;
- private VecI originalSize;
- private double originalHorAxisY;
- private double originalVerAxisX;
- private Dictionary<Guid, CommittedChunkStorage> savedChunks = new();
- private Dictionary<Guid, CommittedChunkStorage> savedMaskChunks = new();
- [GenerateMakeChangeAction]
- public ResizeImage_Change(VecI size, ResamplingMethod method)
- {
- this.newSize = size;
- this.method = method;
- }
- public override bool InitializeAndValidate(Document target)
- {
- if (newSize.X < 1 || newSize.Y < 1)
- return false;
- originalSize = target.Size;
- originalHorAxisY = target.HorizontalSymmetryAxisY;
- originalVerAxisX = target.VerticalSymmetryAxisX;
- return true;
- }
- private static FilterQuality ToFilterQuality(ResamplingMethod method, bool downscaling) =>
- (method, downscaling) switch
- {
- (ResamplingMethod.NearestNeighbor, _) => FilterQuality.None,
- (ResamplingMethod.Bilinear, true) => FilterQuality.Medium,
- (ResamplingMethod.Bilinear, false) => FilterQuality.Low,
- (ResamplingMethod.Bicubic, _) => FilterQuality.High,
- _ => throw new ArgumentOutOfRangeException(),
- };
- private void ScaleChunkyImage(ChunkyImage image)
- {
- using Surface originalSurface = Surface.ForProcessing(originalSize, image.ProcessingColorSpace);
- image.DrawMostUpToDateRegionOn(
- new(VecI.Zero, originalSize),
- ChunkResolution.Full,
- originalSurface.DrawingSurface,
- VecI.Zero);
- bool downscaling = newSize.LengthSquared < originalSize.LengthSquared;
- FilterQuality quality = ToFilterQuality(method, downscaling);
- using Paint paint = new() { FilterQuality = quality, BlendMode = BlendMode.Src, };
- using Surface newSurface = Surface.ForProcessing(newSize, image.ProcessingColorSpace);
- newSurface.DrawingSurface.Canvas.Save();
- newSurface.DrawingSurface.Canvas.Scale(newSize.X / (float)originalSize.X, newSize.Y / (float)originalSize.Y);
- newSurface.DrawingSurface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
- newSurface.DrawingSurface.Canvas.Restore();
- image.EnqueueResize(newSize);
- image.EnqueueClear();
- image.EnqueueDrawImage(VecI.Zero, newSurface);
- }
- public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
- out bool ignoreInUndo)
- {
- if (originalSize == newSize)
- {
- ignoreInUndo = true;
- return new None();
- }
- target.Size = newSize;
- target.VerticalSymmetryAxisX = Math.Clamp(originalVerAxisX, 0, target.Size.X);
- target.HorizontalSymmetryAxisY = Math.Clamp(originalHorAxisY, 0, target.Size.Y);
- target.ForEveryMember(member =>
- {
- if (member is ImageLayerNode layer)
- {
- layer.ForEveryFrame((img, id) =>
- {
- ScaleChunkyImage(img);
- var affected = img.FindAffectedArea();
- savedChunks[id] = new CommittedChunkStorage(img, affected.Chunks);
- img.CommitChanges();
- });
- }
- else if (member is IScalable scalableLayer)
- {
- VecD multiplier = new VecD(newSize.X / (double)originalSize.X, newSize.Y / (double)originalSize.Y);
- scalableLayer.Resize(multiplier);
- }
- // Add support for different Layer types
- if (member.EmbeddedMask is not null)
- {
- ScaleChunkyImage(member.EmbeddedMask);
- var affected = member.EmbeddedMask.FindAffectedArea();
- savedMaskChunks[member.Id] = new CommittedChunkStorage(member.EmbeddedMask, affected.Chunks);
- member.EmbeddedMask.CommitChanges();
- }
- });
- ignoreInUndo = false;
- return new Size_ChangeInfo(newSize, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);
- }
- public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
- {
- target.Size = originalSize;
- target.ForEveryMember((member) =>
- {
- if (member is ImageLayerNode layer)
- {
- layer.ForEveryFrame((layerImage, id) =>
- {
- layerImage.EnqueueResize(originalSize);
- layerImage.EnqueueClear();
- savedChunks[id].ApplyChunksToImage(layerImage);
- layerImage.CommitChanges();
- });
- }
- else if (member is IScalable scalableLayer)
- {
- VecD multiplier = new VecD(originalSize.X / (double)newSize.X, originalSize.Y / (double)newSize.Y);
- scalableLayer.Resize(multiplier);
- }
- if (member.EmbeddedMask is not null)
- {
- member.EmbeddedMask.EnqueueResize(originalSize);
- member.EmbeddedMask.EnqueueClear();
- savedMaskChunks[member.Id].ApplyChunksToImage(member.EmbeddedMask);
- member.EmbeddedMask.CommitChanges();
- }
- });
- target.HorizontalSymmetryAxisY = originalHorAxisY;
- target.VerticalSymmetryAxisX = originalVerAxisX;
- foreach (var stored in savedChunks)
- stored.Value.Dispose();
- savedChunks = new();
- foreach (var stored in savedMaskChunks)
- stored.Value.Dispose();
- savedMaskChunks = new();
- return new Size_ChangeInfo(originalSize, originalVerAxisX, originalHorAxisY);
- }
- public override void Dispose()
- {
- foreach (var layer in savedChunks)
- layer.Value.Dispose();
- foreach (var mask in savedMaskChunks)
- mask.Value.Dispose();
- }
- }
|