Browse Source

Proper gamma correction

flabbet 8 months ago
parent
commit
47c78eaa66
43 changed files with 247 additions and 217 deletions
  1. 5 4
      src/ChunkyImageLib/Chunk.cs
  2. 13 11
      src/ChunkyImageLib/ChunkyImage.cs
  3. 1 1
      src/ChunkyImageLib/CommittedChunkStorage.cs
  4. 2 0
      src/ChunkyImageLib/IReadOnlyChunkyImage.cs
  5. 1 1
      src/Drawie
  6. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  7. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IChunkRenderable.cs
  8. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs
  9. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/SceneObjectRenderContext.cs
  10. 1 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs
  11. 2 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs
  12. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CreateImageNode.cs
  13. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs
  14. 1 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs
  15. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  16. 9 8
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  17. 1 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  18. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  19. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  20. 14 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  21. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  22. 1 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  23. 2 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/RenderNode.cs
  24. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShapeNode.cs
  25. 0 14
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/ShapeNode.cs
  26. 7 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs
  27. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
  28. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs
  29. 7 2
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs
  30. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs
  31. 35 0
      src/PixiEditor.ChangeableDocument/Changes/Properties/ChangeProcessingColorSpace_Change.cs
  32. 5 6
      src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs
  33. 5 1
      src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs
  34. 7 0
      src/PixiEditor/Helpers/DocumentViewModelBuilder.cs
  35. 1 0
      src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs
  36. 3 2
      src/PixiEditor/Models/Rendering/AnimationPreviewRenderer.cs
  37. 54 40
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  38. 13 3
      src/PixiEditor/Models/Rendering/PreviewPainter.cs
  39. 5 4
      src/PixiEditor/Models/Rendering/SceneRenderer.cs
  40. 1 0
      src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs
  41. 26 74
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  42. 1 2
      src/PixiEditor/Views/Visuals/PreviewPainterControl.cs
  43. 1 1
      src/PixiParser

+ 5 - 4
src/ChunkyImageLib/Chunk.cs

@@ -2,6 +2,7 @@
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
@@ -48,21 +49,21 @@ public class Chunk : IDisposable
     public bool Disposed => returned;
     public bool Disposed => returned;
 
 
     private Surface internalSurface;
     private Surface internalSurface;
-    private Chunk(ChunkResolution resolution)
+    private Chunk(ChunkResolution resolution, ColorSpace colorSpace)
     {
     {
         int size = resolution.PixelSize();
         int size = resolution.PixelSize();
 
 
         Resolution = resolution;
         Resolution = resolution;
         PixelSize = new(size, size);
         PixelSize = new(size, size);
-        internalSurface = Surface.ForProcessing(PixelSize);
+        internalSurface = new Surface(new ImageInfo(size, size, ColorType.RgbaF16, AlphaType.Premul, colorSpace));
     }
     }
 
 
     /// <summary>
     /// <summary>
     /// Tries to take a chunk with the <paramref name="resolution"/> from the pool, or creates a new one
     /// Tries to take a chunk with the <paramref name="resolution"/> from the pool, or creates a new one
     /// </summary>
     /// </summary>
-    public static Chunk Create(ChunkResolution resolution = ChunkResolution.Full)
+    public static Chunk Create(ColorSpace chunkCs, ChunkResolution resolution = ChunkResolution.Full)
     {
     {
-        var chunk = ChunkPool.Instance.Get(resolution) ?? new Chunk(resolution);
+        var chunk = ChunkPool.Instance.Get(resolution) ?? new Chunk(resolution, chunkCs);
         chunk.returned = false;
         chunk.returned = false;
         Interlocked.Increment(ref chunkCounter);
         Interlocked.Increment(ref chunkCounter);
         return chunk;
         return chunk;

+ 13 - 11
src/ChunkyImageLib/ChunkyImage.cs

@@ -75,6 +75,8 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     private static Paint AddingPaint { get; } = new Paint() { BlendMode = BlendMode.Plus };
     private static Paint AddingPaint { get; } = new Paint() { BlendMode = BlendMode.Plus };
     private readonly Paint blendModePaint = new Paint() { BlendMode = BlendMode.Src };
     private readonly Paint blendModePaint = new Paint() { BlendMode = BlendMode.Src };
 
 
+    public ColorSpace ProcessingColorSpace { get; set; } = ColorSpace.CreateSrgbLinear();
+    
     public int CommitCounter => commitCounter;
     public int CommitCounter => commitCounter;
 
 
     public VecI CommittedSize { get; private set; }
     public VecI CommittedSize { get; private set; }
@@ -324,7 +326,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                     : latestChunk.Surface.GetSRGBPixel(posInChunk);
                     : latestChunk.Surface.GetSRGBPixel(posInChunk);
                 // using a whole chunk just to draw 1 pixel is kinda dumb,
                 // using a whole chunk just to draw 1 pixel is kinda dumb,
                 // but this should be faster than any approach that requires allocations
                 // but this should be faster than any approach that requires allocations
-                using Chunk tempChunk = Chunk.Create(ChunkResolution.Eighth);
+                using Chunk tempChunk = Chunk.Create(ProcessingColorSpace, ChunkResolution.Eighth);
                 using Paint committedPaint = new Paint() { Color = committedColor, BlendMode = BlendMode.Src };
                 using Paint committedPaint = new Paint() { Color = committedColor, BlendMode = BlendMode.Src };
                 using Paint latestPaint = new Paint() { Color = latestColor, BlendMode = this.blendMode };
                 using Paint latestPaint = new Paint() { Color = latestColor, BlendMode = this.blendMode };
                 tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, committedPaint);
                 tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, committedPaint);
@@ -381,7 +383,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             }
             }
 
 
             // combine with committed and then draw
             // combine with committed and then draw
-            using var tempChunk = Chunk.Create(resolution);
+            using var tempChunk = Chunk.Create(ProcessingColorSpace, resolution);
             tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(committedChunk.Surface.DrawingSurface, 0, 0,
             tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(committedChunk.Surface.DrawingSurface, 0, 0,
                 ReplacingPaint);
                 ReplacingPaint);
             blendModePaint.BlendMode = blendMode;
             blendModePaint.BlendMode = blendMode;
@@ -1022,7 +1024,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                     blendModePaint.BlendMode = blendMode;
                     blendModePaint.BlendMode = blendMode;
                     if (lockTransparency)
                     if (lockTransparency)
                     {
                     {
-                        using Chunk tempChunk = Chunk.Create(resolution);
+                        using Chunk tempChunk = Chunk.Create(ProcessingColorSpace, resolution);
                         tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(maybeCommitted.Surface.DrawingSurface, 0, 0,
                         tempChunk.Surface.DrawingSurface.Canvas.DrawSurface(maybeCommitted.Surface.DrawingSurface, 0, 0,
                             ReplacingPaint);
                             ReplacingPaint);
                         maybeCommitted.Surface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, 0, 0,
                         maybeCommitted.Surface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, 0, 0,
@@ -1197,7 +1199,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             return new FilledChunk();
             return new FilledChunk();
         }
         }
 
 
-        var intersection = Chunk.Create(resolution);
+        var intersection = Chunk.Create(ProcessingColorSpace, resolution);
         intersection.Surface.DrawingSurface.Canvas.Clear(Colors.White);
         intersection.Surface.DrawingSurface.Canvas.Clear(Colors.White);
 
 
         foreach (var mask in activeClips)
         foreach (var mask in activeClips)
@@ -1250,7 +1252,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             // drawing with raster clipping
             // drawing with raster clipping
             var clip = combinedRasterClips.AsT2;
             var clip = combinedRasterClips.AsT2;
 
 
-            using var tempChunk = Chunk.Create(targetChunk.Resolution);
+            using var tempChunk = Chunk.Create(ProcessingColorSpace, targetChunk.Resolution);
             targetChunk.DrawChunkOn(tempChunk.Surface.DrawingSurface, VecI.Zero, ReplacingPaint);
             targetChunk.DrawChunkOn(tempChunk.Surface.DrawingSurface, VecI.Zero, ReplacingPaint);
 
 
             CallDrawWithClip(chunkOperation, operationAffectedArea.GlobalArea, tempChunk, resolution, chunkPos);
             CallDrawWithClip(chunkOperation, operationAffectedArea.GlobalArea, tempChunk, resolution, chunkPos);
