Browse Source

forgot color space in chunky image

flabbet 8 months ago
parent
commit
6ea50f9fb0

+ 5 - 3
src/ChunkyImageLib/ChunkyImage.cs

@@ -105,7 +105,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     private readonly Dictionary<ChunkResolution, Dictionary<VecI, Chunk>> latestChunks;
     private readonly Dictionary<ChunkResolution, Dictionary<VecI, LatestChunkData>> latestChunksData;
 
-    public ChunkyImage(VecI size)
+    public ChunkyImage(VecI size, ColorSpace colorSpace)
     {
         CommittedSize = size;
         LatestSize = size;
@@ -130,9 +130,11 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             [ChunkResolution.Quarter] = new(),
             [ChunkResolution.Eighth] = new(),
         };
+        
+        ProcessingColorSpace = colorSpace;
     }
 
-    public ChunkyImage(Surface image) : this(image.Size)
+    public ChunkyImage(Surface image, ColorSpace colorSpace) : this(image.Size, colorSpace)
     {
         EnqueueDrawImage(VecI.Zero, image);
         CommitChanges();
@@ -252,7 +254,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         lock (lockObject)
         {
             ThrowIfDisposed();
-            ChunkyImage output = new(LatestSize);
+            ChunkyImage output = new(LatestSize, ProcessingColorSpace);
             var chunks = FindCommittedChunks();
             foreach (var chunk in chunks)
             {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Factories/ImageLayerNodeFactory.cs

@@ -7,6 +7,6 @@ public class ImageLayerNodeFactory : NodeFactory<ImageLayerNode>
 {
     public override ImageLayerNode CreateNode(IReadOnlyDocument document)
     {
-        return new ImageLayerNode(document.Size);
+        return new ImageLayerNode(document.Size, document.ProcessingColorSpace);
     }
 }

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

@@ -24,19 +24,21 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     public bool LockTransparency { get; set; }
 
     private VecI startSize;
+    private ColorSpace colorSpace;
     private ChunkyImage layerImage => keyFrames[0]?.Data as ChunkyImage;
 
     private Texture fullResrenderedSurface;
     private int renderedSurfaceFrame = -1;
 
-    public ImageLayerNode(VecI size)
+    public ImageLayerNode(VecI size, ColorSpace colorSpace)
     {
         if (keyFrames.Count == 0)
         {
-            keyFrames.Add(new KeyFrameData(Guid.NewGuid(), 0, 0, ImageLayerKey) { Data = new ChunkyImage(size) });
+            keyFrames.Add(new KeyFrameData(Guid.NewGuid(), 0, 0, ImageLayerKey) { Data = new ChunkyImage(size, colorSpace) });
         }
 
         this.startSize = size;
+        this.colorSpace = colorSpace;
     }
 
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
@@ -213,7 +215,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     public override Node CreateCopy()
     {
-        var image = new ImageLayerNode(startSize) { MemberName = this.MemberName, };
+        var image = new ImageLayerNode(startSize, colorSpace) { MemberName = this.MemberName, };
 
         image.keyFrames.Clear();
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Animation/CreateRasterKeyFrame_Change.cs

@@ -39,7 +39,7 @@ internal class CreateRasterKeyFrame_Change : Change
 
         ImageLayerNode targetNode = target.FindMemberOrThrow<ImageLayerNode>(_targetLayerGuid);
 
-        ChunkyImage img = cloneFromImage?.CloneFromCommitted() ?? new ChunkyImage(target.Size);
+        ChunkyImage img = cloneFromImage?.CloneFromCommitted() ?? new ChunkyImage(target.Size, target.ProcessingColorSpace);
 
         var keyFrame =
             new RasterKeyFrame(createdKeyFrameId, targetNode.Id, _frame, target);

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/ApplyLayerMask_Change.cs

@@ -37,7 +37,7 @@ internal class ApplyLayerMask_Change : Change
             throw new InvalidOperationException("Cannot apply layer mask, no mask");
 
         var layerImage = layer.GetLayerImageAtFrame(frame);
-        ChunkyImage newLayerImage = new ChunkyImage(target.Size);
+        ChunkyImage newLayerImage = new ChunkyImage(target.Size, target.ProcessingColorSpace);
         newLayerImage.AddRasterClip(layer.EmbeddedMask);
         newLayerImage.EnqueueDrawCommitedChunkyImage(VecI.Zero, layerImage);
         newLayerImage.CommitChanges();
@@ -68,7 +68,7 @@ internal class ApplyLayerMask_Change : Change
         if (savedLayer is null || savedMask is null)
             throw new InvalidOperationException("Cannot restore layer mask, no saved data");
 
-        ChunkyImage newMask = new ChunkyImage(target.Size);
+        ChunkyImage newMask = new ChunkyImage(target.Size, target.ProcessingColorSpace);
         savedMask.ApplyChunksToImage(newMask);
         var affectedChunksMask = newMask.FindAffectedArea();
         newMask.CommitChanges();

+ 14 - 5
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -42,6 +42,10 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         this.spacing = spacing;
         points.Add(pos);
         this.frame = frame;
+        
+        srcPaint.Shader?.Dispose();
+        srcPaint.Shader = null;
+        
         if (this.antiAliasing && !erasing)
         {
             srcPaint.BlendMode = BlendMode.SrcOver;
@@ -82,7 +86,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         int opCount = image.QueueLength;
 
         var bresenham = BresenhamLineHelper.GetBresenhamLine(from, to);
-        
+
         float spacingPixels = strokeWidth * spacing;
 
         foreach (var point in bresenham)
@@ -96,7 +100,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             {
                 ApplySoftnessGradient((VecD)point);
             }
-            
+
             image.EnqueueDrawEllipse((RectD)rect, color, color, 0, 0, antiAliasing, srcPaint);
         }
 
@@ -110,12 +114,17 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         if (points.Count == 1)
         {
             var rect = new RectI(points[0] - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
-            targetImage.EnqueueDrawEllipse((RectD)rect, color, color, 1, 0, antiAliasing, srcPaint);
+            if (antiAliasing)
+            {
+                ApplySoftnessGradient(points[0]);
+            }
+
+            targetImage.EnqueueDrawEllipse((RectD)rect, color, color, 0, 0, antiAliasing, srcPaint);
             return;
         }
 
         VecF lastPos = points[0];
-        
+
         float spacingInPixels = strokeWidth * this.spacing;
 
         for (int i = 0; i < points.Count; i++)
@@ -142,7 +151,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         radius = MathF.Max(1, radius);
         srcPaint.Shader = Shader.CreateRadialGradient(
             pos, radius, [color, color.WithAlpha(0)],
-            [hardness - 0.04f, 1f], ShaderTileMode.Clamp);
+            [Math.Max(hardness - 0.04f, 0), 1f], ShaderTileMode.Clamp);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Properties/LayerStructure/CreateStructureMemberMask_Change.cs

@@ -22,7 +22,7 @@ internal class CreateStructureMemberMask_Change : Change
         var member = target.FindMemberOrThrow(targetMember);
         if (member.EmbeddedMask is not null)
             throw new InvalidOperationException("Cannot create a mask; the target member already has one");
-        member.EmbeddedMask = new ChunkyImage(target.Size);
+        member.EmbeddedMask = new ChunkyImage(target.Size, target.ProcessingColorSpace);
 
         ignoreInUndo = false;
         return new StructureMemberMask_ChangeInfo(targetMember, true);

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Structure/RasterizeMember_Change.cs

@@ -44,7 +44,7 @@ internal class RasterizeMember_Change : Change
         
         IRasterizable rasterizable = (IRasterizable)node;
         
-        ImageLayerNode imageLayer = new ImageLayerNode(target.Size);
+        ImageLayerNode imageLayer = new ImageLayerNode(target.Size, target.ProcessingColorSpace);
         imageLayer.MemberName = node.DisplayName;
 
         target.NodeGraph.AddNode(imageLayer);

+ 5 - 4
src/PixiEditor/Helpers/DocumentViewModelBuilder.cs

@@ -7,6 +7,7 @@ using PixiEditor.ViewModels.Document;
 using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using Drawie.Numerics;
 using PixiEditor.Parser;
@@ -310,7 +311,7 @@ internal class NodeGraphBuilder
         return this;
     }
 
-    public NodeGraphBuilder WithImageLayerNode(string name, Surface image, out int id)
+    public NodeGraphBuilder WithImageLayerNode(string name, Surface image, ColorSpace colorSpace, out int id)
     {
         this.WithNodeOfType(typeof(ImageLayerNode))
             .WithName(name)
@@ -320,7 +321,7 @@ internal class NodeGraphBuilder
                 new KeyFrameData
                 {
                     AffectedElement = ImageLayerNode.ImageLayerKey,
-                    Data = new ChunkyImage(image),
+                    Data = new ChunkyImage(image, colorSpace),
                     Duration = 0,
                     StartFrame = 0,
                     IsVisible = true
@@ -331,7 +332,7 @@ internal class NodeGraphBuilder
         return this;
     }
 
-    public NodeGraphBuilder WithImageLayerNode(string name, VecI size, out int id)
+    public NodeGraphBuilder WithImageLayerNode(string name, VecI size, ColorSpace colorSpace, out int id)
     {
         this.WithNodeOfType(typeof(ImageLayerNode))
             .WithName(name)
@@ -341,7 +342,7 @@ internal class NodeGraphBuilder
                 new KeyFrameData
                 {
                     AffectedElement = ImageLayerNode.ImageLayerKey,
-                    Data = new ChunkyImage(size),
+                    Data = new ChunkyImage(size, colorSpace),
                     Duration = 0,
                     StartFrame = 0,
                     IsVisible = true

+ 4 - 3
src/PixiEditor/Helpers/Extensions/PixiParserPixiV4DocumentEx.cs

@@ -3,6 +3,7 @@ using ChunkyImageLib;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using Drawie.Numerics;
@@ -100,7 +101,7 @@ internal static class PixiParserPixiV4DocumentEx
             if (folder.Mask is not null)
             {
                 inputValues["MaskIsVisible"] = folder.Mask.Enabled;
-                additionalValues["embeddedMask"] = new ChunkyImage(ConvertToNewMaskFormat(Surface.Load(folder.Mask.ImageBytes), document.Width, document.Height));
+                additionalValues["embeddedMask"] = new ChunkyImage(ConvertToNewMaskFormat(Surface.Load(folder.Mask.ImageBytes), document.Width, document.Height), ColorSpace.CreateSrgb());
             }
 
             PropertyConnection contentConnection = new PropertyConnection()
@@ -141,7 +142,7 @@ internal static class PixiParserPixiV4DocumentEx
             if (layer.Mask is not null)
             {
                 inputValues["MaskIsVisible"] = layer.Mask.Enabled;
-                additionalValues["embeddedMask"] = new ChunkyImage(ConvertToNewMaskFormat(Surface.Load(layer.Mask.ImageBytes), document.Width, document.Height)); 
+                additionalValues["embeddedMask"] = new ChunkyImage(ConvertToNewMaskFormat(Surface.Load(layer.Mask.ImageBytes), document.Width, document.Height), ColorSpace.CreateSrgb()); 
             }
 
             PropertyConnection connection = new PropertyConnection()
@@ -160,7 +161,7 @@ internal static class PixiParserPixiV4DocumentEx
                     new KeyFrameData()
                     {
                         AffectedElement = ImageLayerNode.ImageLayerKey,
-                        Data = new ChunkyImage(Surface.Load(layer.ImageBytes)),
+                        Data = new ChunkyImage(Surface.Load(layer.ImageBytes), ColorSpace.CreateSrgb()),
                         Duration = 0,
                         StartFrame = 0,
                         IsVisible = true

+ 1 - 1
src/PixiEditor/Models/Serialization/Factories/ChunkyImageSerializationFactory.cs

@@ -35,7 +35,7 @@ public class ChunkyImageSerializationFactory : SerializationFactory<byte[], Chun
                 return false;
             }
 
-            original = new ChunkyImage(surface.Size);
+            original = new ChunkyImage(surface.Size, Config.ProcessingProcessingColorSpace);
             original.EnqueueDrawImage(VecI.Zero, surface);
             original.CommitChanges();
             return true;

+ 5 - 2
src/PixiEditor/Models/Serialization/SerializationConfig.cs

@@ -1,13 +1,16 @@
-using PixiEditor.Parser.Skia;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using PixiEditor.Parser.Skia;
 
 namespace PixiEditor.Models.Serialization;
 
 public class SerializationConfig
 {
     public ImageEncoder Encoder { get; set; }
+    public ColorSpace ProcessingProcessingColorSpace { get; set; } = ColorSpace.CreateSrgbLinear();
     
-    public SerializationConfig(ImageEncoder encoder)
+    public SerializationConfig(ImageEncoder encoder, ColorSpace processingColorSpace)
     {
         Encoder = encoder;
+        ProcessingProcessingColorSpace = processingColorSpace;
     }
 }

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

@@ -52,7 +52,7 @@ internal partial class DocumentViewModel
     {
         NodeGraph graph = new();
         ImageEncoder encoder = new QoiEncoder();
-        var serializationConfig = new SerializationConfig(encoder);
+        var serializationConfig = new SerializationConfig(encoder, ColorSpace.CreateSrgb());
         var doc = Internals.Tracker.Document;
 
         Dictionary<Guid, int> nodeIdMap = new();

+ 3 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -294,8 +294,10 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
         var acc = viewModel.Internals.ActionAccumulator;
 
+        ColorSpace targetProcessingColorSpace = ColorSpace.CreateSrgbLinear();
         if (builderInstance.UsesLegacyColorBlending || IsFileWithOldColorBlending(serializerData))
         {
+            targetProcessingColorSpace = ColorSpace.CreateSrgb();
             acc.AddFinishedActions(new ChangeProcessingColorSpace_Action(ColorSpace.CreateSrgb()));
         }
 
@@ -320,7 +322,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         viewModel.Palette = new ObservableRangeCollection<PaletteColor>(builderInstance.Palette);
 
         SerializationConfig config =
-            new SerializationConfig(BuiltInEncoders.Encoders[builderInstance.ImageEncoderUsed]);
+            new SerializationConfig(BuiltInEncoders.Encoders[builderInstance.ImageEncoderUsed], targetProcessingColorSpace);
 
         List<SerializationFactory> allFactories =
             ViewModelMain.Current.Services.GetServices<SerializationFactory>().ToList();

+ 10 - 3
src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs

@@ -16,6 +16,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.ImageData;
 using PixiEditor.Exceptions;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
@@ -261,7 +262,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             .WithGraph(x => x
                 .WithImageLayerNode(
                     new LocalizedString("IMAGE"),
-                    image, out int id)
+                    image, 
+                    ColorSpace.CreateSrgbLinear(),
+                    out int id)
                 .WithOutputNode(id, "Output")
             ));
 
@@ -297,7 +300,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             .WithGraph(x => x
                 .WithImageLayerNode(
                     new LocalizedString("IMAGE"),
-                    surface, out int id)
+                    surface,
+                    ColorSpace.CreateSrgbLinear(),
+                    out int id)
                 .WithOutputNode(id, "Output")
             ));
 
@@ -325,7 +330,9 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
                 .WithGraph(x => x
                     .WithImageLayerNode(
                         new LocalizedString("BASE_LAYER_NAME"),
-                        new VecI(newFile.Width, newFile.Height), out int id)
+                        new VecI(newFile.Width, newFile.Height), 
+                        ColorSpace.CreateSrgbLinear(),
+                        out int id)
                     .WithOutputNode(id, "Output")
                 ));