Browse Source

Fixed flood fill not taking color space into account

flabbet 7 months ago
parent
commit
60e7dd0e67

+ 6 - 6
src/ChunkyImageLib/ChunkyImage.cs

@@ -280,7 +280,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             return MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) switch
             return MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) switch
             {
             {
                 null => Colors.Transparent,
                 null => Colors.Transparent,
-                var chunk => chunk.Surface.GetSRGBPixel(posInChunk)
+                var chunk => chunk.Surface.GetPixel(posInChunk)
             };
             };
         }
         }
     }
     }
@@ -301,7 +301,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 return committedChunk switch
                 return committedChunk switch
                 {
                 {
                     null => Colors.Transparent,
                     null => Colors.Transparent,
-                    _ => committedChunk.Surface.GetSRGBPixel(posInChunk)
+                    _ => committedChunk.Surface.GetPixel(posInChunk)
                 };
                 };
             }
             }
 
 
@@ -312,7 +312,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 return latestChunk switch
                 return latestChunk switch
                 {
                 {
                     null => Colors.Transparent,
                     null => Colors.Transparent,
-                    _ => latestChunk.Surface.GetSRGBPixel(posInChunk)
+                    _ => latestChunk.Surface.GetPixel(posInChunk)
                 };
                 };
             }
             }
 
 
@@ -322,10 +322,10 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 Chunk? latestChunk = GetLatestChunk(chunkPos, ChunkResolution.Full);
                 Chunk? latestChunk = GetLatestChunk(chunkPos, ChunkResolution.Full);
                 Color committedColor = committedChunk is null
                 Color committedColor = committedChunk is null
                     ? Colors.Transparent
                     ? Colors.Transparent
-                    : committedChunk.Surface.GetSRGBPixel(posInChunk);
+                    : committedChunk.Surface.GetPixel(posInChunk);
                 Color latestColor = latestChunk is null
                 Color latestColor = latestChunk is null
                     ? Colors.Transparent
                     ? Colors.Transparent
-                    : latestChunk.Surface.GetSRGBPixel(posInChunk);
+                    : latestChunk.Surface.GetPixel(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(ProcessingColorSpace, ChunkResolution.Eighth);
                 using Chunk tempChunk = Chunk.Create(ProcessingColorSpace, ChunkResolution.Eighth);
@@ -333,7 +333,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
                 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);
                 tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, latestPaint);
                 tempChunk.Surface.DrawingSurface.Canvas.DrawPixel(VecI.Zero, latestPaint);
-                return tempChunk.Surface.GetSRGBPixel(VecI.Zero);
+                return tempChunk.Surface.GetPixel(VecI.Zero);
             }
             }
         }
         }
     }
     }

+ 1 - 1
src/ChunkyImageLib/Operations/PixelOperation.cs

@@ -55,7 +55,7 @@ internal class PixelOperation : IMirroredDrawOperation
         if (colorProcessor != null && getCommitedPixelFunc != null)
         if (colorProcessor != null && getCommitedPixelFunc != null)
         {
         {
             var pos = pixel - chunkPos * ChunkyImage.FullChunkSize;
             var pos = pixel - chunkPos * ChunkyImage.FullChunkSize;
-            pixelColor = colorProcessor(getCommitedPixelFunc(pixel), chunk.Surface.GetSRGBPixel(pos));
+            pixelColor = colorProcessor(getCommitedPixelFunc(pixel), chunk.Surface.GetPixel(pos));
         }
         }
 
 
         return new Color(pixelColor.R, pixelColor.G, pixelColor.B, (byte)(pixelColor.A * chunk.Resolution.Multiplier()));
         return new Color(pixelColor.R, pixelColor.G, pixelColor.B, (byte)(pixelColor.A * chunk.Resolution.Multiplier()));

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 1124f58f7ca5b4312aeb0ef8e22ceea7103a9bb2
+Subproject commit 303ed11c4cb15983a0d0ca29edc798c3898a2d52

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

@@ -21,7 +21,8 @@ public static class FloodFillHelper
     private static readonly VecI Left = new VecI(-1, 0);
     private static readonly VecI Left = new VecI(-1, 0);
     private static readonly VecI Right = new VecI(1, 0);
     private static readonly VecI Right = new VecI(1, 0);
 
 
-    internal static FloodFillChunkCache CreateCache(HashSet<Guid> membersToFloodFill, IReadOnlyDocument document, int frame)
+    internal static FloodFillChunkCache CreateCache(HashSet<Guid> membersToFloodFill, IReadOnlyDocument document,
+        int frame)
     {
     {
         if (membersToFloodFill.Count == 1)
         if (membersToFloodFill.Count == 1)
         {
         {
@@ -33,6 +34,7 @@ public static class FloodFillHelper
                 throw new InvalidOperationException("Member is not a raster layer");
                 throw new InvalidOperationException("Member is not a raster layer");
             return new FloodFillChunkCache(rasterLayer.GetLayerImageAtFrame(frame));
             return new FloodFillChunkCache(rasterLayer.GetLayerImageAtFrame(frame));
         }
         }
+
         return new FloodFillChunkCache(membersToFloodFill, document, frame);
         return new FloodFillChunkCache(membersToFloodFill, document, frame);
     }
     }
 
 
