Browse Source

Implement GetCommittedPixel and GetMostUpToDatePixel in ChunkyImage.cs

Equbuxu 3 years ago
parent
commit
f67bc43b08

+ 65 - 0
src/ChunkyImageLib/ChunkyImage.cs

@@ -122,6 +122,71 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
         }
     }
 
+    public SKColor GetCommittedPixel(VecI posOnImage)
+    {
+        lock (lockObject)
+        {
+            var chunkPos = OperationHelper.GetChunkPos(posOnImage, FullChunkSize);
+            var posInChunk = posOnImage - chunkPos * FullChunkSize;
+            return MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) switch
+            {
+                null => SKColors.Transparent,
+                var chunk => chunk.Surface.GetSRGBPixel(posInChunk)
+            };
+        }
+    }
+    
+    public SKColor GetMostUpToDatePixel(VecI posOnImage)
+    {
+        lock (lockObject)
+        {
+            var chunkPos = OperationHelper.GetChunkPos(posOnImage, FullChunkSize);
+            var posInChunk = posOnImage - chunkPos * FullChunkSize;
+
+            // nothing queued, return committed
+            if (queuedOperations.Count == 0)
+            {
+                Chunk? committedChunk = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
+                return committedChunk switch
+                {
+                    null => SKColors.Transparent,
+                    _ => committedChunk.Surface.GetSRGBPixel(posInChunk)
+                };
+            }
+
+            // something is queued, blend mode is Src so no merging needed
+            if (blendMode == SKBlendMode.Src)
+            {
+                Chunk? latestChunk = GetLatestChunk(chunkPos, ChunkResolution.Full);
+                return latestChunk switch
+                {
+                    null => SKColors.Transparent,
+                    _ => latestChunk.Surface.GetSRGBPixel(posInChunk)
+                };
+            }
+
+            // something is queued, blend mode is not Src so we have to do merging
+            {
+                Chunk? committedChunk = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
+                Chunk? latestChunk = GetLatestChunk(chunkPos, ChunkResolution.Full);
+                SKColor committedColor = committedChunk is null ? 
+                    SKColors.Transparent :  
+                    committedChunk.Surface.GetSRGBPixel(posInChunk);
+                SKColor latestColor = latestChunk is null ?
+                    SKColors.Transparent : 
+                    latestChunk.Surface.GetSRGBPixel(posInChunk);
+                // using a whole chunk just to draw 1 pixel is kinda dumb,
+                // but this should be faster than any approach that requires allocations
+                using Chunk tempChunk = Chunk.Create(ChunkResolution.Eighth);
+                using SKPaint committedPaint = new SKPaint() { Color = committedColor, BlendMode = SKBlendMode.Src };
+                using SKPaint latestPaint = new SKPaint() { Color = latestColor, BlendMode = this.blendMode };
+                tempChunk.Surface.SkiaSurface.Canvas.DrawPoint(VecI.Zero, committedPaint);
+                tempChunk.Surface.SkiaSurface.Canvas.DrawPoint(VecI.Zero, latestPaint);
+                return tempChunk.Surface.GetSRGBPixel(VecI.Zero);
+            }
+        }
+    }
+    
     /// <returns>
     /// True if the chunk existed and was drawn, otherwise false
     /// </returns>

+ 5 - 0
src/ChunkyImageLib/DataHolders/VecD.cs

@@ -21,6 +21,11 @@ public struct VecD : IEquatable<VecD>
         X = x;
         Y = y;
     }
+    public VecD(double bothAxesValue)
+    {
+        X = bothAxesValue;
+        Y = bothAxesValue;
+    }
     public static VecD FromAngleAndLength(double angle, double length)
     {
         return new VecD(Math.Cos(angle) * length, Math.Sin(angle) * length);

+ 5 - 1
src/ChunkyImageLib/DataHolders/VecI.cs

@@ -20,7 +20,11 @@ public struct VecI : IEquatable<VecI>
         X = x;
         Y = y;
     }
-
+    public VecI(int bothAxesValue)
+    {
+        X = bothAxesValue;
+        Y = bothAxesValue;
+    }
     public VecI Signs()
     {
         return new VecI(X >= 0 ? 1 : -1, Y >= 0 ? 1 : -1);

+ 47 - 1
src/ChunkyImageLibTest/ChunkyImageTests.cs

@@ -7,7 +7,7 @@ namespace ChunkyImageLibTest;
 public class ChunkyImageTests
 {
     [Fact]
-    public void ChunkyImage_Dispose_ReturnsAllChunks()
+    public void Dispose_ComplexImage_ReturnsAllChunks()
     {
         ChunkyImage image = new ChunkyImage(new(ChunkyImage.FullChunkSize, ChunkyImage.FullChunkSize));
         image.EnqueueDrawRectangle(new(new(5, 5), new(80, 80), 0, 2, SKColors.AliceBlue, SKColors.Snow));
@@ -32,4 +32,50 @@ public class ChunkyImageTests
 
         Assert.Equal(0, Chunk.ChunkCounter);
     }
+
+    [Fact]
+    public void GetCommittedPixel_RedImage_ReturnsRedPixel()
+    {
+        const int chunkSize = ChunkyImage.FullChunkSize;
+        ChunkyImage image = new ChunkyImage(new VecI(chunkSize * 2));
+        image.EnqueueDrawRectangle
+            (new ShapeData(new VecD(chunkSize), new VecD(chunkSize * 2), 0, 0, SKColors.Transparent, SKColors.Red));
+        image.CommitChanges();
+        Assert.Equal(SKColors.Red, image.GetCommittedPixel(new VecI(chunkSize + chunkSize / 2)));
+        image.Dispose();
+        Assert.Equal(0, Chunk.ChunkCounter);
+    }
+
+    [Fact]
+    public void GetMostUpToDatePixel_BlendModeSrc_ReturnsCorrectPixel()
+    {
+        const int chunkSize = ChunkyImage.FullChunkSize;
+        ChunkyImage image = new ChunkyImage(new VecI(chunkSize * 2));
+        image.EnqueueDrawRectangle
+            (new ShapeData(new VecD(chunkSize), new VecD(chunkSize * 2), 0, 0, SKColors.Transparent, SKColors.Red));
+        Assert.Equal(SKColors.Red, image.GetMostUpToDatePixel(new VecI(chunkSize + chunkSize / 2)));
+        image.Dispose();
+        Assert.Equal(0, Chunk.ChunkCounter);
+    }
+    
+    [Fact]
+    public void GetMostUpToDatePixel_BlendModeSrcOver_ReturnsCorrectPixel()
+    {
+        const int chunkSize = ChunkyImage.FullChunkSize;
+        ChunkyImage image = new ChunkyImage(new VecI(chunkSize * 2));
+        image.EnqueueDrawRectangle
+            (new ShapeData(new VecD(chunkSize), new VecD(chunkSize * 2), 0, 0, SKColors.Transparent, SKColors.Red));
+        image.CommitChanges();
+        image.SetBlendMode(SKBlendMode.SrcOver);
+        image.EnqueueDrawRectangle(new ShapeData(
+            new VecD(chunkSize),
+            new VecD(chunkSize * 2),
+            0, 
+            0,
+            SKColors.Transparent,
+            new SKColor(0, 255, 0, 128)));
+        Assert.Equal(new SKColor(127, 128, 0), image.GetMostUpToDatePixel(new VecI(chunkSize + chunkSize / 2)));
+        image.Dispose();
+        Assert.Equal(0, Chunk.ChunkCounter);
+    }
 }