Browse Source

Shaders wip

flabbet 3 years ago
parent
commit
681c6326b2

+ 2 - 0
src/ChunkyImageLib/ChunkyImageLib.csproj

@@ -9,6 +9,8 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="ComputeSharp.Core" Version="2.0.0-alpha.26" />
+    <PackageReference Include="ComputeSharp.Dynamic" Version="2.0.0-alpha.26" />
     <PackageReference Include="OneOf" Version="3.0.216" />
     <PackageReference Include="SkiaSharp" Version="2.80.3" />
   </ItemGroup>

+ 17 - 0
src/ChunkyImageLib/DataHolders/ColorBounds.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading.Tasks;
+using ComputeSharp;
 using SkiaSharp;
 
 namespace ChunkyImageLib.DataHolders;
@@ -60,5 +61,21 @@ public struct ColorBounds
             return false;
         return true;
     }
+    
+    [ShaderMethod]
+    public readonly bool IsWithinBounds(float4 color)
+    {
+        float r = color.R;
+        float g = color.G;
+        float b = color.B;
+        float a = color.A;
+        if (r < LowerR || r > UpperR)
+            return false;
+        if (g < LowerG || g > UpperG)
+            return false;
+        if (b < LowerB || b > UpperB)
+            return false;
+        return !(a < LowerA) && !(a > UpperA);
+    }
 }
 

+ 34 - 3
src/ChunkyImageLib/Operations/ReplaceColorOperation.cs

@@ -4,6 +4,8 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.Shaders;
+using ComputeSharp;
 using SkiaSharp;
 
 namespace ChunkyImageLib.Operations;
@@ -13,6 +15,7 @@ internal class ReplaceColorOperation : IDrawOperation
     private readonly SKColor newColor;
 
     private readonly ColorBounds oldColorBounds;
+    private readonly HlslColorBounds oldColorBoundsHlsl;
     private readonly ulong newColorBits;
 
     public bool IgnoreEmptyChunks => true;
@@ -22,17 +25,32 @@ internal class ReplaceColorOperation : IDrawOperation
         this.oldColor = oldColor;
         this.newColor = newColor;
         oldColorBounds = new ColorBounds(oldColor);
+        oldColorBoundsHlsl = new HlslColorBounds(new Float4(oldColor.Red, oldColor.Green, oldColor.Blue, oldColor.Alpha));
         newColorBits = newColor.ToULong();
     }
 
     public void DrawOnChunk(Chunk chunk, VecI chunkPos)
     {
-        ReplaceColor(oldColorBounds, newColorBits, chunk);
+        ReplaceColor(oldColorBoundsHlsl, oldColor, newColor, chunk);
     }
 
-    private static unsafe void ReplaceColor(ColorBounds oldColorBounds, ulong newColorBits, Chunk chunk)
+    private static void ReplaceColor(HlslColorBounds oldColorBounds, SKColor oldColor, SKColor newColor, Chunk chunk)
     {
-        int maxThreads = Environment.ProcessorCount;
+        var span = chunk.Surface.SkiaSurface.PeekPixels().GetPixelSpan<Rgba64>();
+        using var texture = GraphicsDevice.GetDefault()
+            .AllocateReadWriteTexture2D<Rgba64, float4>(span, chunk.PixelSize.X, chunk.PixelSize.Y);
+        
+        GraphicsDevice.GetDefault().For(texture.Width, texture.Height, 
+            new ReplaceColorShader(
+                texture,
+                oldColorBounds,
+                new Float3(newColor.Red / 255f, newColor.Green / 255f, newColor.Blue / 255f)));
+        Rgba64[] pixels = new Rgba64[texture.Width * texture.Height];
+        texture.CopyTo(pixels);
+        ApplyPixelsToChunk(chunk, pixels);
+        //SKImage processedImage = SKBitmap.
+        
+        /*int maxThreads = Environment.ProcessorCount;
         VecI imageSize = chunk.PixelSize;
         int rowsPerThread = imageSize.Y / maxThreads;
 
@@ -44,6 +62,19 @@ internal class ReplaceColorOperation : IDrawOperation
         {
             if (oldColorBounds.IsWithinBounds(i))
                 *(ulong*)i = newColorBits;
+        }*/
+    }
+
+    private static unsafe void ApplyPixelsToChunk(Chunk chunk, Rgba64[] pixels)
+    {
+        using var drawPixmap = chunk.Surface.SkiaSurface.PeekPixels();
+        Half* drawArray = (Half*)drawPixmap.GetPixels();
+
+        for (int i = 0; i < pixels.Length; i++)
+        {
+            ulong pixel = pixels[i].PackedValue;
+            Half* drawPixel = drawArray + i * 4;
+            *(ulong*)drawPixel = pixel;
         }
     }
 