@@ -1365,7 +1367,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         // for full res chunks: nothing exists, create brand new chunk
         // for full res chunks: nothing exists, create brand new chunk
         if (resolution == ChunkResolution.Full)
         if (resolution == ChunkResolution.Full)
         {
         {
-            var newChunk = Chunk.Create(resolution);
+            var newChunk = Chunk.Create(ProcessingColorSpace, resolution);
             committedChunks[resolution][chunkPos] = newChunk;
             committedChunks[resolution][chunkPos] = newChunk;
             return newChunk;
             return newChunk;
         }
         }
@@ -1374,7 +1376,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         Chunk? existingFullResChunk = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
         Chunk? existingFullResChunk = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
         if (existingFullResChunk is not null)
         if (existingFullResChunk is not null)
         {
         {
-            var newChunk = Chunk.Create(resolution);
+            var newChunk = Chunk.Create(ProcessingColorSpace, resolution);
             newChunk.Surface.DrawingSurface.Canvas.Save();
             newChunk.Surface.DrawingSurface.Canvas.Save();
             newChunk.Surface.DrawingSurface.Canvas.Scale((float)resolution.Multiplier());
             newChunk.Surface.DrawingSurface.Canvas.Scale((float)resolution.Multiplier());
 
 
@@ -1388,7 +1390,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         // for low res chunks: full res version doesn't exist
         // for low res chunks: full res version doesn't exist
         {
         {
             GetOrCreateCommittedChunk(chunkPos, ChunkResolution.Full);
             GetOrCreateCommittedChunk(chunkPos, ChunkResolution.Full);
-            var newChunk = Chunk.Create(resolution);
+            var newChunk = Chunk.Create(ProcessingColorSpace, resolution);
             committedChunks[resolution][chunkPos] = newChunk;
             committedChunks[resolution][chunkPos] = newChunk;
             return newChunk;
             return newChunk;
         }
         }
@@ -1408,7 +1410,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         var maybeCommittedAnyRes = MaybeGetCommittedChunk(chunkPos, resolution);
         var maybeCommittedAnyRes = MaybeGetCommittedChunk(chunkPos, resolution);
         if (maybeCommittedAnyRes is not null)
         if (maybeCommittedAnyRes is not null)
         {
         {
-            Chunk newChunk = Chunk.Create(resolution);
+            Chunk newChunk = Chunk.Create(ProcessingColorSpace, resolution);
             if (blendMode == BlendMode.Src)
             if (blendMode == BlendMode.Src)
                 maybeCommittedAnyRes.Surface.CopyTo(newChunk.Surface);
                 maybeCommittedAnyRes.Surface.CopyTo(newChunk.Surface);
             else
             else
@@ -1424,14 +1426,14 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             //create low res committed chunk
             //create low res committed chunk
             var committedChunkLowRes = GetOrCreateCommittedChunk(chunkPos, resolution);
             var committedChunkLowRes = GetOrCreateCommittedChunk(chunkPos, resolution);
             //create latest based on it
             //create latest based on it
-            Chunk newChunk = Chunk.Create(resolution);
+            Chunk newChunk = Chunk.Create(ProcessingColorSpace, resolution);
             committedChunkLowRes.Surface.CopyTo(newChunk.Surface);
             committedChunkLowRes.Surface.CopyTo(newChunk.Surface);
             latestChunks[resolution][chunkPos] = newChunk;
             latestChunks[resolution][chunkPos] = newChunk;
             return newChunk;
             return newChunk;
         }
         }
 
 
         // no previous chunks exist
         // no previous chunks exist
-        var newLatestChunk = Chunk.Create(resolution);
+        var newLatestChunk = Chunk.Create(ProcessingColorSpace, resolution);
         newLatestChunk.Surface.DrawingSurface.Canvas.Clear();
         newLatestChunk.Surface.DrawingSurface.Canvas.Clear();
         latestChunks[resolution][chunkPos] = newLatestChunk;
         latestChunks[resolution][chunkPos] = newLatestChunk;
         return newLatestChunk;
         return newLatestChunk;

+ 1 - 1
src/ChunkyImageLib/CommittedChunkStorage.cs

@@ -16,7 +16,7 @@ public class CommittedChunkStorage : IDisposable
     {
     {
         foreach (var chunkPos in committedChunksToSave)
         foreach (var chunkPos in committedChunksToSave)
         {
         {
-            Chunk copy = Chunk.Create();
+            Chunk copy = Chunk.Create(image.ProcessingColorSpace);
             if (!image.DrawCommittedChunkOn(chunkPos, ChunkResolution.Full, copy.Surface.DrawingSurface, VecI.Zero, ReplacingPaint))
             if (!image.DrawCommittedChunkOn(chunkPos, ChunkResolution.Full, copy.Surface.DrawingSurface, VecI.Zero, ReplacingPaint))
             {
             {
                 copy.Dispose();
                 copy.Dispose();

+ 2 - 0
src/ChunkyImageLib/IReadOnlyChunkyImage.cs

@@ -2,6 +2,7 @@
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
@@ -22,4 +23,5 @@ public interface IReadOnlyChunkyImage
     HashSet<VecI> FindAllChunks();
     HashSet<VecI> FindAllChunks();
     VecI CommittedSize { get; }
     VecI CommittedSize { get; }
     VecI LatestSize { get; }
     VecI LatestSize { get; }
+    public ColorSpace ProcessingColorSpace { get; }
 }
 }

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 54e24062eba5e133998933b58a4aa30316c63780
+Subproject commit fcff0f9fb01a65935d1bd8bdf15d36edf5379c02

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -32,6 +32,7 @@ internal class Document : IChangeable, IReadOnlyDocument
 
 
     IReadOnlyReferenceLayer? IReadOnlyDocument.ReferenceLayer => ReferenceLayer;
     IReadOnlyReferenceLayer? IReadOnlyDocument.ReferenceLayer => ReferenceLayer;
     public DocumentRenderer Renderer { get; }
     public DocumentRenderer Renderer { get; }
+    public ColorSpace ProcessingColorSpace { get; internal set; } = ColorSpace.CreateSrgbLinear();
 
 
     /// <summary>
     /// <summary>
     /// The default size for a new document
     /// The default size for a new document

+ 3 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IChunkRenderable.cs

@@ -1,9 +1,10 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 
 public interface IChunkRenderable
 public interface IChunkRenderable
 {
 {
-    public void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime);
+    public void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime, ColorSpace processingColorSpace);
 }
 }

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs

@@ -1,12 +1,13 @@
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 
 public interface IPreviewRenderable
 public interface IPreviewRenderable
 {
 {
     public RectD? GetPreviewBounds(int frame, string elementToRenderName = ""); 
     public RectD? GetPreviewBounds(int frame, string elementToRenderName = ""); 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName);
         string elementToRenderName);
 }
 }

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/SceneObjectRenderContext.cs

@@ -1,6 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
@@ -12,7 +13,7 @@ public class SceneObjectRenderContext : RenderContext
     public RenderOutputProperty TargetPropertyOutput { get; }
     public RenderOutputProperty TargetPropertyOutput { get; }
 
 
     public SceneObjectRenderContext(RenderOutputProperty targetPropertyOutput, DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime,
     public SceneObjectRenderContext(RenderOutputProperty targetPropertyOutput, DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime,
-        ChunkResolution chunkResolution, VecI docSize, bool renderSurfaceIsScene, double opacity) : base(surface, frameTime, chunkResolution, docSize, opacity)
+        ChunkResolution chunkResolution, VecI docSize, bool renderSurfaceIsScene, ColorSpace processingColorSpace, double opacity) : base(surface, frameTime, chunkResolution, docSize, processingColorSpace, opacity)
     {
     {
         TargetPropertyOutput = targetPropertyOutput;
         TargetPropertyOutput = targetPropertyOutput;
         LocalBounds = localBounds;
         LocalBounds = localBounds;

+ 1 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -119,15 +119,13 @@ public class CombineChannelsNode : RenderNode
         return finalBounds;
         return finalBounds;
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         if (Red.Value == null && Green.Value == null && Blue.Value == null && Alpha.Value == null)
         if (Red.Value == null && Green.Value == null && Blue.Value == null && Alpha.Value == null)
         {
         {
             return false;
             return false;
         }
         }
 
 
-        RenderContext context = new(renderOn, frame, resolution, VecI.One);
-        
         OnPaint(context, renderOn); 
         OnPaint(context, renderOn); 
         
         
         return true;
         return true;

+ 2 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs

@@ -97,18 +97,16 @@ public class SeparateChannelsNode : Node, IRenderInput, IPreviewRenderable
         return bounds;
         return bounds;
     }
     }
 
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         if (Image.Value == null)
         if (Image.Value == null)
             return false;
             return false;
 
 
-        RectD? bounds = GetPreviewBounds(frame, elementToRenderName);
+        RectD? bounds = GetPreviewBounds(context.FrameTime.Frame, elementToRenderName);
         
         
         if (bounds == null)
         if (bounds == null)
             return false;
             return false;
         
         
-        RenderContext context = new(renderOn, frame, resolution, VecI.One);
-
         renderOn.Canvas.Save();
         renderOn.Canvas.Save();
 
 
         _paint.ColorFilter = Grayscale.Value ? _redGrayscaleFilter : _redFilter;
         _paint.ColorFilter = Grayscale.Value ? _redGrayscaleFilter : _redFilter;

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CreateImageNode.cs

@@ -36,14 +36,14 @@ public class CreateImageNode : Node
             return;
             return;
         }
         }
 
 
