Skip to content

Commit

Permalink
Fixed clipboard and implemented Undo Change Disposing
Browse files Browse the repository at this point in the history
  • Loading branch information
CPKreu committed Nov 24, 2021
1 parent 4969c4c commit ce436dc
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 74 deletions.
11 changes: 11 additions & 0 deletions PixiEditor/Helpers/Extensions/PixiParserHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using PixiEditor.Parser;
using SkiaSharp;

namespace PixiEditor.Helpers.Extensions
{
public static class PixiParserHelper
{
public static SKRectI GetRect(this SerializableLayer layer) =>
SKRectI.Create(layer.OffsetX, layer.OffsetY, layer.Width, layer.Height);
}
}
2 changes: 1 addition & 1 deletion PixiEditor/Models/Controllers/BitmapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void CloseDocument(Document document)

Documents.Remove(document);
ActiveDocument = nextIndex >= 0 ? Documents[nextIndex] : null;
document.DisposeLayerBitmaps();
document.Dispose();
}

public void ExecuteTool(Coordinates newPosition, bool clickedOnCanvas)
Expand Down
179 changes: 116 additions & 63 deletions PixiEditor/Models/Controllers/ClipboardController.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using PixiEditor.Helpers.Extensions;
using PixiEditor.Exceptions;
using PixiEditor.Helpers.Extensions;
using PixiEditor.Models.DataHolders;
using PixiEditor.Models.ImageManipulation;
using PixiEditor.Models.IO;
using PixiEditor.Models.Layers;
using PixiEditor.Models.Position;
using PixiEditor.Models.Undo;
using PixiEditor.Parser;
using PixiEditor.Parser.Skia;
using PixiEditor.ViewModels;
using SkiaSharp;
using System;
Expand Down Expand Up @@ -78,8 +78,8 @@ public static void CopyToClipboard(Document document)
{
CopyToClipboard(
document.Layers.Where(x => document.GetFinalLayerIsVisible(x)).ToArray(),
//doc.ActiveSelection.SelectedPoints.ToArray(),
new Coordinates[] { (0, 0), (15, 15) },
document.ActiveSelection.SelectedPoints.ToArray(),
//new Coordinates[] { (0, 0), (15, 15) },
document.Width,
document.Height,
document.ToSerializable());
Expand All @@ -90,62 +90,81 @@ public static void CopyToClipboard(Document document)
/// </summary>
public static void PasteFromClipboard()
{
var images = GetImagesFromClipboard();
var layers = GetLayersFromClipboard();

foreach (var (surface, name) in images)
Document activeDocument = ViewModelMain.Current.BitmapManager.ActiveDocument;
int startIndex = activeDocument.Layers.Count;

foreach (var layer in layers)
{
AddImageToLayers(surface, name);
int latestLayerIndex = ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Count - 1;
ViewModelMain.Current.BitmapManager.ActiveDocument.UndoManager.AddUndoChange(
new Change(RemoveLayerProcess, new object[] { latestLayerIndex }, AddLayerProcess, new object[] { images }));
activeDocument.Layers.Add(layer);
}
}

activeDocument.UndoManager.AddUndoChange(
new Change(RemoveLayersProcess, new object[] { startIndex }, AddLayersProcess, new object[] { layers }) { DisposeProcess = DisposeProcess });
}