@@ -55,8 +57,9 @@ public static class FloodFillHelper
         VecI initChunkPos = OperationHelper.GetChunkPos(startingPos, chunkSize);
         VecI initChunkPos = OperationHelper.GetChunkPos(startingPos, chunkSize);
         VecI imageSizeInChunks = (VecI)(document.Size / (double)chunkSize).Ceiling();
         VecI imageSizeInChunks = (VecI)(document.Size / (double)chunkSize).Ceiling();
         VecI initPosOnChunk = startingPos - initChunkPos * chunkSize;
         VecI initPosOnChunk = startingPos - initChunkPos * chunkSize;
-        Color colorToReplace = cache.GetChunk(initChunkPos).Match(
-            (Chunk chunk) => chunk.Surface.GetSRGBPixel(initPosOnChunk),
+        var chunkAtPos = cache.GetChunk(initChunkPos);
+        Color colorToReplace = chunkAtPos.Match(
+            (Chunk chunk) => chunk.Surface.GetPixel(initPosOnChunk),
             static (EmptyChunk _) => Colors.Transparent
             static (EmptyChunk _) => Colors.Transparent
         );
         );
 
 
@@ -69,6 +72,16 @@ public static class FloodFillHelper
         // Used for faster pixel checking
         // Used for faster pixel checking
         ColorBounds colorRange = new(colorToReplace, tolerance);
         ColorBounds colorRange = new(colorToReplace, tolerance);
         ulong uLongColor = drawingColor.ToULong();
         ulong uLongColor = drawingColor.ToULong();
+        if (chunkAtPos.IsT0 && !chunkAtPos.AsT0.Surface.ImageInfo.ColorSpace.IsSrgb)
+        {
+            if (chunkAtPos.AsT0.Surface?.ImageInfo.ColorSpace != null)
+            {
+                var srgbTransform = ColorSpace.CreateSrgb().GetTransformFunction();
+
+                var fixedColor = drawingColor.TransformColor(srgbTransform);
+                uLongColor = fixedColor.ToULong();
+            }
+        }
 
 
         Dictionary<VecI, Chunk> drawingChunks = new();
         Dictionary<VecI, Chunk> drawingChunks = new();
         HashSet<VecI> processedEmptyChunks = new();
         HashSet<VecI> processedEmptyChunks = new();
@@ -88,6 +101,7 @@ public static class FloodFillHelper
                 chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
                 chunk.Surface.DrawingSurface.Canvas.Clear(Colors.Transparent);
                 drawingChunks[chunkPos] = chunk;
                 drawingChunks[chunkPos] = chunk;
             }
             }
+
             var drawingChunk = drawingChunks[chunkPos];
             var drawingChunk = drawingChunks[chunkPos];
             var referenceChunk = cache.GetChunk(chunkPos);
             var referenceChunk = cache.GetChunk(chunkPos);
 
 
@@ -108,8 +122,10 @@ public static class FloodFillHelper
                         if (chunkPos.X < imageSizeInChunks.X - 1)
                         if (chunkPos.X < imageSizeInChunks.X - 1)
                             positionsToFloodFill.Push((new(chunkPos.X + 1, chunkPos.Y), new(0, i)));
                             positionsToFloodFill.Push((new(chunkPos.X + 1, chunkPos.Y), new(0, i)));
                     }
                     }
+
                     processedEmptyChunks.Add(chunkPos);
                     processedEmptyChunks.Add(chunkPos);
                 }
                 }
+
                 continue;
                 continue;
             }
             }
 
 
@@ -142,6 +158,7 @@ public static class FloodFillHelper
                     positionsToFloodFill.Push((new(chunkPos.X + 1, chunkPos.Y), new(0, i)));
                     positionsToFloodFill.Push((new(chunkPos.X + 1, chunkPos.Y), new(0, i)));
             }
             }
         }
         }
+
         return drawingChunks;
         return drawingChunks;
     }
     }
 
 