-        var surface = RequestTexture(0, Size.Value, false);
+        var surface = RequestTexture(0, Size.Value, context.ProcessingColorSpace, false);
 
 
         surface.DrawingSurface.Canvas.Clear(Fill.Value);
         surface.DrawingSurface.Canvas.Clear(Fill.Value);
 
 
         int saved = surface.DrawingSurface.Canvas.Save();
         int saved = surface.DrawingSurface.Canvas.Save();
 
 
         RenderContext ctx = new RenderContext(surface.DrawingSurface, context.FrameTime, context.ChunkResolution,
         RenderContext ctx = new RenderContext(surface.DrawingSurface, context.FrameTime, context.ChunkResolution,
-            context.DocumentSize);
+            context.DocumentSize, context.ProcessingColorSpace);
 
 
         Content.Value?.Paint(ctx, surface.DrawingSurface);
         Content.Value?.Paint(ctx, surface.DrawingSurface);
 
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs

@@ -36,7 +36,7 @@ public class DebugBlendModeNode : Node
             return;
             return;
 
 
         var size = new VecI(Math.Max(src.Size.X, dst.Size.X), int.Max(src.Size.Y, dst.Size.Y));
         var size = new VecI(Math.Max(src.Size.X, dst.Size.X), int.Max(src.Size.Y, dst.Size.Y));
-        var workingSurface = RequestTexture(0, size);
+        var workingSurface = RequestTexture(0, size, context.ProcessingColorSpace);
 
 
         workingSurface.DrawingSurface.Canvas.DrawSurface(dst.DrawingSurface, 0, 0, blendModeOpacityPaint);
         workingSurface.DrawingSurface.Canvas.DrawSurface(dst.DrawingSurface, 0, 0, blendModeOpacityPaint);
 
 

+ 1 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs

@@ -40,14 +40,12 @@ public class ApplyFilterNode : RenderNode, IRenderInput
         return PreviewUtils.FindPreviewBounds(Background.Connection, frame, elementToRenderName);
         return PreviewUtils.FindPreviewBounds(Background.Connection, frame, elementToRenderName);
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
         string elementToRenderName)
     {
     {
         if (Background.Value == null)
         if (Background.Value == null)
             return false;
             return false;
 
 
-        RenderContext context = new(renderOn, frame, ChunkResolution.Full, VecI.One);
-
         int layer = renderOn.Canvas.SaveLayer(_paint);
         int layer = renderOn.Canvas.SaveLayer(_paint);
         Background.Value.Paint(context, renderOn);
         Background.Value.Paint(context, renderOn);
         renderOn.Canvas.RestoreToCount(layer);
         renderOn.Canvas.RestoreToCount(layer);

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs

@@ -83,7 +83,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
     private void RenderFolderContent(SceneObjectRenderContext sceneContext, RectD bounds, bool useFilters)
     private void RenderFolderContent(SceneObjectRenderContext sceneContext, RectD bounds, bool useFilters)
     {
     {
         VecI size = (VecI)bounds.Size;
         VecI size = (VecI)bounds.Size;
-        var outputWorkingSurface = RequestTexture(0, size, true);
+        var outputWorkingSurface = RequestTexture(0, size, sceneContext.ProcessingColorSpace, true);
 
 
         blendPaint.ImageFilter = null;
         blendPaint.ImageFilter = null;
         blendPaint.ColorFilter = null;
         blendPaint.ColorFilter = null;
@@ -94,7 +94,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
 
 
         if (Background.Value != null && sceneContext.TargetPropertyOutput != RawOutput)
         if (Background.Value != null && sceneContext.TargetPropertyOutput != RawOutput)
         {
         {
-            Texture tempSurface = RequestTexture(1, outputWorkingSurface.Size);
+            Texture tempSurface = RequestTexture(1, outputWorkingSurface.Size, sceneContext.ProcessingColorSpace);
             if (Background.Connection.Node is IClipSource clipSource && ClipToPreviousMember)
             if (Background.Connection.Node is IClipSource clipSource && ClipToPreviousMember)
             {
             {
                 DrawClipSource(tempSurface.DrawingSurface, clipSource, sceneContext);
                 DrawClipSource(tempSurface.DrawingSurface, clipSource, sceneContext);
@@ -201,12 +201,12 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
         return GetTightBounds(frame);
         return GetTightBounds(frame);
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
         string elementToRenderName)
     {
     {
         if (elementToRenderName == nameof(EmbeddedMask))
         if (elementToRenderName == nameof(EmbeddedMask))
         {
         {
-            return base.RenderPreview(renderOn, resolution, frame, elementToRenderName);
+            return base.RenderPreview(renderOn, context, elementToRenderName);
         }
         }
 
 
         // TODO: Make preview better, with filters, clips and stuff
         // TODO: Make preview better, with filters, clips and stuff
@@ -219,7 +219,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPrev
                 IReadOnlyNode node = executionQueue.Dequeue();
                 IReadOnlyNode node = executionQueue.Dequeue();
                 if (node is IPreviewRenderable previewRenderable)
                 if (node is IPreviewRenderable previewRenderable)
                 {
                 {
-                    previewRenderable.RenderPreview(renderOn, resolution, frame, elementToRenderName);
+                    previewRenderable.RenderPreview(renderOn, context, elementToRenderName);
                 }
                 }
             }
             }
         }
         }

+ 9 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -6,6 +6,7 @@ using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
@@ -136,7 +137,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         }
         }
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOnto, ChunkResolution resolution, int frame,
+    public override bool RenderPreview(DrawingSurface renderOnto, RenderContext context,
         string elementToRenderName)
         string elementToRenderName)
     {
     {
         if (IsDisposed)
         if (IsDisposed)
@@ -146,10 +147,10 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
 
         if (elementToRenderName == nameof(EmbeddedMask))
         if (elementToRenderName == nameof(EmbeddedMask))
         {
         {
-            return base.RenderPreview(renderOnto, resolution, frame, elementToRenderName);
+            return base.RenderPreview(renderOnto, context, elementToRenderName);
         }
         }
 
 
-        var img = GetLayerImageAtFrame(frame);
+        var img = GetLayerImageAtFrame(context.FrameTime.Frame);
 
 
         if (Guid.TryParse(elementToRenderName, out Guid guid))
         if (Guid.TryParse(elementToRenderName, out Guid guid))
         {
         {
@@ -166,7 +167,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             return false;
             return false;
         }
         }
 
 
-        if (renderedSurfaceFrame == frame)
+        if (renderedSurfaceFrame == context.FrameTime.Frame)
         {
         {
             renderOnto.Canvas.DrawSurface(fullResrenderedSurface.DrawingSurface, VecI.Zero, blendPaint);
             renderOnto.Canvas.DrawSurface(fullResrenderedSurface.DrawingSurface, VecI.Zero, blendPaint);
         }
         }
@@ -174,7 +175,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         {
         {
             img.DrawMostUpToDateRegionOn(
             img.DrawMostUpToDateRegionOn(
                 new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
                 new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
-                resolution,
+                context.ChunkResolution,
                 renderOnto, VecI.Zero, blendPaint);
                 renderOnto, VecI.Zero, blendPaint);
         }
         }
 
 
@@ -239,13 +240,13 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
 
     void IReadOnlyImageNode.ForEveryFrame(Action<IReadOnlyChunkyImage> action) => ForEveryFrame(action);
     void IReadOnlyImageNode.ForEveryFrame(Action<IReadOnlyChunkyImage> action) => ForEveryFrame(action);
 
 
-    public override void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime)
+    public override void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime, ColorSpace processColorSpace)
     {
     {
-        base.RenderChunk(chunkPos, resolution, frameTime);
+        base.RenderChunk(chunkPos, resolution, frameTime, processColorSpace);
 
 
         var img = GetLayerImageAtFrame(frameTime.Frame);
         var img = GetLayerImageAtFrame(frameTime.Frame);
 
 
-        RenderChunkyImageChunk(chunkPos, resolution, img, 85, ref fullResrenderedSurface);
+        RenderChunkyImageChunk(chunkPos, resolution, img, 85, processColorSpace, ref fullResrenderedSurface);
         renderedSurfaceFrame = frameTime.Frame;
         renderedSurfaceFrame = frameTime.Frame;
     }
     }
 
 

+ 1 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs

@@ -103,14 +103,13 @@ public class MergeNode : RenderNode
         return totalBounds;
         return totalBounds;
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         if (Top.Value == null && Bottom.Value == null)
         if (Top.Value == null && Bottom.Value == null)
         {
         {
             return false;
             return false;
         }
         }
 
 
-        RenderContext context = new RenderContext(renderOn, frame, ChunkResolution.Full, VecI.Zero);
         Merge(renderOn, context);
         Merge(renderOn, context);
 
 
         return true;
         return true;

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs

@@ -55,7 +55,7 @@ public class ModifyImageLeftNode : Node, IPairNode, IPreviewRenderable
         return new RectD(0, 0, Image.Value.Size.X, Image.Value.Size.Y);
         return new RectD(0, 0, Image.Value.Size.X, Image.Value.Size.Y);
     }
     }
 
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         if(Image.Value is null)
         if(Image.Value is null)
         {
         {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -110,7 +110,7 @@ public class ModifyImageRightNode : RenderNode, IPairNode, ICustomShaderNode
         return null;
         return null;
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         //TODO: Implement
         //TODO: Implement
         return false;
         return false;

+ 14 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs

@@ -10,6 +10,7 @@ using Drawie.Backend.Core;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Shaders;
 using Drawie.Backend.Core.Shaders;
 using Drawie.Backend.Core.Shaders.Generation;
 using Drawie.Backend.Core.Shaders.Generation;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -83,14 +84,14 @@ public abstract class Node : IReadOnlyNode, IDisposable
         }
         }
     }
     }
 
 
