Selaa lähdekoodia

Improved image previews

Krzysztof Krysiński 2 viikkoa sitten
vanhempi
commit
9ab21fc740

+ 38 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

@@ -34,7 +34,6 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
 
         RenderContent(sceneContext, sceneContext.RenderSurface,
             sceneContext.TargetPropertyOutput == Output);
-
     }
 
     private void RenderContent(SceneObjectRenderContext context, DrawingSurface renderOnto, bool useFilters)
@@ -245,7 +244,16 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
         if (texture == null || texture.IsDisposed)
             return;
 
-        VecD densityVec = ((VecD)ctx.DocumentSize).Divide(texture.Size);
+
+
+        int saved = texture.DrawingSurface.Canvas.Save();
+
+        RenderContext previewCtx = ctx.Clone();
+
+        var approxBounds = GetApproxBounds(ctx.FrameTime);
+
+        VecD size = approxBounds?.Size ?? ctx.DocumentSize;
+        VecD densityVec = size.Divide(texture.Size);
         double density = Math.Min(densityVec.X, densityVec.Y);
         ChunkResolution resolution = density switch
         {
@@ -255,14 +263,36 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
             _ => ChunkResolution.Full
         };
 
-        int saved = texture.DrawingSurface.Canvas.Save();
-
-        RenderContext previewCtx = ctx.Clone();
         previewCtx.ChunkResolution = resolution;
 
-        VecD scaling = new VecD((double)texture.Size.X / ctx.DocumentSize.X,
-            (double)texture.Size.Y / ctx.DocumentSize.Y) * resolution.InvertedMultiplier();
-        texture.DrawingSurface.Canvas.Scale((float)scaling.X, (float)scaling.Y);
+        if (approxBounds.HasValue)
+        {
+            var bounds = approxBounds.Value;
+
+            // target size = texture size
+            var targetW = texture.Size.X;
+            var targetH = texture.Size.Y;
+
+            // scale so that bounds fits inside target
+            double scaleX = (double)targetW / bounds.Width;
+            double scaleY = (double)targetH / bounds.Height;
+            double uniformScale = Math.Min(scaleX, scaleY);
+
+            // scaled content size
+            double scaledW = bounds.Width * uniformScale;
+            double scaledH = bounds.Height * uniformScale;
+
+            // offset so it’s centered
+            double offsetX = (targetW - scaledW) / 2.0 - bounds.Left * uniformScale;
+            double offsetY = (targetH - scaledH) / 2.0 - bounds.Top * uniformScale;
+
+            var canvas = texture.DrawingSurface.Canvas;
+            canvas.Translate((float)offsetX, (float)offsetY);
+            uniformScale *= resolution.InvertedMultiplier();
+            canvas.Scale((float)uniformScale, (float)uniformScale);
+
+            previewCtx.DesiredSamplingOptions = uniformScale < 1.0 ? SamplingOptions.Bilinear : SamplingOptions.Default;
+        }
 
         RenderPreview(texture.DrawingSurface, previewCtx, elementToRender);
 

+ 3 - 1
src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs

@@ -123,6 +123,8 @@ internal class ActionAccumulator
                     toExecute.Any(static action => action.action is RefreshViewport_PassthroughAction);
                 bool refreshPreviewsRequest =
                     toExecute.Any(static action => action.action is RefreshPreviews_PassthroughAction);
+                bool refreshPreviewRequest =
+                    toExecute.Any(static action => action.action is RefreshPreview_PassthroughAction);
                 bool changeFrameRequest =
                     toExecute.Any(static action => action.action is SetActiveFrame_PassthroughAction);
 