+ 71 - 0
src/ChunkyImageLib/Shaders/ReplaceColorShader.cs

@@ -0,0 +1,71 @@
+using ChunkyImageLib.DataHolders;
+using ComputeSharp;
+
+namespace ChunkyImageLib.Shaders;
+
+[AutoConstructor]
+public readonly partial struct ReplaceColorShader : IComputeShader
+{
+    public readonly IReadWriteNormalizedTexture2D<float4> texture;
+    public readonly HlslColorBounds colorBounds;
+    public readonly Float3 newColor;
+    public void Execute()
+    {
+        float4 rgba = texture[ThreadIds.XY].RGBA;
+        if(IsWithinBounds(rgba))
+        {
+            texture[ThreadIds.XY].RGB = newColor;
+        }
+    }
+
+    private bool IsWithinBounds(float4 color)
+    {
+        float r = color.R;
+        float g = color.G;
+        float b = color.B;
+        float a = color.A;
+        if (r < colorBounds.LowerR || r > colorBounds.UpperR)
+            return false;
+        if (g < colorBounds.LowerG || g > colorBounds.UpperG)
+            return false;
+        if (b < colorBounds.LowerB || b > colorBounds.UpperB)
+            return false;
+        return !(a < colorBounds.LowerA) && !(a > colorBounds.UpperA);
+    }
+}
+
+public readonly struct HlslColorBounds
+{
+    public readonly float LowerR;
+    public readonly float LowerG;
+    public readonly float LowerB;
+    public readonly float LowerA;
+    public readonly float UpperR;
+    public readonly float UpperG;
+    public readonly float UpperB;
+    public readonly float UpperA;
+
+    public HlslColorBounds(float4 color)
+    {
+        static (float lower, float upper) FindInclusiveBoundaryPremul(float channel, float alpha)
+        {
+            float subHalf = channel > 0 ? channel - .5f : channel;
+            float addHalf = channel < 255 ? channel + .5f : channel;
+            return (subHalf * alpha / 255f, addHalf * alpha / 255f);
+        }
+
+        static (float lower, float upper) FindInclusiveBoundary(float channel)
+        {
+            float subHalf = channel > 0 ? channel - .5f : channel;
+            float addHalf = channel < 255 ? channel + .5f : channel;
+            return (subHalf / 255f, addHalf / 255f);
+        }
+
+        float a = color.A / 255f;
+
+        (LowerR, UpperR) = FindInclusiveBoundaryPremul(color.R, a);
+        (LowerG, UpperG) = FindInclusiveBoundaryPremul(color.G, a);
+        (LowerB, UpperB) = FindInclusiveBoundaryPremul(color.B, a);
+        (LowerA, UpperA) = FindInclusiveBoundary(color.A);
+    }
+}

+ 0 - 1
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -113,7 +113,6 @@ internal class ViewModelMain : ViewModelBase
 
         Preferences.Init();
         DocumentManagerSubViewModel = services.GetRequiredService<DocumentManagerViewModel>();
-
         SelectionSubViewModel = services.GetService<SelectionViewModel>();
 
         OnStartupCommand = new RelayCommand(OnStartup);