-    protected Texture RequestTexture(int id, VecI size, bool clear = true)
+    protected Texture RequestTexture(int id, VecI size, ColorSpace processingCs, bool clear = true)
     {
     {
         if (_managedTextures.TryGetValue(id, out var texture))
         if (_managedTextures.TryGetValue(id, out var texture))
         {
         {
             if (texture.Size != size || texture.IsDisposed)
             if (texture.Size != size || texture.IsDisposed)
             {
             {
                 texture.Dispose();
                 texture.Dispose();
-                texture = Texture.ForProcessing(size);
+                texture = new Texture(CreateImageInfo(size, processingCs));
                 _managedTextures[id] = texture;
                 _managedTextures[id] = texture;
                 return texture;
                 return texture;
             }
             }
@@ -103,9 +104,19 @@ public abstract class Node : IReadOnlyNode, IDisposable
             return texture;
             return texture;
         }
         }
 
 
-        _managedTextures[id] = Texture.ForProcessing(size);
+        _managedTextures[id] = new Texture(CreateImageInfo(size, processingCs));
         return _managedTextures[id];
         return _managedTextures[id];
     }
     }
+    
+    private ImageInfo CreateImageInfo(VecI size, ColorSpace processingCs)
+    {
+        if (processingCs == null)
+        {
+            return new ImageInfo(size.X, size.Y, ColorType.RgbaF16, AlphaType.Premul, ColorSpace.CreateSrgbLinear()) { GpuBacked = true};
+        }
+
+        return new ImageInfo(size.X, size.Y, ColorType.RgbaF16, AlphaType.Premul, processingCs) { GpuBacked = true };
+    }
 
 
     public void TraverseBackwards(Func<IReadOnlyNode, bool> action)
     public void TraverseBackwards(Func<IReadOnlyNode, bool> action)
     {
     {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs

@@ -94,7 +94,7 @@ public class NoiseNode : RenderNode
         return new RectD(0, 0, 128, 128); 
         return new RectD(0, 0, 128, 128); 
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         var shader = SelectShader();
         var shader = SelectShader();
         if (shader == null)
         if (shader == null)

+ 1 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs

@@ -48,14 +48,13 @@ public class OutputNode : Node, IRenderInput, IPreviewRenderable
         return new RectD(0, 0, lastDocumentSize.Value.X, lastDocumentSize.Value.Y); 
         return new RectD(0, 0, lastDocumentSize.Value.X, lastDocumentSize.Value.Y); 
     }
     }
 
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         if (Input.Value == null)
         if (Input.Value == null)
         {
         {
             return false;
             return false;
         }
         }
         
         
-        RenderContext context = new(renderOn, frame, resolution, VecI.One);
         int saved = renderOn.Canvas.Save();
         int saved = renderOn.Canvas.Save();
         Input.Value.Paint(context, renderOn);
         Input.Value.Paint(context, renderOn);
         
         

+ 2 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/RenderNode.cs

@@ -2,7 +2,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
-using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -40,7 +39,7 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
                                && surface.DeviceClipBounds.Size != context.DocumentSize;
                                && surface.DeviceClipBounds.Size != context.DocumentSize;
         if (useIntermediate)
         if (useIntermediate)
         {
         {
-            Texture intermediate = RequestTexture(0, context.DocumentSize);
+            Texture intermediate = RequestTexture(0, context.DocumentSize, context.ProcessingColorSpace);
             target = intermediate.DrawingSurface;
             target = intermediate.DrawingSurface;
         }
         }
 
 
@@ -56,6 +55,6 @@ public abstract class RenderNode : Node, IPreviewRenderable, IHighDpiRenderNode
 
 
     public abstract RectD? GetPreviewBounds(int frame, string elementToRenderName = "");
     public abstract RectD? GetPreviewBounds(int frame, string elementToRenderName = "");
 
 
-    public abstract bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public abstract bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName);
         string elementToRenderName);
 }
 }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RasterizeShapeNode.cs

@@ -35,7 +35,7 @@ public class RasterizeShapeNode : RenderNode
         return Data?.Value?.TransformedAABB;
         return Data?.Value?.TransformedAABB;
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         var shape = Data.Value;
         var shape = Data.Value;
 
 

+ 0 - 14
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/ShapeNode.cs

@@ -23,21 +23,7 @@ public abstract class ShapeNode<T> : Node where T : ShapeVectorData
         var data = GetShapeData(context);
         var data = GetShapeData(context);
 
 
         Output.Value = data;
         Output.Value = data;
-        
-        /*if (data == null || !data.IsValid())
-            return;
-
-        return RasterizePreview(data, context.DocumentSize);*/
     }
     }
     
     
     protected abstract T? GetShapeData(RenderContext context);
     protected abstract T? GetShapeData(RenderContext context);
-
-    public Texture RasterizePreview(ShapeVectorData vectorData, VecI size)
-    {
-        Texture texture = RequestTexture(0, size);
-        
-        vectorData.RasterizeTransformed(texture.DrawingSurface);
-        
-        return texture;
-    }
 }
 }

+ 7 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs

@@ -7,6 +7,7 @@ using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
@@ -144,6 +145,7 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
 
 
         SceneObjectRenderContext renderObjectContext = new SceneObjectRenderContext(output, renderTarget, localBounds,
         SceneObjectRenderContext renderObjectContext = new SceneObjectRenderContext(output, renderTarget, localBounds,
             context.FrameTime, context.ChunkResolution, context.DocumentSize, renderTarget == context.RenderSurface,
             context.FrameTime, context.ChunkResolution, context.DocumentSize, renderTarget == context.RenderSurface,
+            context.ProcessingColorSpace,
             context.Opacity);
             context.Opacity);
         renderObjectContext.FullRerender = context.FullRerender;
         renderObjectContext.FullRerender = context.FullRerender;
         return renderObjectContext;
         return renderObjectContext;
@@ -192,13 +194,13 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
         maskCacheHash = EmbeddedMask?.GetCacheHash() ?? 0;
         maskCacheHash = EmbeddedMask?.GetCacheHash() ?? 0;
     }
     }
 
 