/// <summary>
/// Gets image from clipboard, supported PNG, Dib and Bitmap.
/// </summary>
/// <returns>WriteableBitmap.</returns>
public static IEnumerable<(Surface, string name)> GetImagesFromClipboard()
public static IEnumerable<Layer> GetLayersFromClipboard()
{
DataObject data = (DataObject)Clipboard.GetDataObject();

if (data.GetDataPresent("PIXI"))
{
SerializableDocument document = GetSerializable(data, out CropData crop);
SKRectI cropRect = SKRectI.Create(crop.OffsetX, crop.OffsetY, crop.Width, crop.Height);

foreach (SerializableLayer layer in document)
foreach (SerializableLayer sLayer in document)
{
if (layer.OffsetX > crop.OffsetX + crop.Width || layer.OffsetY > crop.OffsetY + crop.Height ||
!layer.IsVisible || layer.Opacity == 0)
SKRectI intersect;

if (/*layer.OffsetX > crop.OffsetX + crop.Width || layer.OffsetY > crop.OffsetY + crop.Height ||*/
!sLayer.IsVisible || sLayer.Opacity == 0 ||
(intersect = SKRectI.Intersect(cropRect, sLayer.GetRect())) == SKRectI.Empty)
{
continue;
}

using Surface tempSurface = new Surface(layer.ToSKImage());
var layer = sLayer.ToLayer();

yield return (tempSurface.Crop(crop.OffsetX, crop.OffsetY, crop.Width, crop.Height), layer.Name);
layer.Crop(intersect);

yield return layer;
}
}
else if (TryFromSingleImage(data, out Surface singleImage))
{
yield return (singleImage, "Copied");
yield break;
yield return new Layer("Image", singleImage);
}
else if(data.GetDataPresent(DataFormats.FileDrop))
else if (data.GetDataPresent(DataFormats.FileDrop))
{
foreach (string path in data.GetFileDropList())
{
yield return (Importer.ImportSurface(path), Path.GetFileName(path));
}
if (!Importer.IsSupportedFile(path))
{
continue;
}

Layer layer = null;

try
{
layer = new(Path.GetFileName(path), Importer.ImportSurface(path));
}
catch (CorruptedFileException)
{
}

yield break;
yield return layer ?? new($"Corrupt {path}");
}
}
else
{
throw new NotImplementedException();
}

}

public static bool IsImageInClipboard()
Expand All @@ -156,17 +175,30 @@ public static bool IsImageInClipboard()
return false;
}

var files = dao.GetFileDropList();

if (files != null)
{
foreach (var file in files)
{
if (Importer.IsSupportedFile(file))
{
return true;
}
}
}

return dao.GetDataPresent("PNG") || dao.GetDataPresent(DataFormats.Dib) ||
dao.GetDataPresent(DataFormats.Bitmap) || dao.GetDataPresent(DataFormats.FileDrop) ||
dao.GetDataPresent("PIXI");
}

public static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection, out int offsetX, out int offsetY, out int width, out int height)
{
offsetX = selection.Min(x => x.X);
offsetY = selection.Min(x => x.Y);
width = selection.Max(x => x.X) - offsetX + 1;
height = selection.Max(x => x.Y) - offsetY + 1;
offsetX = selection.Min(min => min.X);
offsetY = selection.Min(min => min.Y);
width = selection.Max(max => max.X) - offsetX + 1;
height = selection.Max(max => max.Y) - offsetY + 1;
return bitmap.Crop(offsetX, offsetY, width, height);
}

