| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337 |
- using System.Collections.Immutable;
- using System.Collections.ObjectModel;
- using Avalonia.Threading;
- using ChunkyImageLib.DataHolders;
- using Microsoft.Extensions.DependencyInjection;
- using PixiEditor.Models.DocumentPassthroughActions;
- using PixiEditor.ChangeableDocument.Actions.Generated;
- using PixiEditor.ChangeableDocument.Actions.Undo;
- using PixiEditor.ChangeableDocument.Changeables.Animations;
- using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
- using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
- using PixiEditor.ChangeableDocument.Changeables.Interfaces;
- using PixiEditor.ChangeableDocument.Enums;
- using PixiEditor.ChangeableDocument.Rendering;
- using Drawie.Backend.Core;
- using Drawie.Backend.Core.Bridge;
- using Drawie.Backend.Core.Numerics;
- using Drawie.Backend.Core.Surfaces.ImageData;
- using Drawie.Backend.Core.Surfaces.PaintImpl;
- using Drawie.Backend.Core.Vector;
- using PixiEditor.Extensions.CommonApi.Palettes;
- using PixiEditor.Helpers;
- using PixiEditor.Helpers.Collections;
- using PixiEditor.Helpers.Extensions;
- using PixiEditor.Models.Controllers;
- using PixiEditor.Models.DocumentModels;
- using PixiEditor.Models.DocumentModels.Public;
- using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
- using PixiEditor.Models.Handlers;
- using PixiEditor.Models.Rendering;
- using PixiEditor.Models.Serialization;
- using PixiEditor.Models.Serialization.Factories;
- using PixiEditor.Models.Structures;
- using PixiEditor.Models.Tools;
- using Drawie.Numerics;
- using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Workspace;
- using PixiEditor.Models.IO;
- using PixiEditor.Parser;
- using PixiEditor.Parser.Skia;
- using PixiEditor.UI.Common.Localization;
- using PixiEditor.ViewModels.Document.Nodes.Workspace;
- using PixiEditor.ViewModels.Document.TransformOverlays;
- using PixiEditor.Views.Overlays.SymmetryOverlay;
- using BlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
- using Color = Drawie.Backend.Core.ColorsImpl.Color;
- using Colors = Drawie.Backend.Core.ColorsImpl.Colors;
- namespace PixiEditor.ViewModels.Document;
- #nullable enable
- internal partial class DocumentViewModel : PixiObservableObject, IDocument
- {
- public event EventHandler<LayersChangedEventArgs>? LayersChanged;
- public event EventHandler<DocumentSizeChangedEventArgs>? SizeChanged;
- public event Action ToolSessionFinished;
- private bool busy = false;
- public bool Busy
- {
- get => busy;
- set => SetProperty(ref busy, value);
- }
- private string coordinatesString = "";
- public string CoordinatesString
- {
- get => coordinatesString;
- set => SetProperty(ref coordinatesString, value);
- }
- private string? fullFilePath = null;
- public string? FullFilePath
- {
- get => fullFilePath;
- set
- {
- SetProperty(ref fullFilePath, value);
- OnPropertyChanged(nameof(FileName));
- }
- }
- public string FileName
- {
- get => fullFilePath is null ? new LocalizedString("UNNAMED") : Path.GetFileName(fullFilePath);
- }
- private Guid? lastChangeOnSave = null;
- private Guid? lastChangeOnAutosave = null;
- public bool AllChangesSaved
- {
- get
- {
- return Internals.Tracker.LastChangeGuid == lastChangeOnSave;
- }
- }
- public bool AllChangesAutosaved
- {
- get
- {
- return Internals.Tracker.LastChangeGuid == lastChangeOnAutosave;
- }
- }
- public DateTime OpenedUTC { get; } = DateTime.UtcNow;
- private bool horizontalSymmetryAxisEnabled;
- public bool HorizontalSymmetryAxisEnabledBindable
- {
- get => horizontalSymmetryAxisEnabled;
- set
- {
- if (!Internals.ChangeController.IsBlockingChangeActive)
- Internals.ActionAccumulator.AddFinishedActions(
- new SymmetryAxisState_Action(SymmetryAxisDirection.Horizontal, value));
- }
- }
- private bool verticalSymmetryAxisEnabled;
- public bool VerticalSymmetryAxisEnabledBindable
- {
- get => verticalSymmetryAxisEnabled;
- set
- {
- if (!Internals.ChangeController.IsBlockingChangeActive)
- Internals.ActionAccumulator.AddFinishedActions(
- new SymmetryAxisState_Action(SymmetryAxisDirection.Vertical, value));
- }
- }
- public bool AnySymmetryAxisEnabledBindable =>
- HorizontalSymmetryAxisEnabledBindable || VerticalSymmetryAxisEnabledBindable;
- public bool OverlayEventsSuppressed => overlaySuppressors.Count > 0;
- private readonly HashSet<string> overlaySuppressors = new();
- private VecI size = new VecI(64, 64);
- public int Width => size.X;
- public int Height => size.Y;
- public VecI SizeBindable => size;
- private double horizontalSymmetryAxisY;
- public double HorizontalSymmetryAxisYBindable => horizontalSymmetryAxisY;
- private double verticalSymmetryAxisX;
- public double VerticalSymmetryAxisXBindable => verticalSymmetryAxisX;
- private readonly HashSet<IStructureMemberHandler> softSelectedStructureMembers = new();
- public bool BlockingUpdateableChangeActive => Internals.ChangeController.IsBlockingChangeActive;
- public bool IsChangeFeatureActive<T>() where T : IExecutorFeature =>
- Internals.ChangeController.IsChangeOfTypeActive<T>();
- public T? TryGetExecutorFeature<T>() where T : IExecutorFeature =>
- Internals.ChangeController.TryGetExecutorFeature<T>();
- public bool PointerDragChangeInProgress =>
- Internals.ChangeController.IsBlockingChangeActive && Internals.ChangeController.LeftMousePressed;
- public bool HasSavedUndo => Internals.Tracker.HasSavedUndo;
- public bool HasSavedRedo => Internals.Tracker.HasSavedRedo;
- public NodeGraphViewModel NodeGraph { get; }
- public DocumentStructureModule StructureHelper { get; }
- public DocumentToolsModule Tools { get; }
- public DocumentOperationsModule Operations { get; }
- public DocumentRenderer Renderer { get; }
- public SceneRenderer SceneRenderer { get; }
- public DocumentEventsModule EventInlet { get; }
- public ActionDisplayList ActionDisplays { get; } =
- new(() => ViewModelMain.Current.NotifyToolActionDisplayChanged());
- public IStructureMemberHandler? SelectedStructureMember { get; private set; } = null;
- private PreviewPainter previewSurface;
- public PreviewPainter PreviewPainter
- {
- get => previewSurface;
- set
- {
- SetProperty(ref previewSurface, value);
- }
- }
- private VectorPath selectionPath = new VectorPath();
- public VectorPath SelectionPathBindable => selectionPath;
- public ObservableCollection<PaletteColor> Swatches { get; set; } = new();
- public Guid Id => Internals.Tracker.Document.DocumentId;
- public ObservableRangeCollection<PaletteColor> Palette { get; set; } = new();
- public SnappingViewModel SnappingViewModel { get; }
- ISnappingHandler IDocument.SnappingHandler => SnappingViewModel;
- public IReadOnlyCollection<Guid> SelectedMembers => GetSelectedMembers().AsReadOnly();
- public DocumentTransformViewModel TransformViewModel { get; }
- public PathOverlayViewModel PathOverlayViewModel { get; }
- public ReferenceLayerViewModel ReferenceLayerViewModel { get; }
- public LineToolOverlayViewModel LineToolOverlayViewModel { get; }
- public AnimationDataViewModel AnimationDataViewModel { get; }
- public TextOverlayViewModel TextOverlayViewModel { get; }
- public IReadOnlyCollection<IStructureMemberHandler> SoftSelectedStructureMembers => softSelectedStructureMembers;
- private DocumentInternalParts Internals { get; }
- INodeGraphHandler IDocument.NodeGraphHandler => NodeGraph;
- IDocumentOperations IDocument.Operations => Operations;
- ITransformHandler IDocument.TransformHandler => TransformViewModel;
- ITextOverlayHandler IDocument.TextOverlayHandler => TextOverlayViewModel;
- IPathOverlayHandler IDocument.PathOverlayHandler => PathOverlayViewModel;
- ILineOverlayHandler IDocument.LineToolOverlayHandler => LineToolOverlayViewModel;
- IReferenceLayerHandler IDocument.ReferenceLayerHandler => ReferenceLayerViewModel;
- IAnimationHandler IDocument.AnimationHandler => AnimationDataViewModel;
- public bool UsesSrgbBlending { get; private set; }
- public AutosaveDocumentViewModel AutosaveViewModel { get; }
- private DocumentViewModel()
- {
- var serviceProvider = ViewModelMain.Current.Services;
- Internals = new DocumentInternalParts(this, serviceProvider);
- Internals.ChangeController.ToolSessionFinished += () => ToolSessionFinished?.Invoke();
- Tools = new DocumentToolsModule(this, Internals);
- StructureHelper = new DocumentStructureModule(this);
- EventInlet = new DocumentEventsModule(this, Internals);
- Operations = new DocumentOperationsModule(this, Internals);
- AnimationDataViewModel = new(this, Internals);
- NodeGraph = new NodeGraphViewModel(this, Internals);
- AutosaveViewModel = new AutosaveDocumentViewModel(this, Internals);
- TransformViewModel = new(this);
- TransformViewModel.TransformChanged += (args) => Internals.ChangeController.TransformChangedInlet(args);
- TransformViewModel.TransformDragged += (from, to) => Internals.ChangeController.TransformDraggedInlet(from, to);
- TransformViewModel.TransformStopped += () => Internals.ChangeController.TransformStoppedInlet();
- PathOverlayViewModel = new(this, Internals);
- PathOverlayViewModel.PathChanged += path =>
- {
- Internals.ChangeController.PathOverlayChangedInlet(path);
- };
- LineToolOverlayViewModel = new();
- LineToolOverlayViewModel.LineMoved += (_, args) =>
- Internals.ChangeController.LineOverlayMovedInlet(args.Item1, args.Item2);
- TextOverlayViewModel = new TextOverlayViewModel();
- TextOverlayViewModel.TextChanged += text =>
- {
- Internals.ChangeController.TextOverlayTextChangedInlet(text);
- };
- SnappingViewModel = new();
- SnappingViewModel.AddFromDocumentSize(SizeBindable);
- SizeChanged += (_, args) =>
- {
- SnappingViewModel.AddFromDocumentSize(args.NewSize);
- };
- LayersChanged += (sender, args) =>
- {
- /*if (args.LayerChangeType == LayerAction.Add)
- {
- IReadOnlyStructureNode layer = Internals.Tracker.Document.FindMember(args.LayerAffectedGuid);
- if (layer is not null)
- {
- SnappingViewModel.AddFromBounds(layer.Id.ToString(),
- () => layer.GetTightBounds(AnimationDataViewModel.ActiveFrameTime) ?? RectD.Empty);
- }
- }
- else if (args.LayerChangeType == LayerAction.Remove)
- {
- SnappingViewModel.Remove(args.LayerAffectedGuid.ToString());
- }*/
- };
- ReferenceLayerViewModel = new(this, Internals);
- Renderer = new DocumentRenderer(Internals.Tracker.Document);
- SceneRenderer = new SceneRenderer(Internals.Tracker.Document, this);
- }
- /// <summary>
- /// Creates a new document using the <paramref name="builder"/>
- /// </summary>
- /// <returns>The created document</returns>
- public static DocumentViewModel Build(Action<DocumentViewModelBuilder> builder)
- {
- var builderInstance = new DocumentViewModelBuilder();
- builder(builderInstance);
- (string serializerName, string serializerVersion) serializerData = (builderInstance.SerializerName,
- builderInstance.SerializerVersion);
- Dictionary<int, Guid> mappedNodeIds = new();
- Dictionary<int, Guid> mappedKeyFrameIds = new();
- ResourceStorageLocator? resourceLocator = null;
- if (builderInstance.DocumentResources != null)
- {
- resourceLocator = ExtractResources(builderInstance.DocumentResources);
- }
- var viewModel = new DocumentViewModel();
- viewModel.Operations.ResizeCanvas(new VecI(builderInstance.Width, builderInstance.Height), ResizeAnchor.Center);
- var acc = viewModel.Internals.ActionAccumulator;
- ColorSpace targetProcessingColorSpace = ColorSpace.CreateSrgbLinear();
- if (builderInstance.UsesSrgbColorBlending ||
- IsFileWithSrgbColorBlending(serializerData, builderInstance.PixiParserVersionUsed))
- {
- targetProcessingColorSpace = ColorSpace.CreateSrgb();
- viewModel.Internals.Tracker.Document.InitProcessingColorSpace(ColorSpace.CreateSrgb());
- viewModel.UsesSrgbBlending = true;
- }
- viewModel.Internals.ChangeController.SymmetryDraggedInlet(
- new SymmetryAxisDragInfo(SymmetryAxisDirection.Horizontal, builderInstance.Height / 2));
- viewModel.Internals.ChangeController.SymmetryDraggedInlet(
- new SymmetryAxisDragInfo(SymmetryAxisDirection.Vertical, builderInstance.Width / 2));
- acc.AddActions(
- new SymmetryAxisPosition_Action(SymmetryAxisDirection.Horizontal, (double)builderInstance.Height / 2),
- new EndSymmetryAxisPosition_Action(),
- new SymmetryAxisPosition_Action(SymmetryAxisDirection.Vertical, (double)builderInstance.Width / 2),
- new EndSymmetryAxisPosition_Action());
- if (builderInstance.ReferenceLayer is { } refLayer)
- {
- acc.AddActions(new SetReferenceLayer_Action(refLayer.Shape, refLayer.ImageBgra8888Bytes.ToImmutableArray(),
- refLayer.ImageSize));
- }
- viewModel.Swatches = new ObservableCollection<PaletteColor>(builderInstance.Swatches);
- viewModel.Palette = new ObservableRangeCollection<PaletteColor>(builderInstance.Palette);
- SerializationConfig config =
- new SerializationConfig(BuiltInEncoders.Encoders[builderInstance.ImageEncoderUsed],
- targetProcessingColorSpace);
- List<SerializationFactory> allFactories =
- ViewModelMain.Current.Services.GetServices<SerializationFactory>().ToList();
- foreach (var factory in allFactories)
- {
- factory.ResourceLocator = resourceLocator;
- }
- AddNodes(builderInstance.Graph);
- if (builderInstance.Graph.AllNodes.Count == 0 || !builderInstance.Graph.AllNodes.Any(x => x is OutputNode))
- {
- Guid outputNodeGuid = Guid.NewGuid();
- acc.AddActions(new CreateNode_Action(typeof(OutputNode), outputNodeGuid, Guid.Empty));
- }
- AddAnimationData(builderInstance.AnimationData, mappedNodeIds, mappedKeyFrameIds);
- acc.AddFinishedActions(new ChangeBoundary_Action(), new DeleteRecordedChanges_Action());
- acc.AddActions(new InvokeAction_PassthroughAction(() =>
- {
- viewModel.MarkAsSaved();
- }));
- foreach (var factory in allFactories)
- {
- factory.ResourceLocator = null;
- }
- return viewModel;
- void AddNodes(NodeGraphBuilder graph)
- {
- foreach (var node in graph.AllNodes)
- {
- AddNode(node.Id, node);
- }
- foreach (var node in graph.AllNodes)
- {
- Guid nodeGuid = mappedNodeIds[node.Id];
- var serializedNode = graph.AllNodes.First(x => x.Id == node.Id);
- if (serializedNode.AdditionalData != null && serializedNode.AdditionalData.Count > 0)
- {
- acc.AddActions(new DeserializeNodeAdditionalData_Action(nodeGuid,
- SerializationUtil.DeserializeDict(serializedNode.AdditionalData, config, allFactories,
- serializerData)));
- }
- }
- foreach (var node in graph.AllNodes)
- {
- Guid nodeGuid = mappedNodeIds[node.Id];
- if (node.InputConnections != null)
- {
- foreach (var connections in node.InputConnections)
- {
- if (mappedNodeIds.TryGetValue(connections.Key, out Guid outputNodeId))
- {
- foreach (var connection in connections.Value)
- {
- acc.AddActions(new ConnectProperties_Action(nodeGuid, outputNodeId,
- connection.inputPropName, connection.outputPropName));
- }
- }
- }
- }
- }
- }
- void AddNode(int id, NodeGraphBuilder.NodeBuilder serializedNode)
- {
- Guid guid = Guid.NewGuid();
- mappedNodeIds.Add(id, guid);
- Guid pairGuid = Guid.Empty;
- if (serializedNode.PairId != null &&
- mappedNodeIds.TryGetValue(serializedNode.PairId.Value, out Guid pairId))
- {
- pairGuid = pairId;
- }
- acc.AddActions(new CreateNodeFromName_Action(serializedNode.UniqueNodeName, guid, pairGuid));
- acc.AddFinishedActions(new NodePosition_Action([guid], serializedNode.Position.ToVecD()),
- new EndNodePosition_Action());
- if (serializedNode.InputValues != null)
- {
- foreach (var propertyValue in serializedNode.InputValues)
- {
- object value =
- SerializationUtil.Deserialize(propertyValue.Value, config, allFactories, serializerData);
- acc.AddActions(new UpdatePropertyValue_Action(guid, propertyValue.Key, value), new EndUpdatePropertyValue_Action());
- }
- }
- if (serializedNode.KeyFrames != null)
- {
- foreach (var keyFrame in serializedNode.KeyFrames)
- {
- Guid keyFrameGuid = Guid.NewGuid();
- /*Add should be here I think, but it crashes while deserializing multiple layers with no frames*/
- mappedKeyFrameIds[keyFrame.Id] = keyFrameGuid;
- acc.AddActions(
- new SetKeyFrameData_Action(
- guid,
- keyFrameGuid,
- SerializationUtil.Deserialize(keyFrame.Data, config, allFactories, serializerData),
- keyFrame.StartFrame,
- keyFrame.Duration, keyFrame.AffectedElement, keyFrame.IsVisible));
- }
- }
- if (!string.IsNullOrEmpty(serializedNode.Name))
- {
- acc.AddActions(new SetNodeName_Action(guid, serializedNode.Name));
- }
- }
- void AddAnimationData(AnimationDataBuilder? data, Dictionary<int, Guid> mappedIds,
- Dictionary<int, Guid> mappedKeyFrameIds)
- {
- if (data is null)
- return;
- acc.AddActions(new SetFrameRate_Action(data.FrameRate));
- acc.AddActions(new SetOnionSettings_Action(data.OnionFrames, data.OnionOpacity));
- acc.AddActions(new SetDefaultEndFrame_Action(data.DefaultEndFrame));
- foreach (var keyFrame in data.KeyFrameGroups)
- {
- if (keyFrame is GroupKeyFrameBuilder group)
- {
- foreach (var child in group.Children)
- {
- acc.AddActions(
- new CreateCel_Action(
- mappedIds[child.NodeId],
- mappedKeyFrameIds[child.KeyFrameId],
- -1, -1, default));
- acc.AddFinishedActions();
- }
- }
- }
- }
- bool IsFileWithSrgbColorBlending((string serializerName, string serializerVersion) serializerData,
- Version? pixiParserVersionUsed)
- {
- if (pixiParserVersionUsed != null && pixiParserVersionUsed.Major < 5)
- {
- return true;
- }
- if (serializerData.serializerVersion == null || serializerData.serializerName == null)
- {
- return false;
- }
- try
- {
- Version parsedVersion = new Version(serializerData.serializerVersion);
- return serializerData.serializerName == "PixiEditor"
- && parsedVersion is { Major: 2, Minor: 0, Build: 0, Revision: >= 28 and <= 31 };
- }
- catch (Exception)
- {
- return false;
- }
- }
- ResourceStorageLocator ExtractResources(ResourceStorage? resources)
- {
- if (resources is null)
- return null;
- string resourcesPath = Paths.TempResourcesPath;
- if (!Directory.Exists(resourcesPath))
- Directory.CreateDirectory(resourcesPath);
- Dictionary<int, string> mapping = new();
- foreach (var resource in resources.Resources)
- {
- string formattedGuid = resource.CacheId.ToString("N");
- string filePath = Path.Combine(resourcesPath, $"{formattedGuid}{Path.GetExtension(resource.FileName)}");
- File.WriteAllBytes(filePath, resource.Data);
- mapping.Add(resource.Handle, filePath);
- }
- return new ResourceStorageLocator(mapping, resourcesPath);
- }
- }
- public void MarkAsSaved()
- {
- Internals.ActionAccumulator.AddActions(new MarkAsAutosaved_PassthroughAction(DocumentMarkType.Saved));
- }
- public void MarkAsAutosaved()
- {
- Internals.ActionAccumulator.AddActions(new MarkAsAutosaved_PassthroughAction(DocumentMarkType.Autosaved));
- }
- public void MarkAsUnsaved()
- {
- Internals.ActionAccumulator.AddActions(new MarkAsAutosaved_PassthroughAction(DocumentMarkType.Unsaved));
- }
- public void InternalMarkSaveState(DocumentMarkType type)
- {
- switch (type)
- {
- case DocumentMarkType.Saved:
- lastChangeOnSave = Internals.Tracker.LastChangeGuid;
- OnPropertyChanged(nameof(AllChangesSaved));
- break;
- case DocumentMarkType.Unsaved:
- lastChangeOnSave = Guid.NewGuid();
- OnPropertyChanged(nameof(AllChangesSaved));
- break;
- case DocumentMarkType.Autosaved:
- lastChangeOnAutosave = Internals.Tracker.LastChangeGuid;
- OnPropertyChanged(nameof(AllChangesAutosaved));
- break;
- case DocumentMarkType.UnAutosaved:
- lastChangeOnAutosave = Guid.NewGuid();
- OnPropertyChanged(nameof(AllChangesAutosaved));
- break;
- }
- }
- public (string name, VecI originalSize)[] GetAvailableExportOutputs()
- {
- var allExportNodes = NodeGraph.AllNodes.Where(x => x is CustomOutputNodeViewModel).ToArray();
- if (allExportNodes.Length == 0)
- {
- return [("DEFAULT", SizeBindable)];
- }
- using var block = Operations.StartChangeBlock();
- foreach (var node in allExportNodes)
- {
- if (node is not CustomOutputNodeViewModel)
- continue;
- Internals.ActionAccumulator.AddActions(new EvaluateGraph_Action(node.Id,
- AnimationDataViewModel.ActiveFrameTime));
- Internals.ActionAccumulator.AddActions(
- new GetComputedPropertyValue_Action(node.Id, CustomOutputNode.OutputNamePropertyName, true),
- new GetComputedPropertyValue_Action(node.Id, CustomOutputNode.SizePropertyName, true));
- }
- block.ExecuteQueuedActions();
- var exportNodes = NodeGraph.AllNodes.Where(x => x is CustomOutputNodeViewModel).ToArray();
- var exportNames = new List<(string name, VecI origianlSize)>();
- exportNames.Add(("DEFAULT", SizeBindable));
- foreach (var node in exportNodes)
- {
- if (node is not CustomOutputNodeViewModel exportZone)
- continue;
- var name = exportZone.Inputs.FirstOrDefault(x => x.PropertyName == CustomOutputNode.OutputNamePropertyName);
- if (name?.ComputedValue is not string finalName)
- continue;
- if (string.IsNullOrEmpty(finalName))
- {
- continue;
- }
- VecI originalSize =
- exportZone.Inputs.FirstOrDefault(x => x.PropertyName == CustomOutputNode.SizePropertyName)
- ?.ComputedValue as VecI? ?? SizeBindable;
- if (originalSize.ShortestAxis <= 0)
- {
- originalSize = SizeBindable;
- }
- exportNames.Add((finalName, originalSize));
- }
- return exportNames.ToArray();
- }
- public VecI GetDefaultRenderSize(out string? renderOutputName)
- {
- var allExportNodes = NodeGraph.AllNodes.Where(x => x is CustomOutputNodeViewModel).ToArray();
- renderOutputName = "DEFAULT";
- if (allExportNodes.Length == 0)
- {
- return SizeBindable;
- }
- using var block = Operations.StartChangeBlock();
- foreach (var node in allExportNodes)
- {
- if (node is not CustomOutputNodeViewModel exportZone)
- continue;
- Internals.ActionAccumulator.AddActions(new EvaluateGraph_Action(node.Id,
- AnimationDataViewModel.ActiveFrameTime));
- Internals.ActionAccumulator.AddActions(
- new GetComputedPropertyValue_Action(node.Id, CustomOutputNode.OutputNamePropertyName, true),
- new GetComputedPropertyValue_Action(node.Id, CustomOutputNode.IsDefaultExportPropertyName, true),
- new GetComputedPropertyValue_Action(node.Id, CustomOutputNode.SizePropertyName, true));
- }
- block.ExecuteQueuedActions();
- var exportNodes = NodeGraph.AllNodes.Where(x => x is CustomOutputNodeViewModel exportZone
- && exportZone.Inputs.Any(x => x is
- {
- PropertyName: CustomOutputNode.IsDefaultExportPropertyName,
- ComputedValue: true
- })).ToArray();
- if (exportNodes.Length == 0)
- return SizeBindable;
- var exportNode = exportNodes.FirstOrDefault();
- if (exportNode is null)
- return SizeBindable;
- var exportSize =
- exportNode.Inputs.FirstOrDefault(x => x.PropertyName == CustomOutputNode.SizePropertyName);
- if (exportSize is null)
- return SizeBindable;
- if (exportSize.ComputedValue is VecI finalSize)
- {
- if (exportNode.Inputs.FirstOrDefault(x => x.PropertyName == CustomOutputNode.OutputNamePropertyName) is
- { } name)
- {
- renderOutputName = name.ComputedValue?.ToString();
- }
- if (finalSize.ShortestAxis <= 0)
- {
- finalSize = SizeBindable;
- }
- return finalSize;
- }
- return SizeBindable;
- }
- public ICrossDocumentPipe<T> ShareNode<T>(Guid layerId) where T : class, IReadOnlyNode
- {
- return Internals.Tracker.Document.CreateNodePipe<T>(layerId);
- }
- public OneOf<Error, Surface> TryRenderWholeImage(KeyFrameTime frameTime, VecI renderSize)
- {
- return TryRenderWholeImage(frameTime, renderSize, SizeBindable);
- }
- public OneOf<Error, Surface> TryRenderWholeImage(KeyFrameTime frameTime, string? renderOutputName)
- {
- (string name, VecI originalSize)[] outputs = [];
- Dispatcher.UIThread.Invoke(() =>
- {
- outputs = GetAvailableExportOutputs();
- });
- string outputName = renderOutputName ?? "DEFAULT";
- var output = outputs.FirstOrDefault(x => x.name == outputName);
- VecI originalSize = string.IsNullOrEmpty(output.name) ? SizeBindable : output.originalSize;
- if (originalSize.ShortestAxis <= 0)
- return new Error();
- return TryRenderWholeImage(frameTime, originalSize, originalSize, renderOutputName);
- }
- public OneOf<Error, Surface> TryRenderWholeImage(KeyFrameTime frameTime, VecI renderSize, string? renderOutputName)
- {
- (string name, VecI originalSize)[] outputs = [];
- Dispatcher.UIThread.Invoke(() =>
- {
- outputs = GetAvailableExportOutputs();
- });
- string outputName = renderOutputName ?? "DEFAULT";
- var output = outputs.FirstOrDefault(x => x.name == outputName);
- VecI originalSize = string.IsNullOrEmpty(output.name) ? SizeBindable : output.originalSize;
- if (originalSize.ShortestAxis <= 0)
- return new Error();
- return TryRenderWholeImage(frameTime, renderSize, originalSize, renderOutputName);
- }
- public OneOf<Error, Surface> TryRenderWholeImage(KeyFrameTime frameTime, VecI renderSize, VecI originalSize,
- string? renderOutputName = null)
- {
- if (renderSize.ShortestAxis <= 0)
- return new Error();
- try
- {
- Surface finalSurface = null;
- DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
- {
- finalSurface = Surface.ForDisplay(renderSize);
- finalSurface.DrawingSurface.Canvas.Save();
- VecD scaling = new VecD(renderSize.X / (double)originalSize.X, renderSize.Y / (double)originalSize.Y);
- finalSurface.DrawingSurface.Canvas.Scale((float)scaling.X, (float)scaling.Y);
- Renderer.RenderDocument(finalSurface.DrawingSurface, frameTime, renderSize, renderOutputName);
- finalSurface.DrawingSurface.Canvas.Restore();
- });
- return finalSurface;
- }
- catch (ObjectDisposedException)
- {
- return new Error();
- }
- }
- /// <summary>
- /// Tries rendering the whole document
- /// </summary>
- /// <returns><see cref="Error"/> if the ChunkyImage was disposed, otherwise a <see cref="Surface"/> of the rendered document</returns>
- public OneOf<Error, Surface> TryRenderWholeImage(KeyFrameTime frameTime)
- {
- return TryRenderWholeImage(frameTime, SizeBindable);
- }
- /// <summary>
- /// Takes the selected area and converts it into a surface
- /// </summary>
- /// <returns><see cref="Error"/> on error, <see cref="None"/> for empty <see cref="Surface"/>, <see cref="Surface"/> otherwise.</returns>
- public OneOf<Error, None, (Surface, RectI)> TryExtractAreaFromSelected(
- RectI bounds)
- {
- var selectedLayers = ExtractSelectedLayers(true);
- if (selectedLayers.Count == 0)
- return new Error();
- if (bounds.IsZeroOrNegativeArea)
- return new None();
- RectI finalBounds = default;
- for (int i = 0; i < selectedLayers.Count; i++)
- {
- var memberVm = StructureHelper.Find(selectedLayers.ElementAt(i));
- IReadOnlyStructureNode? layer = Internals.Tracker.Document.FindMember(memberVm.Id);
- if (layer is null)
- return new Error();
- RectI? memberImageBounds;
- try
- {
- memberImageBounds = (RectI?)layer.GetTightBounds(AnimationDataViewModel.ActiveFrameTime);
- }
- catch (ObjectDisposedException)
- {
- return new Error();
- }
- if (memberImageBounds is null)
- continue;
- RectI combinedBounds = bounds.Intersect(memberImageBounds.Value);
- combinedBounds = combinedBounds.Intersect(new RectI(VecI.Zero, SizeBindable));
- if (combinedBounds.IsZeroOrNegativeArea)
- continue;
- if (i == 0 || finalBounds == default)
- {
- finalBounds = combinedBounds;
- }
- else
- {
- finalBounds = finalBounds.Union(combinedBounds);
- }
- }
- if (finalBounds.IsZeroOrNegativeArea)
- return new None();
- Surface output = new(finalBounds.Size);
- VectorPath clipPath = new VectorPath(SelectionPathBindable) { FillType = PathFillType.EvenOdd };
- //clipPath.Transform(Matrix3X3.CreateTranslation(-bounds.X, -bounds.Y));
- output.DrawingSurface.Canvas.Save();
- output.DrawingSurface.Canvas.Translate(-finalBounds.X, -finalBounds.Y);
- if (!clipPath.IsEmpty)
- {
- output.DrawingSurface.Canvas.ClipPath(clipPath);
- }
- using Paint paint = new Paint() { BlendMode = BlendMode.SrcOver };
- DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
- {
- try
- {
- Renderer.RenderLayers(output.DrawingSurface, selectedLayers.ToHashSet(),
- AnimationDataViewModel.ActiveFrameBindable, ChunkResolution.Full, SizeBindable);
- }
- catch (ObjectDisposedException)
- {
- output?.Dispose();
- }
- });
- output.DrawingSurface.Canvas.Restore();
- return (output, finalBounds);
- }
- /// <summary>
- /// Picks the color at <paramref name="pos"/>
- /// </summary>
- /// <param name="includeReference">Should the color be picked from the reference layer</param>
- /// <param name="includeCanvas">Should the color be picked from the canvas</param>
- /// <param name="referenceTopmost">Is the reference layer topmost. (Only affects the result is includeReference and includeCanvas are set.)</param>
- public Color PickColor(VecD pos, DocumentScope scope, bool includeReference, bool includeCanvas, int frame,
- bool referenceTopmost = false, string? customOutput = null)
- {
- if (scope == DocumentScope.SingleLayer && includeReference && includeCanvas)
- includeReference = false;
- if (includeCanvas && includeReference)
- {
- Color canvasColor = PickColorFromCanvas((VecI)pos, scope, frame);
- Color? potentialReferenceColor = PickColorFromReferenceLayer(pos);
- if (potentialReferenceColor is not { } referenceColor)
- return canvasColor;
- if (!referenceTopmost)
- {
- return ColorHelpers.BlendColors(referenceColor, canvasColor);
- }
- byte referenceAlpha = canvasColor.A == 0
- ? referenceColor.A
- : (byte)(referenceColor.A * ReferenceLayerViewModel.TopMostOpacity);
- referenceColor = new Color(referenceColor.R, referenceColor.G, referenceColor.B, referenceAlpha);
- return ColorHelpers.BlendColors(canvasColor, referenceColor);
- }
- if (includeCanvas)
- {
- return PickColorFromCanvas((VecI)pos, scope, frame, customOutput);
- }
- if (includeReference)
- return PickColorFromReferenceLayer(pos) ?? Colors.Transparent;
- return Colors.Transparent;
- }
- public Color? PickColorFromReferenceLayer(VecD pos)
- {
- Texture? bitmap = ReferenceLayerViewModel.ReferenceTexture;
- if (bitmap is null)
- return null;
- Matrix3X3 matrix = ReferenceLayerViewModel.ReferenceTransformMatrix;
- matrix = matrix.Invert();
- var transformed = matrix.MapPoint(pos);
- if (transformed.X < 0 || transformed.Y < 0 || transformed.X >= bitmap.Size.X || transformed.Y >= bitmap.Size.Y)
- return null;
- return bitmap.GetSRGBPixel(new VecI((int)transformed.X, (int)transformed.Y));
- }
- public void SuppressAllOverlayEvents(string suppressor)
- {
- overlaySuppressors.Add(suppressor);
- OnPropertyChanged(nameof(OverlayEventsSuppressed));
- }
- public void RestoreAllOverlayEvents(string suppressor)
- {
- overlaySuppressors.Remove(suppressor);
- OnPropertyChanged(nameof(OverlayEventsSuppressed));
- }
- public Color PickColorFromCanvas(VecI pos, DocumentScope scope, KeyFrameTime frameTime, string? customOutput = null)
- {
- // there is a tiny chance that the image might get disposed by another thread
- try
- {
- // it might've been a better idea to implement this function asynchronously
- // via a passthrough action to avoid all the try catches
- if (scope == DocumentScope.Canvas)
- {
- using Surface
- tmpSurface =
- new Surface(SizeBindable); // new Surface is on purpose, Surface.ForDisplay doesn't work here
- Renderer.RenderDocument(tmpSurface.DrawingSurface, frameTime, SizeBindable, customOutput);
- return tmpSurface.GetSrgbPixel(pos);
- }
- if (SelectedStructureMember is not ILayerHandler layerVm)
- return Colors.Transparent;
- IReadOnlyStructureNode? maybeMember = Internals.Tracker.Document.FindMember(layerVm.Id);
- if (maybeMember is not IReadOnlyImageNode layer)
- {
- if (maybeMember is IRasterizable rasterizable)
- {
- using Surface texture = new Surface(SizeBindable);
- using Paint paint = new Paint();
- rasterizable.Rasterize(texture.DrawingSurface, paint);
- return texture.GetSrgbPixel(pos);
- }
- }
- else
- {
- return layer.GetLayerImageAtFrame(frameTime.Frame).GetMostUpToDatePixel(pos);
- }
- return Colors.Transparent;
- }
- catch (ObjectDisposedException)
- {
- return Colors.Transparent;
- }
- }
- #region Internal Methods
- // these are intended to only be called from DocumentUpdater
- public void InternalRaiseLayersChanged(LayersChangedEventArgs args) => LayersChanged?.Invoke(this, args);
- public void RaiseSizeChanged(DocumentSizeChangedEventArgs args) => SizeChanged?.Invoke(this, args);
- public void ISetVerticalSymmetryAxisEnabled(bool verticalSymmetryAxisEnabled)
- {
- this.verticalSymmetryAxisEnabled = verticalSymmetryAxisEnabled;
- OnPropertyChanged(nameof(VerticalSymmetryAxisEnabledBindable));
- }
- public void SetHorizontalSymmetryAxisEnabled(bool horizontalSymmetryAxisEnabled)
- {
- this.horizontalSymmetryAxisEnabled = horizontalSymmetryAxisEnabled;
- OnPropertyChanged(nameof(HorizontalSymmetryAxisEnabledBindable));
- OnPropertyChanged(nameof(AnySymmetryAxisEnabledBindable));
- }
- public void SetVerticalSymmetryAxisEnabled(bool infoState)
- {
- verticalSymmetryAxisEnabled = infoState;
- OnPropertyChanged(nameof(VerticalSymmetryAxisEnabledBindable));
- OnPropertyChanged(nameof(AnySymmetryAxisEnabledBindable));
- }
- public void SetVerticalSymmetryAxisX(double verticalSymmetryAxisX)
- {
- this.verticalSymmetryAxisX = verticalSymmetryAxisX;
- OnPropertyChanged(nameof(VerticalSymmetryAxisXBindable));
- }
- public void SetSelectedMember(IStructureMemberHandler member)
- {
- SelectedStructureMember = member;
- Internals.ChangeController.MembersSelectedInlet(GetSelectedMembers());
- OnPropertyChanged(nameof(SelectedStructureMember));
- }
- public void SetHorizontalSymmetryAxisY(double horizontalSymmetryAxisY)
- {
- this.horizontalSymmetryAxisY = horizontalSymmetryAxisY;
- OnPropertyChanged(nameof(HorizontalSymmetryAxisYBindable));
- }
- public void SetProcessingColorSpace(ColorSpace infoColorSpace)
- {
- UsesSrgbBlending = infoColorSpace.IsSrgb;
- }
- public void SetSize(VecI size)
- {
- var oldSize = size;
- this.size = size;
- OnPropertyChanged(nameof(SizeBindable));
- OnPropertyChanged(nameof(Width));
- OnPropertyChanged(nameof(Height));
- // TODO: Make sure this is correct, it was in InternalRaiseSizeChanged previously, check DocumentUpdater.cs ProcessSize
- SizeChanged?.Invoke(this, new DocumentSizeChangedEventArgs(this, oldSize, size));
- }
- public void UpdateSelectionPath(VectorPath vectorPath)
- {
- (VectorPath? toDispose, this.selectionPath) = (this.selectionPath, vectorPath);
- toDispose.Dispose();
- OnPropertyChanged(nameof(SelectionPathBindable));
- }
- public void AddSoftSelectedMember(IStructureMemberHandler member)
- {
- softSelectedStructureMembers.Add(member);
- Internals.ChangeController.MembersSelectedInlet(GetSelectedMembers());
- OnPropertyChanged(nameof(SoftSelectedStructureMembers));
- }
- public void RemoveSoftSelectedMember(IStructureMemberHandler member)
- {
- softSelectedStructureMembers.Remove(member);
- Internals.ChangeController.MembersSelectedInlet(GetSelectedMembers());
- OnPropertyChanged(nameof(SoftSelectedStructureMembers));
- }
- public void ClearSoftSelectedMembers()
- {
- softSelectedStructureMembers.Clear();
- Internals.ChangeController.MembersSelectedInlet(GetSelectedMembers());
- OnPropertyChanged(nameof(SoftSelectedStructureMembers));
- }
- #endregion
- /// <summary>
- /// Returns a list of all selected members (Hard and Soft selected)
- /// </summary>
- public List<Guid> GetSelectedMembers()
- {
- List<Guid> layerGuids = new List<Guid>();
- if (SelectedStructureMember is not null)
- layerGuids.Add(SelectedStructureMember.Id);
- foreach (var member in softSelectedStructureMembers)
- {
- if (member.Id != SelectedStructureMember?.Id)
- {
- layerGuids.Add(member.Id);
- }
- }
- return layerGuids;
- }
- public List<Guid> GetSelectedMembersInOrder(bool includeNested = false)
- {
- var selectedMembers = GetSelectedMembers();
- List<Guid> orderedMembers = new List<Guid>();
- var allMembers = StructureHelper.TraverseAllMembers();
- for (var index = 0; index < allMembers.Count; index++)
- {
- var member = allMembers[index];
- if (selectedMembers.Contains(member.Id))
- {
- if (!includeNested)
- {
- var parents = StructureHelper.GetParents(member.Id);
- if (parents.Any(x => selectedMembers.Contains(x.Id)))
- continue;
- }
- orderedMembers.Add(member.Id);
- }
- }
- return orderedMembers;
- }
- /// <summary>
- /// Gets all selected layers extracted from selected folders.
- /// </summary>
- /// <param name="includeFoldersWithMask">Should folders with mask be included</param>
- /// <returns>A list of GUIDs of selected layers</returns>
- public HashSet<Guid> ExtractSelectedLayers(bool includeFoldersWithMask = false)
- {
- var result = new HashSet<Guid>();
- List<Guid> selectedMembers = GetSelectedMembers();
- var allLayers = StructureHelper.GetAllMembers();
- foreach (var member in allLayers)
- {
- if (!selectedMembers.Contains(member.Id))
- continue;
- if (member is ILayerHandler)
- {
- result.Add(member.Id);
- }
- else if (member is IFolderHandler folder)
- {
- if (includeFoldersWithMask && folder.HasMaskBindable)
- result.Add(folder.Id);
- ExtractSelectedLayers(folder, result, includeFoldersWithMask);
- }
- }
- return result;
- }
- public void UpdateSavedState()
- {
- OnPropertyChanged(nameof(AllChangesSaved));
- OnPropertyChanged(nameof(HasSavedUndo));
- OnPropertyChanged(nameof(HasSavedRedo));
- }
- private void ExtractSelectedLayers(IFolderHandler folder, HashSet<Guid> list,
- bool includeFoldersWithMask)
- {
- foreach (var member in folder.Children)
- {
- if (member is ILayerHandler layer && !list.Contains(layer.Id))
- {
- list.Add(layer.Id);
- }
- else if (member is IFolderHandler childFolder)
- {
- if (includeFoldersWithMask && childFolder.HasMaskBindable && !list.Contains(childFolder.Id))
- list.Add(childFolder.Id);
- ExtractSelectedLayers(childFolder, list, includeFoldersWithMask);
- }
- }
- }
- public Image[] RenderFrames(Func<Surface, Surface> processFrameAction = null, string? renderOutput = null,
- CancellationToken token = default)
- {
- if (token.IsCancellationRequested)
- return [];
- int firstFrame = 1;
- int lastFrame = AnimationDataViewModel.GetLastVisibleFrame();
- int framesCount = lastFrame;
- Image[] images = new Image[framesCount - firstFrame];
- // TODO: Multi-threading
- for (int i = firstFrame; i < lastFrame; i++)
- {
- if (token.IsCancellationRequested)
- return [];
- double normalizedTime = (double)(i - firstFrame) / framesCount;
- KeyFrameTime frameTime = new KeyFrameTime(i, normalizedTime);
- var surface = TryRenderWholeImage(frameTime, renderOutput);
- if (surface.IsT0)
- {
- continue;
- }
- if (processFrameAction is not null)
- {
- surface = processFrameAction(surface.AsT1);
- }
- images[i - firstFrame] = surface.AsT1.DrawingSurface.Snapshot();
- surface.AsT1.Dispose();
- }
- return images;
- }
- /// <summary>
- /// Render frames progressively and disposes the surface after processing.
- /// </summary>
- /// <param name="processFrameAction">Action to perform on rendered frame</param>
- /// <param name="token">Cancellation token to cancel the rendering</param>
- public void RenderFramesProgressive(Action<Surface, int> processFrameAction, CancellationToken token,
- string? renderOutput)
- {
- int firstFrame = 1;
- int lastFrame = AnimationDataViewModel.GetLastVisibleFrame();
- int totalFrames = lastFrame - firstFrame;
- int activeFrame = AnimationDataViewModel.ActiveFrameBindable;
- for (int i = firstFrame; i < lastFrame; i++)
- {
- if (token.IsCancellationRequested)
- return;
- KeyFrameTime frameTime = new KeyFrameTime(i, (double)(i - firstFrame) / totalFrames);
- var surface = TryRenderWholeImage(frameTime, renderOutput);
- if (surface.IsT0)
- {
- continue;
- }
- processFrameAction(surface.AsT1, i - firstFrame);
- surface.AsT1.Dispose();
- }
- }
- public bool RenderFrames(List<Image> frames, Func<Surface, Surface> processFrameAction = null,
- string? renderOutput = null)
- {
- var firstFrame = 1;
- var lastFrame = AnimationDataViewModel.GetLastVisibleFrame();
- for (int i = firstFrame; i < lastFrame; i++)
- {
- KeyFrameTime frameTime = new KeyFrameTime(i, (double)(i - firstFrame) / (lastFrame - firstFrame));
- var surface = TryRenderWholeImage(frameTime, renderOutput);
- if (surface.IsT0)
- {
- return false;
- }
- if (processFrameAction is not null)
- {
- surface = processFrameAction(surface.AsT1);
- }
- var snapshot = surface.AsT1.DrawingSurface.Snapshot();
- frames.Add(snapshot);
- }
- return true;
- }
- private static void ClearTempFolder(string tempRenderingPath)
- {
- string[] files = Directory.GetFiles(tempRenderingPath);
- for (var i = 0; i < files.Length; i++)
- {
- var file = files[i];
- File.Delete(file);
- }
- }
- public void Dispose()
- {
- NodeGraph.Dispose();
- Renderer.Dispose();
- SceneRenderer.Dispose();
- AnimationDataViewModel.Dispose();
- Internals.ChangeController.TryStopActiveExecutor();
- Internals.Tracker.Dispose();
- Internals.Tracker.Document.Dispose();
- }
- public VecI GetRenderOutputSize(string renderOutputName)
- {
- var exportOutputs = GetAvailableExportOutputs();
- var exportOutput = exportOutputs.FirstOrDefault(x => x.name == renderOutputName);
- VecI size = SizeBindable;
- if (exportOutput != default)
- {
- size = exportOutput.originalSize;
- if (size.ShortestAxis <= 0)
- {
- size = SizeBindable;
- }
- }
- return size;
- }
- void Extensions.CommonApi.Documents.IDocument.Resize(int width, int height)
- {
- Operations.ResizeImage(new VecI(width, height), ResamplingMethod.NearestNeighbor);
- }
- }
|