-    public virtual void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime)
+    public virtual void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime, ColorSpace processingColorSpace)
     {
     {
-        RenderChunkyImageChunk(chunkPos, resolution, EmbeddedMask, 55, ref renderedMask);
+        RenderChunkyImageChunk(chunkPos, resolution, EmbeddedMask, 55, processingColorSpace, ref renderedMask);
     }
     }
 
 
     protected void RenderChunkyImageChunk(VecI chunkPos, ChunkResolution resolution, ChunkyImage img,
     protected void RenderChunkyImageChunk(VecI chunkPos, ChunkResolution resolution, ChunkyImage img,
-        int textureId,
+        int textureId, ColorSpace processingColorSpace,
         ref Texture? renderSurface)
         ref Texture? renderSurface)
     {
     {
         if (img is null)
         if (img is null)
@@ -208,7 +210,7 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
 
 
         VecI targetSize = img.LatestSize;
         VecI targetSize = img.LatestSize;
         
         
-        renderSurface = RequestTexture(textureId, targetSize, false);
+        renderSurface = RequestTexture(textureId, targetSize, processingColorSpace, false);
 
 
         int saved = renderSurface.DrawingSurface.Canvas.Save();
         int saved = renderSurface.DrawingSurface.Canvas.Save();
         
         
@@ -300,7 +302,7 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
         return null;
         return null;
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
         string elementToRenderName)
     {
     {
         if (elementToRenderName != nameof(EmbeddedMask))
         if (elementToRenderName != nameof(EmbeddedMask))

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs

@@ -85,7 +85,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
         return null;
         return null;
     }
     }
 
 
-    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
         string elementToRenderName)
     {
     {
         if (ShapeData == null)
         if (ShapeData == null)

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs

@@ -102,4 +102,5 @@ public interface IReadOnlyDocument : IDisposable
     IReadOnlyList<IReadOnlyStructureNode> FindMemberPath(Guid guid);
     IReadOnlyList<IReadOnlyStructureNode> FindMemberPath(Guid guid);
     IReadOnlyReferenceLayer? ReferenceLayer { get; }
     IReadOnlyReferenceLayer? ReferenceLayer { get; }
     public DocumentRenderer Renderer { get; }
     public DocumentRenderer Renderer { get; }
+    public ColorSpace ProcessingColorSpace { get; }
 }
 }

+ 7 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
 using Drawie.Numerics;
 
 
@@ -19,10 +20,13 @@ internal class FloodFillChunkCache : IDisposable
     private readonly int frame;
     private readonly int frame;
 
 
     private readonly Dictionary<VecI, OneOf<Chunk, EmptyChunk>> acquiredChunks = new();
     private readonly Dictionary<VecI, OneOf<Chunk, EmptyChunk>> acquiredChunks = new();
+    
+    private ColorSpace processingColorSpace = ColorSpace.CreateSrgbLinear();
 
 
     public FloodFillChunkCache(IReadOnlyChunkyImage image)
     public FloodFillChunkCache(IReadOnlyChunkyImage image)
     {
     {
         this.image = image;
         this.image = image;
+        this.processingColorSpace = image.ProcessingColorSpace;
     }
     }
 
 
     public FloodFillChunkCache(HashSet<Guid> membersToRender, IReadOnlyDocument document, int frame)
     public FloodFillChunkCache(HashSet<Guid> membersToRender, IReadOnlyDocument document, int frame)
@@ -30,6 +34,7 @@ internal class FloodFillChunkCache : IDisposable
         this.membersToRender = membersToRender;
         this.membersToRender = membersToRender;
         this.document = document;
         this.document = document;
         this.frame = frame;
         this.frame = frame;
+        processingColorSpace = document.ProcessingColorSpace;
     }
     }
 
 
     public bool ChunkExistsInStorage(VecI pos)
     public bool ChunkExistsInStorage(VecI pos)
@@ -50,7 +55,7 @@ internal class FloodFillChunkCache : IDisposable
         {
         {
             if (document is null || membersToRender is null)
             if (document is null || membersToRender is null)
                 throw new InvalidOperationException();
                 throw new InvalidOperationException();
-            Chunk chunk = Chunk.Create();
+            Chunk chunk = Chunk.Create(processingColorSpace);
             chunk.Surface.DrawingSurface.Canvas.Save();
             chunk.Surface.DrawingSurface.Canvas.Save();
             
             
             VecI chunkPos = pos * ChunkyImage.FullChunkSize;
             VecI chunkPos = pos * ChunkyImage.FullChunkSize;
@@ -68,7 +73,7 @@ internal class FloodFillChunkCache : IDisposable
         // there is only a single image, just get the chunk from it
         // there is only a single image, just get the chunk from it
         if (!image.LatestOrCommittedChunkExists(pos))
         if (!image.LatestOrCommittedChunkExists(pos))
             return new EmptyChunk();
             return new EmptyChunk();
-        Chunk chunkOnImage = Chunk.Create(ChunkResolution.Full);
+        Chunk chunkOnImage = Chunk.Create(processingColorSpace, ChunkResolution.Full);
 
 
         if (!image.DrawMostUpToDateChunkOn(pos, ChunkResolution.Full, chunkOnImage.Surface.DrawingSurface, VecI.Zero, ReplacingPaint))
         if (!image.DrawMostUpToDateChunkOn(pos, ChunkResolution.Full, chunkOnImage.Surface.DrawingSurface, VecI.Zero, ReplacingPaint))
         {
         {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs

@@ -84,7 +84,7 @@ public static class FloodFillHelper
 
 
             if (!drawingChunks.ContainsKey(chunkPos))
             if (!drawingChunks.ContainsKey(chunkPos))
             {
             {
-                var chunk = Chunk.Create();
+                var chunk = Chunk.Create(document.ProcessingColorSpace);
                 chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
                 chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
                 drawingChunks[chunkPos] = chunk;
                 drawingChunks[chunkPos] = chunk;
             }
             }

+ 35 - 0
src/PixiEditor.ChangeableDocument/Changes/Properties/ChangeProcessingColorSpace_Change.cs

@@ -0,0 +1,35 @@
+using Drawie.Backend.Core.Surfaces.ImageData;
+
+namespace PixiEditor.ChangeableDocument.Changes.Properties;
+
+internal class ChangeProcessingColorSpace_Change : Change
+{
+    private ColorSpace toColorSpace;
+    private ColorSpace original;
+    
+    [GenerateMakeChangeAction]
+    public ChangeProcessingColorSpace_Change(ColorSpace newColorSpace)
+    {
+        this.toColorSpace = newColorSpace;
+    }
+    
+    public override bool InitializeAndValidate(Document target)
+    {
+        original = target.ProcessingColorSpace;
+        return true;  
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
+    {
+        ignoreInUndo = false;
+        target.ProcessingColorSpace = toColorSpace;
+
+        return new None();
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
+    {
+        target.ProcessingColorSpace = original;
+        return new None();
+    }
+}

+ 5 - 6
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -34,7 +34,7 @@ public class DocumentRenderer : IPreviewRenderable
             {
             {
                 if (node is IChunkRenderable imageNode)
                 if (node is IChunkRenderable imageNode)
                 {
                 {
-                    imageNode.RenderChunk(chunkPos, resolution, frameTime);
+                    imageNode.RenderChunk(chunkPos, resolution, frameTime, Document.ProcessingColorSpace);
                 }
                 }
             }));
             }));
         }
         }