Expand Down Expand Up @@ -197,63 +229,84 @@ private static unsafe SerializableDocument GetSerializable(DataObject data, out

private static bool TryFromSingleImage(DataObject data, out Surface result)
{
BitmapSource source;

if (data.GetDataPresent("PNG"))
{
source = FromPNG(data);
}
else if (data.GetDataPresent(DataFormats.Dib) || data.GetDataPresent(DataFormats.Bitmap))
{
source = Clipboard.GetImage();
}
else
try
{
result = null;
return false;
}
BitmapSource source;

if (source.Format.IsSkiaSupported())
{
result = new Surface(source);
}
else
{
FormatConvertedBitmap newFormat = new FormatConvertedBitmap();
newFormat.BeginInit();
newFormat.Source = source;
newFormat.DestinationFormat = PixelFormats.Rgba64;
newFormat.EndInit();
if (data.GetDataPresent("PNG"))
{
source = FromPNG(data);
}
else if (data.GetDataPresent(DataFormats.Dib) || data.GetDataPresent(DataFormats.Bitmap))
{
source = Clipboard.GetImage();
}
else
{
result = null;
return false;
}

result = new Surface(newFormat);
if (source.Format.IsSkiaSupported())
{
result = new Surface(source);
}
else
{
FormatConvertedBitmap newFormat = new FormatConvertedBitmap();
newFormat.BeginInit();
newFormat.Source = source;
newFormat.DestinationFormat = PixelFormats.Rgba64;
newFormat.EndInit();

result = new Surface(newFormat);
}

return true;
}
catch { }

return true;
result = null;
return false;
}

private static void RemoveLayerProcess(object[] parameters)
private static void RemoveLayersProcess(object[] parameters)
{
if (parameters.Length == 0 || !(parameters[0] is int))
if (parameters.Length == 0 || parameters[0] is not int i)
{
return;
}

ViewModelMain.Current.BitmapManager.ActiveDocument.RemoveLayer((int)parameters[0], true);
Document document = ViewModelMain.Current.BitmapManager.ActiveDocument;

while (i < document.Layers.Count)
{
document.RemoveLayer(i, true);
}
}

private static void AddLayerProcess(object[] parameters)
private static void AddLayersProcess(object[] parameters)
{
if (parameters.Length == 0 || !(parameters[0] is Surface))
if (parameters.Length == 0 || parameters[0] is not IEnumerable<Layer> layers)
{
return;
}

AddImageToLayers((Surface)parameters[0]);
foreach (var layer in layers)
{
ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Add(layer);
}
}

private static void AddImageToLayers(Surface image, string name = "Image")
private static void DisposeProcess(object[] rev, object[] proc)
{
ViewModelMain.Current.BitmapManager.ActiveDocument.AddNewLayer(name, image);
if (proc[0] is IEnumerable<Layer> layers)
{
foreach (var layer in layers)
{
layer.LayerBitmap.Dispose();
}
}
}
}
}
12 changes: 11 additions & 1 deletion PixiEditor/Models/Controllers/UndoManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
namespace PixiEditor.Models.Controllers
{
[DebuggerDisplay("{UndoStack.Count} undo steps, {RedoStack.Count} redo step(s)")]
public class UndoManager
public class UndoManager : IDisposable
{
private bool lastChangeWasUndo;

Expand Down Expand Up @@ -158,6 +158,16 @@ public void SquashUndoChanges(int amount, string description)
AddUndoChange(change);
}

public void Dispose()
{
foreach (Change change in UndoStack.Concat(RedoStack))
{
change.Dispose();
}

GC.SuppressFinalize(this);
}

private bool ChangeIsBlockedProperty(Change change)
{
return (change.Root != null || change.FindRootProcess != null)
Expand Down
13 changes: 7 additions & 6 deletions PixiEditor/Models/DataHolders/Document/Document.Layers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -494,12 +494,18 @@ public Layer MergeLayers(Layer[] layersToMerge, bool nameIsLastLayers)
return layer;
}

public void DisposeLayerBitmaps()
public SKColor GetColorAtPoint(int x, int y)
{
return Renderer.FinalSurface.GetSRGBPixel(x, y);
}

private void DisposeLayerBitmaps()
{
foreach (var layer in layers)
{
layer.LayerBitmap.Dispose();
}

referenceLayer?.LayerBitmap.Dispose();
previewLayer?.LayerBitmap.Dispose();

Expand All @@ -508,11 +514,6 @@ public void DisposeLayerBitmaps()
renderer?.Dispose();
}

public SKColor GetColorAtPoint(int x, int y)
{
return Renderer.FinalSurface.GetSRGBPixel(x, y);
}

private void BuildLayerStructureProcess(object[] parameters)
{
if (parameters.Length > 0 && parameters[0] is ObservableCollection<GuidStructureItem> groups)
Expand Down
10 changes: 9 additions & 1 deletion PixiEditor/Models/DataHolders/Document/Document.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
namespace PixiEditor.Models.DataHolders
{
[DebuggerDisplay("'{Name, nq}' {width}x{height} {Layers.Count} Layer(s)")]
public partial class Document : NotifyableObject
public partial class Document : NotifyableObject, IDisposable
{

private ViewModelMain xamlAccesibleViewModel = null;
Expand Down Expand Up @@ -185,6 +185,14 @@ public void CenterContent()
"Center content"));
}

public void Dispose()
{
DisposeLayerBitmaps();
UndoManager.Dispose();

GC.SuppressFinalize(this);
}

private void SetAsActiveOnClick(object obj)
{
XamlAccesibleViewModel.BitmapManager.MouseController.StopRecordingMouseMovementChanges();
Expand Down

0 comments on commit ce436dc

Please sign in to comment.