@@ -158,9 +175,9 @@ public static class FloodFillHelper
         ColorBounds bounds,
         ColorBounds bounds,
         bool checkFirstPixel)
         bool checkFirstPixel)
     {
     {
-        if (referenceChunk.Surface.GetSRGBPixel(pos) == color || drawingChunk.Surface.GetSRGBPixel(pos) == color)
+        if (referenceChunk.Surface.GetPixel(pos) == color || drawingChunk.Surface.GetPixel(pos) == color)
             return null;
             return null;
-        if (checkFirstPixel && !bounds.IsWithinBounds(referenceChunk.Surface.GetSRGBPixel(pos)))
+        if (checkFirstPixel && !bounds.IsWithinBounds(referenceChunk.Surface.GetPixel(pos)))
             return null;
             return null;
 
 
         byte[] pixelStates = new byte[chunkSize * chunkSize];
         byte[] pixelStates = new byte[chunkSize * chunkSize];
@@ -186,13 +203,17 @@ public static class FloodFillHelper
 
 
             if (curPos.X > 0 && pixelStates[pixelOffset - 1] == InSelection && bounds.IsWithinBounds(refPixel - 4))
             if (curPos.X > 0 && pixelStates[pixelOffset - 1] == InSelection && bounds.IsWithinBounds(refPixel - 4))
                 toVisit.Push(new(curPos.X - 1, curPos.Y));
                 toVisit.Push(new(curPos.X - 1, curPos.Y));
-            if (curPos.X < chunkSize - 1 && pixelStates[pixelOffset + 1] == InSelection && bounds.IsWithinBounds(refPixel + 4))
+            if (curPos.X < chunkSize - 1 && pixelStates[pixelOffset + 1] == InSelection &&
+                bounds.IsWithinBounds(refPixel + 4))
                 toVisit.Push(new(curPos.X + 1, curPos.Y));
                 toVisit.Push(new(curPos.X + 1, curPos.Y));
-            if (curPos.Y > 0 && pixelStates[pixelOffset - chunkSize] == InSelection && bounds.IsWithinBounds(refPixel - 4 * chunkSize))
+            if (curPos.Y > 0 && pixelStates[pixelOffset - chunkSize] == InSelection &&
+                bounds.IsWithinBounds(refPixel - 4 * chunkSize))
                 toVisit.Push(new(curPos.X, curPos.Y - 1));
                 toVisit.Push(new(curPos.X, curPos.Y - 1));
-            if (curPos.Y < chunkSize - 1 && pixelStates[pixelOffset + chunkSize] == InSelection && bounds.IsWithinBounds(refPixel + 4 * chunkSize))
+            if (curPos.Y < chunkSize - 1 && pixelStates[pixelOffset + chunkSize] == InSelection &&
+                bounds.IsWithinBounds(refPixel + 4 * chunkSize))
                 toVisit.Push(new(curPos.X, curPos.Y + 1));
                 toVisit.Push(new(curPos.X, curPos.Y + 1));
         }
         }
+
         return pixelStates;
         return pixelStates;
     }
     }
 
 
@@ -215,7 +236,8 @@ public static class FloodFillHelper
     /// <summary>
     /// <summary>
     /// Use skia to set all pixels in array that are inside selection to InSelection
     /// Use skia to set all pixels in array that are inside selection to InSelection
     /// </summary>
     /// </summary>
-    private static unsafe void DrawSelection(byte[] array, VectorPath? selection, RectI globalBounds, VecI chunkPos, int chunkSize)
+    private static unsafe void DrawSelection(byte[] array, VectorPath? selection, RectI globalBounds, VecI chunkPos,
+        int chunkSize)
     {
     {
         if (selection is null)
         if (selection is null)
         {
         {
@@ -232,7 +254,8 @@ public static class FloodFillHelper
         fixed (byte* arr = array)
         fixed (byte* arr = array)
         {
         {
             using DrawingSurface drawingSurface = DrawingSurface.Create(
             using DrawingSurface drawingSurface = DrawingSurface.Create(
-                new ImageInfo(localBounds.Right, localBounds.Bottom, ColorType.Gray8, AlphaType.Opaque), (IntPtr)arr, chunkSize);
+                new ImageInfo(localBounds.Right, localBounds.Bottom, ColorType.Gray8, AlphaType.Opaque), (IntPtr)arr,
+                chunkSize);
             drawingSurface.Canvas.ClipPath(shiftedSelection);
             drawingSurface.Canvas.ClipPath(shiftedSelection);
             drawingSurface.Canvas.Clear(new Color(InSelection, InSelection, InSelection));
             drawingSurface.Canvas.Clear(new Color(InSelection, InSelection, InSelection));
             drawingSurface.Canvas.Flush();
             drawingSurface.Canvas.Flush();

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Selection/MagicWand/MagicWandHelper.cs

@@ -126,7 +126,7 @@ internal class MagicWandHelper
 
 
 
 
         Color colorToReplace = cache.GetChunk(initChunkPos).Match(
         Color colorToReplace = cache.GetChunk(initChunkPos).Match(
-            (Chunk chunk) => chunk.Surface.GetSRGBPixel(initPosOnChunk),
+            (Chunk chunk) => chunk.Surface.GetPixel(initPosOnChunk),
             static (EmptyChunk _) => Colors.Transparent
             static (EmptyChunk _) => Colors.Transparent
         );
         );
 
 
@@ -257,7 +257,7 @@ internal class MagicWandHelper
         VecI pos,
         VecI pos,
         ColorBounds bounds, Lines lines)
         ColorBounds bounds, Lines lines)
     {
     {
-        if (!bounds.IsWithinBounds(referenceChunk.Surface.GetSRGBPixel(pos)))
+        if (!bounds.IsWithinBounds(referenceChunk.Surface.GetPixel(pos)))
         {
         {
             return null;
             return null;
         }
         }