@@ -47,7 +47,7 @@ public class DocumentRenderer : IPreviewRenderable
         ChunkResolution resolution)
         ChunkResolution resolution)
     {
     {
         IsBusy = true;
         IsBusy = true;
-        RenderContext context = new(toDrawOn, frame, resolution, Document.Size);
+        RenderContext context = new(toDrawOn, frame, resolution, Document.Size, Document.ProcessingColorSpace);
         context.FullRerender = true;
         context.FullRerender = true;
         IReadOnlyNodeGraph membersOnlyGraph = ConstructMembersOnlyGraph(layersToCombine, Document.NodeGraph);
         IReadOnlyNodeGraph membersOnlyGraph = ConstructMembersOnlyGraph(layersToCombine, Document.NodeGraph);
         try
         try
@@ -75,7 +75,7 @@ public class DocumentRenderer : IPreviewRenderable
         
         
         IsBusy = true;
         IsBusy = true;
 
 
-        RenderContext context = new(renderOn, frameTime, resolution, Document.Size);
+        RenderContext context = new(renderOn, frameTime, resolution, Document.Size, Document.ProcessingColorSpace);
         context.FullRerender = true;
         context.FullRerender = true;
         
         
         node.RenderForOutput(context, renderOn, null);
         node.RenderForOutput(context, renderOn, null);
@@ -123,10 +123,9 @@ public class DocumentRenderer : IPreviewRenderable
     public RectD? GetPreviewBounds(int frame, string elementNameToRender = "") =>
     public RectD? GetPreviewBounds(int frame, string elementNameToRender = "") =>
         new(0, 0, Document.Size.X, Document.Size.Y);
         new(0, 0, Document.Size.X, Document.Size.Y);
 
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+    public bool RenderPreview(DrawingSurface renderOn, RenderContext context,
         string elementToRenderName)
         string elementToRenderName)
     {
     {
-        RenderContext context = new(renderOn, frame, resolution, Document.Size);
         Document.NodeGraph.Execute(context);
         Document.NodeGraph.Execute(context);
 
 
         return true;
         return true;
@@ -135,7 +134,7 @@ public class DocumentRenderer : IPreviewRenderable
     public void RenderDocument(DrawingSurface toRenderOn, KeyFrameTime frameTime)
     public void RenderDocument(DrawingSurface toRenderOn, KeyFrameTime frameTime)
     {
     {
         IsBusy = true;
         IsBusy = true;
-        RenderContext context = new(toRenderOn, frameTime, ChunkResolution.Full, Document.Size) { FullRerender = true };
+        RenderContext context = new(toRenderOn, frameTime, ChunkResolution.Full, Document.Size, Document.ProcessingColorSpace) { FullRerender = true };
         Document.NodeGraph.Execute(context);
         Document.NodeGraph.Execute(context);
         IsBusy = false;
         IsBusy = false;
     }
     }

+ 5 - 1
src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs

@@ -1,5 +1,6 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 using DrawingApiBlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
 using DrawingApiBlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
@@ -16,16 +17,19 @@ public class RenderContext
     
     
     public DrawingSurface RenderSurface { get; set; }
     public DrawingSurface RenderSurface { get; set; }
     public bool FullRerender { get; set; } = false;
     public bool FullRerender { get; set; } = false;
+    
+    public ColorSpace ProcessingColorSpace { get; set; } 
 
 
 
 
     public RenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution,
     public RenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution,
-        VecI docSize, double opacity = 1) 
+        VecI docSize, ColorSpace processingColorSpace, double opacity = 1) 
     {
     {
         RenderSurface = renderSurface;
         RenderSurface = renderSurface;
         FrameTime = frameTime;
         FrameTime = frameTime;
         ChunkResolution = chunkResolution;
         ChunkResolution = chunkResolution;
         DocumentSize = docSize;
         DocumentSize = docSize;
         Opacity = opacity;
         Opacity = opacity;
+        ProcessingColorSpace = processingColorSpace;
     }
     }
 
 
     public static DrawingApiBlendMode GetDrawingBlendMode(BlendMode blendMode)
     public static DrawingApiBlendMode GetDrawingBlendMode(BlendMode blendMode)

+ 7 - 0
src/PixiEditor/Helpers/DocumentViewModelBuilder.cs

@@ -31,6 +31,7 @@ internal class DocumentViewModelBuilder
 
 
     public NodeGraphBuilder Graph { get; set; }
     public NodeGraphBuilder Graph { get; set; }
     public string ImageEncoderUsed { get; set; } = "QOI";
     public string ImageEncoderUsed { get; set; } = "QOI";
+    public bool UsesLegacyColorBlending { get; set; } = false;
 
 
     public DocumentViewModelBuilder WithSize(int width, int height)
     public DocumentViewModelBuilder WithSize(int width, int height)
     {
     {
@@ -121,6 +122,12 @@ internal class DocumentViewModelBuilder
         ImageEncoderUsed = encoder;
         ImageEncoderUsed = encoder;
         return this;
         return this;
     }
     }
+    
+    public DocumentViewModelBuilder WithLegacyColorBlending(bool usesLegacyColorBlending)
+    {
+        UsesLegacyColorBlending = usesLegacyColorBlending;
+        return this;
+    }
 
 
     private static void BuildKeyFrames(List<KeyFrameGroup> root, List<KeyFrameBuilder> data)
     private static void BuildKeyFrames(List<KeyFrameGroup> root, List<KeyFrameBuilder> data)
     {
     {

+ 1 - 0
src/PixiEditor/Helpers/Extensions/PixiParserDocumentEx.cs

@@ -30,6 +30,7 @@ internal static class PixiParserDocumentEx
 
 
         return DocumentViewModel.Build(b => b
         return DocumentViewModel.Build(b => b
             .WithSerializerData(document.SerializerName, document.SerializerVersion)
             .WithSerializerData(document.SerializerName, document.SerializerVersion)
+            .WithLegacyColorBlending(document.LegacyColorBlending)
             .WithSize(document.Width, document.Height)
             .WithSize(document.Width, document.Height)
             .WithImageEncoder(document.ImageEncoderUsed)
             .WithImageEncoder(document.ImageEncoderUsed)
             .WithPalette(document.Palette, color => new PaletteColor(color.R, color.G, color.B))
             .WithPalette(document.Palette, color => new PaletteColor(color.R, color.G, color.B))

+ 3 - 2
src/PixiEditor/Models/Rendering/AnimationPreviewRenderer.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 
 namespace PixiEditor.Models.Rendering;
 namespace PixiEditor.Models.Rendering;
 
 
@@ -21,7 +22,7 @@ internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals)
         return null;
         return null;
     }
     }
 
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
     {
     {
         if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(
         if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(
                 Guid.Parse(elementToRenderName),
                 Guid.Parse(elementToRenderName),
@@ -32,7 +33,7 @@ internal class AnimationKeyFramePreviewRenderer(DocumentInternalParts internals)
             
             
             if (node is IPreviewRenderable previewRenderable)
             if (node is IPreviewRenderable previewRenderable)
             {
             {
-                return previewRenderable.RenderPreview(renderOn, resolution, frame, elementToRenderName);
+                return previewRenderable.RenderPreview(renderOn, context, elementToRenderName);
             }
             }
         }
         }
         
         

+ 54 - 40
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -9,6 +9,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DocumentModels;
 using PixiEditor.Models.DocumentModels;
@@ -22,7 +23,7 @@ internal class MemberPreviewUpdater
 {
 {
     private readonly IDocument doc;
     private readonly IDocument doc;
     private readonly DocumentInternalParts internals;
     private readonly DocumentInternalParts internals;
-    
+
     private AnimationKeyFramePreviewRenderer AnimationKeyFramePreviewRenderer { get; }
     private AnimationKeyFramePreviewRenderer AnimationKeyFramePreviewRenderer { get; }
 
 
     public MemberPreviewUpdater(IDocument doc, DocumentInternalParts internals)
     public MemberPreviewUpdater(IDocument doc, DocumentInternalParts internals)
@@ -69,15 +70,9 @@ internal class MemberPreviewUpdater
         var previewSize = StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size);
         var previewSize = StructureHelpers.CalculatePreviewSize(internals.Tracker.Document.Size);
         float scaling = (float)previewSize.X / doc.SizeBindable.X;
         float scaling = (float)previewSize.X / doc.SizeBindable.X;
 
 
-        if (doc.PreviewPainter == null)
-        {
-            doc.PreviewPainter = new PreviewPainter(doc.Renderer);
-            doc.PreviewPainter.Repaint();
-        }
-        else
-        {
-            doc.PreviewPainter.Repaint();
-        }
+        doc.PreviewPainter = new PreviewPainter(doc.Renderer, doc.AnimationHandler.ActiveFrameTime,
+            doc.SizeBindable, internals.Tracker.Document.ProcessingColorSpace);
+        doc.PreviewPainter.Repaint();
     }
     }
 
 
     private void RenderLayersPreview(Guid[] memberGuids)
     private void RenderLayersPreview(Guid[] memberGuids)
@@ -96,11 +91,18 @@ internal class MemberPreviewUpdater
                         continue;
                         continue;
 
 
                     structureMemberHandler.PreviewPainter =
                     structureMemberHandler.PreviewPainter =
-                        new PreviewPainter(previewRenderable);
+                        new PreviewPainter(previewRenderable,
+                            doc.AnimationHandler.ActiveFrameTime, doc.SizeBindable,
+                            internals.Tracker.Document.ProcessingColorSpace);
                     structureMemberHandler.PreviewPainter.Repaint();
                     structureMemberHandler.PreviewPainter.Repaint();
                 }
                 }
                 else
                 else
                 {
                 {
+                    structureMemberHandler.PreviewPainter.FrameTime = doc.AnimationHandler.ActiveFrameTime;
+                    structureMemberHandler.PreviewPainter.DocumentSize = doc.SizeBindable;
+                    structureMemberHandler.PreviewPainter.ProcessingColorSpace =
+                        internals.Tracker.Document.ProcessingColorSpace;
+                    
                     structureMemberHandler.PreviewPainter.Repaint();
                     structureMemberHandler.PreviewPainter.Repaint();
                 }
                 }
             }
             }
