123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- using PixiEditor.Exceptions;
- using PixiEditor.Helpers;
- 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.ViewModels;
- using SkiaSharp;
- using System;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.IO;
- using System.Linq;
- using System.Windows;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- namespace PixiEditor.Models.Controllers
- {
- public static class ClipboardController
- {
- public static readonly string TempCopyFilePath = Path.Join(
- Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
- "PixiEditor",
- "Copied.png");
- /// <summary>
- /// Copies the selection to clipboard in PNG, Bitmap and DIB formats. <para/>
- /// Also serailizes the <paramref name="document"/> in the PIXI format and copies it to the clipboard.
- /// </summary>
- public static void CopyToClipboard(Document document)
- {
- CopyToClipboard(
- document.Layers.Where(x => document.GetFinalLayerIsVisible(x) && x.IsActive).ToArray(),
- document.ActiveSelection.SelectionLayer,
- document.LayerStructure,
- document.Width,
- document.Height,
- document.ToSerializable());
- }
- private static Surface CreateMaskedCombinedSurface(Layer[] layers, LayerStructure structure, Layer selLayer)
- {
- if (layers.Length == 0)
- throw new ArgumentException("Can't combine 0 layers");
- selLayer.ClipCanvas();
- Surface combined = BitmapUtils.CombineLayers(new Int32Rect(selLayer.OffsetX, selLayer.OffsetY, selLayer.Width, selLayer.Height), layers, structure);
- using SKImage snapshot = selLayer.LayerBitmap.SkiaSurface.Snapshot();
- combined.SkiaSurface.Canvas.DrawImage(snapshot, 0, 0, Surface.MaskingPaint);
- return combined;
- }
- /// <summary>
- /// Copies the selection to clipboard in PNG, Bitmap and DIB formats.
- /// </summary>
- /// <param name="layers">Layers where selection is.</param>
- public static void CopyToClipboard(Layer[] layers, Layer selLayer, LayerStructure structure, int originalImageWidth, int originalImageHeight, SerializableDocument document = null)
- {
- if (!ClipboardHelper.TryClear())
- return;
- using Surface surface = CreateMaskedCombinedSurface(layers, structure, selLayer);
- DataObject data = new DataObject();
- //BitmapSource croppedBmp = BitmapSelectionToBmpSource(finalBitmap, selLayer, out int offsetX, out int offsetY, out int width, out int height);
- //Remove for now
- //data.SetData(typeof(CropData), new CropData(width, height, offsetX, offsetY).ToStream());
- using (SKData pngData = surface.SkiaSurface.Snapshot().Encode())
- {
- // Stream should not be disposed
- MemoryStream pngStream = new MemoryStream();
- pngData.AsStream().CopyTo(pngStream);
- data.SetData("PNG", pngStream, false); // PNG, supports transparency
- pngStream.Position = 0;
- Directory.CreateDirectory(Path.GetDirectoryName(TempCopyFilePath));
- using FileStream fileStream = new FileStream(TempCopyFilePath, FileMode.Create, FileAccess.Write);
- pngStream.CopyTo(fileStream);
- data.SetFileDropList(new StringCollection() { TempCopyFilePath });
- }
- WriteableBitmap finalBitmap = surface.ToWriteableBitmap();
- data.SetData(DataFormats.Bitmap, finalBitmap, true); // Bitmap, no transparency
- data.SetImage(finalBitmap); // DIB format, no transparency
- // Remove pixi copying for now
- /*
- if (document != null)
- {
- MemoryStream memoryStream = new();
- PixiParser.Serialize(document, memoryStream);
- data.SetData("PIXI", memoryStream); // PIXI, supports transparency, layers, groups and swatches
- ClipboardHelper.TrySetDataObject(data, true);
- }
- */
- ClipboardHelper.TrySetDataObject(data, true);
- }
- /// <summary>
- /// Pastes image from clipboard into new layer.
- /// </summary>
- public static void PasteFromClipboard()
- {
- IEnumerable<Layer> layers;
- try
- {
- layers = GetLayersFromClipboard();
- }
- catch
- {
- return;
- }
- Document activeDocument = ViewModelMain.Current.BitmapManager.ActiveDocument;
- int startIndex = activeDocument.Layers.Count;
- foreach (var layer in layers)
- {
- 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>
- private static IEnumerable<Layer> GetLayersFromClipboard()
- {
- DataObject data = ClipboardHelper.TryGetDataObject();
- if (data == null)
- yield break;
- //Remove pixi for now
- /*
- 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 sLayer in document)
- {
- 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;
- }
- var layer = sLayer.ToLayer();
- layer.Crop(intersect);
- yield return layer;
- }
- }
- else */
- if (TryFromSingleImage(data, out Surface singleImage))
- {
- yield return new Layer("Image", singleImage);
- }
- else if (data.GetDataPresent(DataFormats.FileDrop))
- {
- foreach (string path in data.GetFileDropList())
- {
- if (!Importer.IsSupportedFile(path))
- {
- continue;
- }
- Layer layer = null;
- try
- {
- layer = new(Path.GetFileName(path), Importer.ImportSurface(path));
- }
- catch (CorruptedFileException)
- {
- }
- yield return layer ?? new($"Corrupt {path}");
- }
- }
- else
- {
- yield break;
- }
- }
- public static bool IsImageInClipboard()
- {
- DataObject dao = ClipboardHelper.TryGetDataObject();
- if (dao == null)
- 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");
- }
- private static BitmapSource BitmapSelectionToBmpSource(WriteableBitmap bitmap, Coordinates[] selection, out int offsetX, out int offsetY, out int width, out int height)
- {
- 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);
- }
- private static BitmapSource FromPNG(DataObject data)
- {
- MemoryStream pngStream = data.GetData("PNG") as MemoryStream;
- PngBitmapDecoder decoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
- return decoder.Frames[0];
- }
- private static unsafe SerializableDocument GetSerializable(DataObject data, out CropData cropData)
- {
- MemoryStream pixiStream = data.GetData("PIXI") as MemoryStream;
- SerializableDocument document = PixiParser.Deserialize(pixiStream);
- if (data.GetDataPresent(typeof(CropData)))
- {
- cropData = CropData.FromStream(data.GetData(typeof(CropData)) as MemoryStream);
- }
- else
- {
- cropData = new CropData(document.Width, document.Height, 0, 0);
- }
- return document;
- }
- private static bool TryFromSingleImage(DataObject data, out Surface result)
- {
- try
- {
- BitmapSource source;
- 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;
- }
- 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 { }
- result = null;
- return false;
- }
- private static void RemoveLayersProcess(object[] parameters)
- {
- if (parameters.Length == 0 || parameters[0] is not int i)
- {
- return;
- }
- Document document = ViewModelMain.Current.BitmapManager.ActiveDocument;
- while (i < document.Layers.Count)
- {
- document.RemoveLayer(i, true);
- }
- }
- private static void AddLayersProcess(object[] parameters)
- {
- if (parameters.Length == 0 || parameters[0] is not IEnumerable<Layer> layers)
- {
- return;
- }
- foreach (var layer in layers)
- {
- ViewModelMain.Current.BitmapManager.ActiveDocument.Layers.Add(layer);
- }
- }
- private static void DisposeProcess(object[] rev, object[] proc)
- {
- if (proc[0] is IEnumerable<Layer> layers)
- {
- foreach (var layer in layers)
- {
- layer.LayerBitmap.Dispose();
- }
- }
- }
- }
- }
|