@@ -146,7 +148,7 @@ internal class ActionAccumulator
 
                 if (!previewsDisabled)
                 {
-                    if (undoBoundaryPassed || viewportRefreshRequest || changeFrameRequest ||
+                    if (undoBoundaryPassed || viewportRefreshRequest || refreshPreviewsRequest || refreshPreviewRequest || changeFrameRequest ||
                         document.SizeBindable.LongestAxis <= LiveUpdatePerformanceThreshold)
                     {
                         previewTextures = previewUpdater.GatherPreviewsToUpdate(

+ 7 - 0
src/PixiEditor/Models/DocumentPassthroughActions/RefreshPreview_PassthroughAction.cs

@@ -0,0 +1,7 @@
+using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.ChangeableDocument.ChangeInfos;
+using PixiEditor.Models.Position;
+
+namespace PixiEditor.Models.DocumentPassthroughActions;
+
+internal record class RefreshPreview_PassthroughAction(Guid Id) : IAction, IChangeInfo;

+ 4 - 0
src/PixiEditor/Models/Rendering/AffectedAreasGatherer.cs

@@ -210,6 +210,10 @@ internal class AffectedAreasGatherer
                     AddWholeCanvasToEveryImagePreview(false);
                     AddWholeCanvasToEveryMaskPreview();
                     break;
+                case RefreshPreview_PassthroughAction info:
+                    AddToImagePreviews(info.Id);
+                    AddToNodePreviews(info.Id);
+                    break;
             }
         }
     }

+ 9 - 3
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -12,6 +12,7 @@ using PixiEditor.Models.Handlers;
 using Drawie.Numerics;
 using PixiEditor.ViewModels.Nodes;
 using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.Models.DocumentPassthroughActions;
 using PixiEditor.ViewModels.Document;
 
 namespace PixiEditor.Models.Rendering;
@@ -117,7 +118,7 @@ internal class MemberPreviewUpdater
                 var member = internals.Tracker.Document.FindMember(node.Id);
                 if (structureMemberHandler.Preview == null)
                 {
-                    structureMemberHandler.Preview = new TexturePreview(node.Id);
+                    structureMemberHandler.Preview = new TexturePreview(node.Id, RequestRender);
                     continue;
                 }
 
@@ -247,7 +248,7 @@ internal class MemberPreviewUpdater
 
                 if (structureMemberHandler.MaskPreview == null)
                 {
-                    structureMemberHandler.MaskPreview = new TexturePreview(node.Id);
+                    structureMemberHandler.MaskPreview = new TexturePreview(node.Id, RequestRender);
                     continue;
                 }
 
@@ -346,7 +347,7 @@ internal class MemberPreviewUpdater
 
         if (node is IPreviewRenderable renderable)
         {
-            nodeVm.Preview ??= new TexturePreview(node.Id);
+            nodeVm.Preview ??= new TexturePreview(node.Id, RequestRender);
             if (nodeVm.Preview.Listeners.Count == 0)
             {
                 nodeVm.Preview.Preview?.Dispose();
@@ -379,4 +380,9 @@ internal class MemberPreviewUpdater
                 .Add(new PreviewRenderRequest(nodeVm.Preview.Preview, nodeVm.Preview.InvokeTextureUpdated));
         }
     }
+
+    private void RequestRender(Guid id)
+    {
+        internals.ActionAccumulator.AddActions(new RefreshPreview_PassthroughAction(id));
+    }
 }

+ 2 - 1
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -78,7 +78,8 @@ internal class SceneRenderer : IDisposable
         /*TODO:
          - [ ] Onion skinning
          - [ ] Previews generation
-         - Rendering optimizer
+         - [ ] Panning doesn't rerender in cases where it should
+         - [ ] Rendering optimizer
          - [?] Render thread and proper locking/synchronization
          */
 

+ 0 - 2
src/PixiEditor/ViewModels/Document/Nodes/StructureMemberViewModel.cs

@@ -19,8 +19,6 @@ internal abstract class StructureMemberViewModel<T> : NodeViewModel<T>, IStructu
 {
     public StructureMemberViewModel()
     {
-        Preview = new TexturePreview(Id);
-        MaskPreview = new TexturePreview(Id);
     }
 
     private bool isVisible;

+ 5 - 1
src/PixiEditor/ViewModels/Document/TexturePreview.cs

@@ -25,14 +25,18 @@ public class TexturePreview : ObservableObject
 
     public event Action TextureUpdated;
 
-    public TexturePreview(Guid forId)
+    private Action<Guid> requestRender;
+
+    public TexturePreview(Guid forId, Action<Guid> requestRender)
     {
         Id = forId;
+        this.requestRender = requestRender;
     }
 
     public void Attach(object source, Func<VecI> getSize)
     {
         Listeners.TryAdd(source, getSize);
+        requestRender(Id);
     }
 
     public void Detach(object source)