@@ -131,32 +133,40 @@ internal class MemberPreviewUpdater
             }
             }
         }
         }
     }
     }
-    
-    private bool IsInFrame(ICelHandler iCel)
+
+    private bool IsInFrame(ICelHandler cel)
     {
     {
-        return iCel.StartFrameBindable <= doc.AnimationHandler.ActiveFrameBindable &&
-               iCel.StartFrameBindable + iCel.DurationBindable >= doc.AnimationHandler.ActiveFrameBindable;
+        return cel.StartFrameBindable <= doc.AnimationHandler.ActiveFrameBindable &&
+               cel.StartFrameBindable + cel.DurationBindable >= doc.AnimationHandler.ActiveFrameBindable;
     }
     }
 
 
-    private void RenderFramePreview(ICelHandler iCel)
+    private void RenderFramePreview(ICelHandler cel)
     {
     {
-        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(iCel.Id, out KeyFrame _))
+        if (internals.Tracker.Document.AnimationData.TryFindKeyFrame(cel.Id, out KeyFrame _))
         {
         {
-            iCel.PreviewPainter ??= new PreviewPainter(AnimationKeyFramePreviewRenderer, iCel.Id.ToString());
-            iCel.PreviewPainter.Repaint();
+            KeyFrameTime frameTime = doc.AnimationHandler.ActiveFrameTime;
+            cel.PreviewPainter = new PreviewPainter(AnimationKeyFramePreviewRenderer, frameTime, doc.SizeBindable,
+                internals.Tracker.Document.ProcessingColorSpace, cel.Id.ToString());
+            cel.PreviewPainter.Repaint();
         }
         }
     }
     }
-    
+
     private void RenderGroupPreview(ICelGroupHandler groupHandler)
     private void RenderGroupPreview(ICelGroupHandler groupHandler)
     {
     {
         var group = internals.Tracker.Document.AnimationData.KeyFrames.FirstOrDefault(x => x.Id == groupHandler.Id);
         var group = internals.Tracker.Document.AnimationData.KeyFrames.FirstOrDefault(x => x.Id == groupHandler.Id);
         if (group != null)
         if (group != null)
         {
         {
-            groupHandler.PreviewPainter ??= new PreviewPainter(AnimationKeyFramePreviewRenderer, groupHandler.Id.ToString());
+            KeyFrameTime frameTime = doc.AnimationHandler.ActiveFrameTime;
+            ColorSpace processingColorSpace = internals.Tracker.Document.ProcessingColorSpace;
+            VecI documentSize = doc.SizeBindable;
+
+            groupHandler.PreviewPainter =
+                new PreviewPainter(AnimationKeyFramePreviewRenderer, frameTime, documentSize, processingColorSpace,
+                    groupHandler.Id.ToString());
             groupHandler.PreviewPainter.Repaint();
             groupHandler.PreviewPainter.Repaint();
         }
         }
     }
     }
-    
+
     private void RenderMaskPreviews(Guid[] members)
     private void RenderMaskPreviews(Guid[] members)
     {
     {
         foreach (var node in doc.NodeGraphHandler.AllNodes)
         foreach (var node in doc.NodeGraphHandler.AllNodes)
@@ -170,17 +180,13 @@ internal class MemberPreviewUpdater
                 if (member is not IPreviewRenderable previewRenderable)
                 if (member is not IPreviewRenderable previewRenderable)
                     continue;
                     continue;
 
 
-                if (structureMemberHandler.MaskPreviewPainter == null)
-                {
-                    structureMemberHandler.MaskPreviewPainter = new PreviewPainter(
-                        previewRenderable,
-                        nameof(StructureNode.EmbeddedMask));
-                    structureMemberHandler.MaskPreviewPainter.Repaint();
-                }
-                else
-                {
-                    structureMemberHandler.MaskPreviewPainter.Repaint();
-                }
+                structureMemberHandler.MaskPreviewPainter = new PreviewPainter(
+                    previewRenderable,
+                    doc.AnimationHandler.ActiveFrameTime,
+                    doc.SizeBindable,
+                    internals.Tracker.Document.ProcessingColorSpace,
+                    nameof(StructureNode.EmbeddedMask));
+                structureMemberHandler.MaskPreviewPainter.Repaint();
             }
             }
         }
         }
     }
     }
@@ -211,14 +217,22 @@ internal class MemberPreviewUpdater
                 continue;
                 continue;
             }
             }
 
 
-            if (nodeVm.ResultPainter == null && node is IPreviewRenderable renderable)
+            if (node is IPreviewRenderable renderable)
             {
             {
-                nodeVm.ResultPainter = new PreviewPainter(renderable);
-                nodeVm.ResultPainter.Repaint();
-            }
-            else
-            {
-                nodeVm.ResultPainter?.Repaint();
+                if (nodeVm.ResultPainter == null)
+                {
+                    nodeVm.ResultPainter = new PreviewPainter(renderable, doc.AnimationHandler.ActiveFrameTime,
+                        doc.SizeBindable, internals.Tracker.Document.ProcessingColorSpace);
+                    nodeVm.ResultPainter.Repaint();
+                }
+                else
+                {
+                    nodeVm.ResultPainter.FrameTime = doc.AnimationHandler.ActiveFrameTime;
+                    nodeVm.ResultPainter.DocumentSize = doc.SizeBindable;
+                    nodeVm.ResultPainter.ProcessingColorSpace = internals.Tracker.Document.ProcessingColorSpace;
+
+                    nodeVm.ResultPainter?.Repaint();
+                }
             }
             }
         }
         }
     }
     }

+ 13 - 3
src/PixiEditor/Models/Rendering/PreviewPainter.cs

@@ -2,7 +2,9 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Numerics;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 
 namespace PixiEditor.Models.Rendering;
 namespace PixiEditor.Models.Rendering;
 
 
@@ -10,22 +12,30 @@ public class PreviewPainter
 {
 {
     public string ElementToRenderName { get; set; }
     public string ElementToRenderName { get; set; }
     public IPreviewRenderable PreviewRenderable { get; set; }
     public IPreviewRenderable PreviewRenderable { get; set; }
+    public ColorSpace ProcessingColorSpace { get; set; }
     public event Action RequestRepaint;
     public event Action RequestRepaint;
+    public KeyFrameTime FrameTime { get; set; }
+    public VecI DocumentSize { get; set; }
     
     
-    public PreviewPainter(IPreviewRenderable previewRenderable, string elementToRenderName = "")
+    public PreviewPainter(IPreviewRenderable previewRenderable, KeyFrameTime frameTime, VecI documentSize, ColorSpace processingColorSpace, string elementToRenderName = "")
     {
     {
         PreviewRenderable = previewRenderable;
         PreviewRenderable = previewRenderable;
         ElementToRenderName = elementToRenderName;
         ElementToRenderName = elementToRenderName;
+        ProcessingColorSpace = processingColorSpace;
+        FrameTime = frameTime;
+        DocumentSize = documentSize;
     }
     }
 
 
-    public void Paint(DrawingSurface renderOn, ChunkResolution resolution, KeyFrameTime frame) 
+    public void Paint(DrawingSurface renderOn) 
     {
     {
         if (PreviewRenderable == null)
         if (PreviewRenderable == null)
         {
         {
             return;
             return;
         }
         }
 
 
-        PreviewRenderable.RenderPreview(renderOn, resolution, frame.Frame, ElementToRenderName);
+        RenderContext context = new(renderOn, FrameTime, ChunkResolution.Full, DocumentSize, ProcessingColorSpace);
+
+        PreviewRenderable.RenderPreview(renderOn, context, ElementToRenderName);
     }
     }
 
 
     public void Repaint()
     public void Repaint()

+ 5 - 4
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -3,6 +3,7 @@ using Drawie.Backend.Core;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers;
@@ -35,12 +36,12 @@ internal class SceneRenderer
         
         
         if (!HighResRendering || !HighDpiRenderNodePresent(Document.NodeGraph))
         if (!HighResRendering || !HighDpiRenderNodePresent(Document.NodeGraph))
         {
         {
-            texture = Texture.ForProcessing(Document.Size);
+            texture = Texture.ForProcessing(Document.Size, Document.ProcessingColorSpace);
             renderTarget = texture.DrawingSurface;
             renderTarget = texture.DrawingSurface;
         }
         }
 
 
         RenderContext context = new(renderTarget, DocumentViewModel.AnimationHandler.ActiveFrameTime,
         RenderContext context = new(renderTarget, DocumentViewModel.AnimationHandler.ActiveFrameTime,
-            resolution, Document.Size);
+            resolution, Document.Size, Document.ProcessingColorSpace);
         Document.NodeGraph.Execute(context);
         Document.NodeGraph.Execute(context);
         
         
         if(texture != null)
         if(texture != null)
@@ -86,7 +87,7 @@ internal class SceneRenderer
 
 
             double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
             double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
 
 
-            RenderContext onionContext = new(target, frame, resolution, Document.Size, finalOpacity);
+            RenderContext onionContext = new(target, frame, resolution, Document.Size, Document.ProcessingColorSpace, finalOpacity);
             Document.NodeGraph.Execute(onionContext);
             Document.NodeGraph.Execute(onionContext);
         }
         }
 
 
@@ -100,7 +101,7 @@ internal class SceneRenderer
             }
             }
 
 
             double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
             double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
-            RenderContext onionContext = new(target, frame, resolution, Document.Size, finalOpacity);
+            RenderContext onionContext = new(target, frame, resolution, Document.Size, Document.ProcessingColorSpace, finalOpacity);
             Document.NodeGraph.Execute(onionContext);
             Document.NodeGraph.Execute(onionContext);
         }
         }
     }
     }

+ 1 - 0
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -67,6 +67,7 @@ internal partial class DocumentViewModel
         {
         {
             SerializerName = "PixiEditor",
             SerializerName = "PixiEditor",
             SerializerVersion = VersionHelpers.GetCurrentAssemblyVersion().ToString(),
             SerializerVersion = VersionHelpers.GetCurrentAssemblyVersion().ToString(),
+            LegacyColorBlending = doc.ProcessingColorSpace.IsSrgb,
             Width = Width,
             Width = Width,
             Height = Height,
             Height = Height,
             Swatches = ToCollection(Swatches),
             Swatches = ToCollection(Swatches),

+ 26 - 74
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -292,8 +292,14 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         var viewModel = new DocumentViewModel();
         var viewModel = new DocumentViewModel();
         viewModel.Operations.ResizeCanvas(new VecI(builderInstance.Width, builderInstance.Height), ResizeAnchor.Center);
         viewModel.Operations.ResizeCanvas(new VecI(builderInstance.Width, builderInstance.Height), ResizeAnchor.Center);
 
 
+
         var acc = viewModel.Internals.ActionAccumulator;
         var acc = viewModel.Internals.ActionAccumulator;
 
 
+        if (builderInstance.UsesLegacyColorBlending || IsFileWithOldColorBlending(serializerData))
+        {
+            acc.AddFinishedActions(new ChangeProcessingColorSpace_Action(ColorSpace.CreateSrgb()));
+        }
+
         viewModel.Internals.ChangeController.SymmetryDraggedInlet(
         viewModel.Internals.ChangeController.SymmetryDraggedInlet(
             new SymmetryAxisDragInfo(SymmetryAxisDirection.Horizontal, builderInstance.Height / 2));
             new SymmetryAxisDragInfo(SymmetryAxisDirection.Horizontal, builderInstance.Height / 2));
         viewModel.Internals.ChangeController.SymmetryDraggedInlet(
         viewModel.Internals.ChangeController.SymmetryDraggedInlet(
@@ -417,80 +423,6 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
             }
             }
         }
         }
 
 
-        /*void AddMember(Guid parentGuid, DocumentViewModelBuilder.StructureMemberBuilder member)
-        {
-            acc.AddActions(
-                new CreateStructureMember_Action(parentGuid, member.Id,
-                    member is DocumentViewModelBuilder.LayerBuilder
-                        ? StructureMemberType.Layer
-                        : StructureMemberType.Folder),
-                new StructureMemberName_Action(member.Id, member.Name)
-            );
-
-            if (!member.IsVisible)
-                acc.AddActions(new StructureMemberIsVisible_Action(member.IsVisible, member.Id));
-
-            acc.AddActions(new StructureMemberBlendMode_Action(member.BlendMode, member.Id));
-
-            acc.AddActions(new StructureMemberClipToMemberBelow_Action(member.ClipToMemberBelow, member.Id));
-
-            if (member is DocumentViewModelBuilder.LayerBuilder layerBuilder)
-            {
-                acc.AddActions(new LayerLockTransparency_Action(layerBuilder.Id, layerBuilder.LockAlpha));
-            }
-
-            if (member is DocumentViewModelBuilder.LayerBuilder layer && layer.Surface is not null)
-            {
-                PasteImage(member.Id, layer.Surface, layer.Width, layer.Height, layer.OffsetX, layer.OffsetY,
-                    false, 0);
-            }
-
-            acc.AddActions(
-                new StructureMemberOpacity_Action(member.Id, member.Opacity),
-                new EndStructureMemberOpacity_Action());
-
-            if (member.HasMask)
-            {
-                var maskSurface = member.Mask.Surface.Surface;
-
-                acc.AddActions(new CreateStructureMemberMask_Action(member.Id));
-
-                if (!member.Mask.IsVisible)
-                    acc.AddActions(new StructureMemberMaskIsVisible_Action(member.Mask.IsVisible, member.Id));
-
-                PasteImage(member.Id, member.Mask.Surface, maskSurface.Size.X, maskSurface.Size.Y, 0, 0, true, 0);
-            }
-
-            acc.AddFinishedActions();
-
-            if (member is DocumentViewModelBuilder.FolderBuilder { Children: not null } folder)
-            {
-                AddMembers(member.Id, folder.Children);
-            }
-        }*/
-
-        /*void PasteImage(Guid guid, DocumentViewModelBuilder.SurfaceBuilder surface, int width, int height, int offsetX,
-            int offsetY, bool onMask, int frame, Guid? keyFrameGuid = default)
-        {
-            acc.AddActions(
-                new PasteImage_Action(surface.Surface, new(new RectD(new VecD(offsetX, offsetY), new(width, height))),
-                    guid, true, onMask, frame, keyFrameGuid ?? default),
-                new EndPasteImage_Action());
-        }*/
-
-        /*void AddMembers(Guid parentGuid, IEnumerable<DocumentViewModelBuilder.StructureMemberBuilder> builders)
-        {
-            foreach (var child in builders.Reverse())
-            {
-                if (child.Id == default)
-                {
-                    child.Id = Guid.NewGuid();
-                }
-
-                AddMember(parentGuid, child);
-            }
-        }*/
-
         void AddAnimationData(AnimationDataBuilder? data, Dictionary<int, Guid> mappedIds,
         void AddAnimationData(AnimationDataBuilder? data, Dictionary<int, Guid> mappedIds,
             Dictionary<int, Guid> mappedKeyFrameIds)
             Dictionary<int, Guid> mappedKeyFrameIds)
         {
         {
@@ -516,6 +448,26 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                 }
                 }
             }
             }
         }
         }
+        
+        bool IsFileWithOldColorBlending((string serializerName, string serializerVersion) serializerData)
+        {
+            if(string.IsNullOrEmpty(serializerData.serializerName) && string.IsNullOrEmpty(serializerData.serializerVersion))
+            {
+                return true;
+            }
+
+            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;
+            }
+        }
     }
     }
 
 
     public void MarkAsSaved()
     public void MarkAsSaved()

+ 1 - 2
src/PixiEditor/Views/Visuals/PreviewPainterControl.cs

@@ -79,8 +79,7 @@ public class PreviewPainterControl : DrawieControl
             UniformScale(x, y, surface, previewBounds.Value);
             UniformScale(x, y, surface, previewBounds.Value);
         }
         }
 
 
-        // TODO: Implement ChunkResolution and frame
-        PreviewPainter.Paint(surface, ChunkResolution.Full, FrameToRender);
+        PreviewPainter.Paint(surface);
 
 
         surface.Canvas.Restore();
         surface.Canvas.Restore();
     }
     }

+ 1 - 1
src/PixiParser

@@ -1 +1 @@
-Subproject commit 64b02fab35d2f7fe30a8426b49ff59e1e37ed452
+Subproject commit 4176e1eb211e5e6f8932671e82b2813c737a1a56