Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
11eabed
Begin WebGPU backend
JimBobSquarePants Feb 20, 2026
7ea5866
We have a prototype!
JimBobSquarePants Feb 20, 2026
08fb876
Refactor WebGPU drawing backend and shaders
JimBobSquarePants Feb 20, 2026
ac16b61
Support multiple pixel fomats
JimBobSquarePants Feb 20, 2026
139cbb4
WebGPU: coverage scratch & dynamic uniform buffers
JimBobSquarePants Feb 21, 2026
4d5ce89
Introduce ICanvasFrame and native surface support
JimBobSquarePants Feb 21, 2026
4414c88
Refactor and simplify
JimBobSquarePants Feb 21, 2026
f7a8bc4
Refactor GPU composition to instance-based batching
JimBobSquarePants Feb 22, 2026
1b3b692
Refactor WebGPU backend to use FlushContext
JimBobSquarePants Feb 23, 2026
079488a
Improve WebGPU readback, batching and coverage
JimBobSquarePants Feb 23, 2026
c07f685
Refactor WebGPU composite pipeline & instance data
JimBobSquarePants Feb 23, 2026
00b4523
WebGPU: support instance buffer offsets & sessions
JimBobSquarePants Feb 23, 2026
d4ff9e1
Remove Configuration from FillPath; improve batching
JimBobSquarePants Feb 23, 2026
4d807df
Use List and pre-size entries in DrawTextOperations
JimBobSquarePants Feb 23, 2026
3255a1b
Cache path definition keys to avoid flattening
JimBobSquarePants Feb 23, 2026
4915396
Add glyph cache, layer & path handling
JimBobSquarePants Feb 23, 2026
bdf15aa
Add WebGPU brush composers and pipeline infra
JimBobSquarePants Feb 23, 2026
1d94c73
Per-brush instance payloads for WebGPU composite
JimBobSquarePants Feb 23, 2026
edc9b8c
Move WGPUTextureFormat note to code comment
JimBobSquarePants Feb 24, 2026
7e7ce5d
Switch compositing to compute shaders
JimBobSquarePants Feb 24, 2026
f659a3a
Tiled composite compute pass and brush refactor
JimBobSquarePants Feb 24, 2026
1eb7d56
Introduce CompositionScene and planning API
JimBobSquarePants Feb 24, 2026
a0eccdd
Document and refactor WebGPU drawing backend
JimBobSquarePants Feb 24, 2026
4bf2016
Add composition bounds and refactor flush context
JimBobSquarePants Feb 25, 2026
8f35205
Add WebGPU coverage pipeline and WGSL shaders
JimBobSquarePants Feb 27, 2026
06a8326
Composite: switch to tile-based dispatch
JimBobSquarePants Feb 27, 2026
bd79179
Add GPU tile-count/prefix/scatter passes
JimBobSquarePants Feb 27, 2026
d2d6143
Replace PreparedComposite with fine tiled pipeline
JimBobSquarePants Feb 28, 2026
1d41bac
Implement binning-based composite pipeline
JimBobSquarePants Mar 1, 2026
00f848e
Add docs and refactor WebGPU shaders/backend
JimBobSquarePants Mar 1, 2026
3599c42
Add ClearPath processors and refactor drawing API
JimBobSquarePants Mar 1, 2026
db204ba
Update ImageSharp.Drawing.csproj
JimBobSquarePants Mar 1, 2026
d793983
Close path before transform; update processors
JimBobSquarePants Mar 1, 2026
a534ee4
Merge branch 'main' into js/canvas-api
JimBobSquarePants Mar 1, 2026
5cb9b2d
Add DrawingCanvasState and stateful DrawingCanvas API
JimBobSquarePants Mar 2, 2026
15ade35
SIMD-accelerate coverage application; bump Fonts
JimBobSquarePants Mar 2, 2026
ad121a4
Replace scoped state with save/restore stack
JimBobSquarePants Mar 2, 2026
cc0777c
Add text measurement APIs to DrawingCanvas
JimBobSquarePants Mar 3, 2026
b86a39a
Use RowStride and DangerousTryGetSingleMemory
JimBobSquarePants Mar 3, 2026
4b74a3f
Add ProcessWithCanvas and DrawingCanvas factories
JimBobSquarePants Mar 3, 2026
1425632
Add IDrawingCanvas and non-generic ProcessWithCanvas
JimBobSquarePants Mar 3, 2026
06775a1
Add DrawGlyphs API and glyph rendering tests
JimBobSquarePants Mar 3, 2026
48d4b22
Use literal emoji in test string
JimBobSquarePants Mar 3, 2026
098ccd9
Add Process API, shadow fallback & WebGPU readback
JimBobSquarePants Mar 4, 2026
e450b85
Migrate clear brush tests to canvas API
JimBobSquarePants Mar 4, 2026
d39098d
Move Bezier draw tests to ProcessWithDrawingCanvas
JimBobSquarePants Mar 4, 2026
1d87102
Move DrawLines tests
JimBobSquarePants Mar 4, 2026
0bfb3c0
Mode DrawComplexPolygonTests
JimBobSquarePants Mar 4, 2026
42c80f4
Merge DrawPath/DrawPolygon tests into canvas tests
JimBobSquarePants Mar 4, 2026
3b701b6
Move FillComplexPolygon test to canvas-based tests
JimBobSquarePants Mar 4, 2026
faa3623
Move elliptic gradient tests to ProcessWithCanvas
JimBobSquarePants Mar 4, 2026
130f106
Migrate FillImageBrush tests to ProcessWithCanvas
JimBobSquarePants Mar 4, 2026
b8c714d
Move linear gradient tests to ProcessWithCanvas
JimBobSquarePants Mar 4, 2026
52fb65f
Migrate FillsOutOfBounds tests
JimBobSquarePants Mar 4, 2026
bb12d60
PathGradientBrush sampling & intersect fix; tests move
JimBobSquarePants Mar 4, 2026
b63da78
Move FillPath tests to ProcessWithCanvas
JimBobSquarePants Mar 4, 2026
7fa4871
Migrate text drawing tests to ProcessWithCanvas
JimBobSquarePants Mar 4, 2026
f01190b
Move FillPatternBrush tests to canvas tests
JimBobSquarePants Mar 4, 2026
625ab85
Migrate FillPolygon tests to ProcessWithDrawingCanvas
JimBobSquarePants Mar 4, 2026
34468f3
Migrate RadialGradient tests
JimBobSquarePants Mar 4, 2026
c15ba00
Migrate SolidBrush tests
JimBobSquarePants Mar 4, 2026
405b253
Migrate SweetGradientBrush tests
JimBobSquarePants Mar 4, 2026
e8922e4
Migrate RecolorBrush tests
JimBobSquarePants Mar 4, 2026
79f0909
Migrate SolidBezier tests
JimBobSquarePants Mar 4, 2026
64bcbb0
Migrate Blending tests
JimBobSquarePants Mar 4, 2026
0753ee0
Migrate Clip tests
JimBobSquarePants Mar 4, 2026
7f5f3d6
Migrate robustness tests
JimBobSquarePants Mar 4, 2026
1dd49af
Migrate Issues tests
JimBobSquarePants Mar 4, 2026
a0d0bb8
Migrate SVGPath tests
JimBobSquarePants Mar 4, 2026
fd0babf
Cleanup references
JimBobSquarePants Mar 4, 2026
697319e
Optimize refs
JimBobSquarePants Mar 4, 2026
368f99d
Remove legacy APIs
JimBobSquarePants Mar 4, 2026
691bc17
Fix build
JimBobSquarePants Mar 4, 2026
3fc0488
Update ImageSharp.Drawing.sln
JimBobSquarePants Mar 4, 2026
f4a3b87
Include binaries use streaming for composition
JimBobSquarePants Mar 4, 2026
73e4347
Update ImageSharp.Drawing.Tests.csproj
JimBobSquarePants Mar 4, 2026
d8e0910
Use tolerance comparer
JimBobSquarePants Mar 4, 2026
c2db283
Update DrawingCanvasTests.RegionAndState.cs
JimBobSquarePants Mar 4, 2026
18a51df
Skip WebGPU drawing tests on Linux
JimBobSquarePants Mar 4, 2026
2ec6284
Skip WebGPU tests for all CI
JimBobSquarePants Mar 4, 2026
f427d04
Remove rasterizer config
JimBobSquarePants Mar 4, 2026
d427d29
Replace PolygonScanner with DefaultRasterizer and optimize
JimBobSquarePants Mar 5, 2026
35c4445
Feng shui all the things.
JimBobSquarePants Mar 5, 2026
d06676f
Remove unused type
JimBobSquarePants Mar 5, 2026
d8a57f8
Reuse WorkerScratch across rasterizer calls + optimizations
JimBobSquarePants Mar 5, 2026
7d50cf9
Fix tile mapping and dispatch dimensions
JimBobSquarePants Mar 5, 2026
6fbc481
Use output texture for readback to avoid copy
JimBobSquarePants Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions ImageSharp.Drawing.sln
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\build-and-test.yml = .github\workflows\build-and-test.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImageSharp.Drawing.WebGPU", "src\ImageSharp.Drawing.WebGPU\ImageSharp.Drawing.WebGPU.csproj", "{061582C2-658F-40AE-A978-7D74A4EB2C0A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -359,6 +361,10 @@ Global
{5493F024-0A3F-420C-AC2D-05B77A36025B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5493F024-0A3F-420C-AC2D-05B77A36025B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5493F024-0A3F-420C-AC2D-05B77A36025B}.Release|Any CPU.Build.0 = Release|Any CPU
{061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{061582C2-658F-40AE-A978-7D74A4EB2C0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{061582C2-658F-40AE-A978-7D74A4EB2C0A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -386,6 +392,7 @@ Global
{68A8CC40-6AED-4E96-B524-31B1158FDEEA} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{5493F024-0A3F-420C-AC2D-05B77A36025B} = {528610AC-7C0C-46E8-9A2D-D46FD92FEE29}
{23859314-5693-4E6C-BE5C-80A433439D2A} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D}
{061582C2-658F-40AE-A978-7D74A4EB2C0A} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ SixLabors.ImageSharp.Drawing

</div>

### **ImageSharp.Drawing** provides extensions to ImageSharp containing powerful, cross-platform 2D polygon manipulation and drawing APIs.
### **ImageSharp.Drawing** provides extensions to ImageSharp containing powerful, Cross-Platform 2D polygon manipulation and drawing APIs.

Designed to democratize image processing, ImageSharp.Drawing brings you an incredibly powerful yet beautifully simple API.

Built against [.NET 6](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp.Drawing can be used in device, cloud, and embedded/IoT scenarios.
Built against [.NET 8](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), ImageSharp.Drawing can be used in device, cloud, and embedded/IoT scenarios.

## License

Expand Down Expand Up @@ -61,12 +61,12 @@ If you prefer, you can compile ImageSharp.Drawing yourself (please do and help!)

- Using [Visual Studio 2022](https://visualstudio.microsoft.com/vs/)
- Make sure you have the latest version installed
- Make sure you have [the .NET 7 SDK](https://www.microsoft.com/net/core#windows) installed
- Make sure you have [the .NET 8 SDK](https://www.microsoft.com/net/core#windows) installed

Alternatively, you can work from command line and/or with a lightweight editor on **both Linux/Unix and Windows**:

- [Visual Studio Code](https://code.visualstudio.com/) with [C# Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.csharp)
- [the .NET 7 SDK](https://www.microsoft.com/net/core#linuxubuntu)
- [the .NET 8 SDK](https://www.microsoft.com/net/core#linuxubuntu)

To clone ImageSharp.Drawing locally, click the "Clone in [YOUR_OS]" button above or run the following git commands:

Expand Down
72 changes: 0 additions & 72 deletions samples/DrawShapesWithImageSharp/ImageSharpLogo.cs

This file was deleted.

32 changes: 19 additions & 13 deletions samples/DrawShapesWithImageSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public static void Main(string[] args)
{
OutputClippedRectangle();
OutputStars();

ImageSharpLogo.SaveLogo(300, "ImageSharp.png");
}

private static void OutputStars()
Expand Down Expand Up @@ -239,11 +237,12 @@ public static void SaveImage(this IPathCollection collection, params string[] pa
int height = (int)(collection.Bounds.Top + collection.Bounds.Bottom);
using Image<Rgba32> img = new(width, height);

// Fill the canvas background and draw our shape
img.Mutate(i => i.Fill(Color.DarkBlue));

// Draw our path collection.
img.Mutate(i => i.Fill(Color.HotPink, collection));
img.Mutate(i => i.ProcessWithCanvas(canvas =>
{
// Fill the canvas background and draw our shape.
canvas.Fill(Brushes.Solid(Color.DarkBlue));
canvas.Fill(Brushes.Solid(Color.HotPink), collection);
}));

// Ensure directory exists
string fullPath = IOPath.GetFullPath(IOPath.Combine("Output", IOPath.Combine(path)));
Expand All @@ -264,11 +263,15 @@ public static void SaveImageWithPath(this IPathCollection collection, IPath shap

using Image<Rgba32> img = new(width, height);

// Fill the canvas background and draw our shape
img.Mutate(i => i.Fill(Color.DarkBlue).Fill(Color.White.WithAlpha(.25F), shape));
img.Mutate(i => i.ProcessWithCanvas(canvas =>
{
// Fill the canvas background and draw our shape.
canvas.Fill(Brushes.Solid(Color.DarkBlue));
canvas.Fill(shape, Brushes.Solid(Color.White.WithAlpha(.25F)));

// Draw our path collection.
img.Mutate(i => i.Fill(Color.HotPink, collection));
// Draw our path collection.
canvas.Fill(Brushes.Solid(Color.HotPink), collection);
}));

// Ensure directory exists
string fullPath = IOPath.GetFullPath(IOPath.Combine("Output", IOPath.Combine(path)));
Expand All @@ -282,8 +285,11 @@ public static void SaveImage(this IPath shape, int width, int height, params str
public static void SaveImage(this IPathCollection shape, int width, int height, params string[] path)
{
using Image<Rgba32> img = new(width, height);
img.Mutate(i => i.Fill(Color.DarkBlue));
img.Mutate(i => i.Fill(Color.HotPink, shape));
img.Mutate(i => i.ProcessWithCanvas(canvas =>
{
canvas.Fill(Brushes.Solid(Color.DarkBlue));
canvas.Fill(Brushes.Solid(Color.HotPink), shape);
}));

// Ensure directory exists
string fullPath = IOPath.GetFullPath(IOPath.Combine("Output", IOPath.Combine(path)));
Expand Down
73 changes: 73 additions & 0 deletions src/ImageSharp.Drawing.WebGPU/ImageSharp.Drawing.WebGPU.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>SixLabors.ImageSharp.Drawing.WebGPU</AssemblyName>
<AssemblyTitle>SixLabors.ImageSharp.Drawing.WebGPU</AssemblyTitle>
<RootNamespace>SixLabors.ImageSharp.Drawing.Processing.Backends</RootNamespace>
<PackageId>SixLabors.ImageSharp.Drawing.WebGPU</PackageId>
<PackageIcon>sixlabors.imagesharp.drawing.128.png</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl Condition="'$(RepositoryUrl)' == ''">https://github.com/SixLabors/ImageSharp.Drawing/</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<PackageTags>Image Draw Shape Path Font</PackageTags>
<Description>An extension to ImageSharp that allows the drawing of images, paths, and text.</Description>
<Configurations>Debug;Release</Configurations>
<IsTrimmable>true</IsTrimmable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

<!-- TEMP: To avoid publishing while we experiment. -->
<IsPublishable>false</IsPublishable>

<!--
This project is strong-name signed by the shared SixLabors build settings.
Silk.NET WebGPU packages are currently unsigned, which triggers CS8002.
Keep CS8002 visible as a warning, but do not fail the build for it.
-->
<WarningsNotAsErrors>$(WarningsNotAsErrors);8002</WarningsNotAsErrors>
</PropertyGroup>

<PropertyGroup>
<!--Bump to V1 prior to tagged release.-->
<MinVerMinimumMajorMinor>1.0</MinVerMinimumMajorMinor>
</PropertyGroup>

<!-- This enables the nullable analysis and treats all nullable warnings as error-->
<PropertyGroup>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
</PropertyGroup>

<Choose>
<When Condition="$(SIXLABORS_TESTING_PREVIEW) == true">
<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
</PropertyGroup>
</Otherwise>
</Choose>

<ItemGroup>
<None Include="..\..\LICENSE" Pack="true" PackagePath="" />
<None Include="..\..\shared-infrastructure\branding\icons\imagesharp.drawing\sixlabors.imagesharp.drawing.128.png" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<!--
Silk compile assets are needed by this project but should not flow transitively to consumers.
This keeps downstream projects from seeing Silk reference warnings (for example CS8002)
unless they explicitly reference Silk themselves.
Runtime/native assets still flow so the backend can execute.
-->
<PackageReference Include="Silk.NET.WebGPU" Version="2.23.0" PrivateAssets="compile;build;analyzers;contentfiles;buildtransitive" />
<PackageReference Include="Silk.NET.WebGPU.Extensions.WGPU" Version="2.23.0" PrivateAssets="compile;build;analyzers;contentfiles;buildtransitive" />
<PackageReference Include="Silk.NET.WebGPU.Native.WGPU" Version="2.23.0" PrivateAssets="compile;build;analyzers;contentfiles;buildtransitive" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ImageSharp.Drawing\ImageSharp.Drawing.csproj" />
</ItemGroup>
</Project>
118 changes: 118 additions & 0 deletions src/ImageSharp.Drawing.WebGPU/Shaders/BackdropComputeShader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Drawing.Processing.Backends;

/// <summary>
/// Copies the destination texture into a composition backdrop for read-only sampling.
/// </summary>
internal static class BackdropComputeShader
{
/// <summary>
/// Gets the null-terminated WGSL source for the backdrop copy pass.
/// </summary>
private static readonly byte[] CodeBytes =
[
..
"""
struct Tile {
backdrop: i32,
segment_count_or_ix: u32,
}

struct Config {
width_in_tiles: u32,
height_in_tiles: u32,
target_width: u32,
target_height: u32,
base_color: u32,
n_drawobj: u32,
n_path: u32,
n_clip: u32,
bin_data_start: u32,
pathtag_base: u32,
pathdata_base: u32,
drawtag_base: u32,
drawdata_base: u32,
transform_base: u32,
style_base: u32,
lines_size: u32,
binning_size: u32,
tiles_size: u32,
seg_counts_size: u32,
segments_size: u32,
blend_size: u32,
ptcl_size: u32,
}

@group(0) @binding(0)
var<uniform> config: Config;

@group(0) @binding(1)
var<storage, read_write> tiles: array<Tile>;

const WG_SIZE = 64u;
var<workgroup> sh_backdrop: array<i32, WG_SIZE>;
var<workgroup> running_backdrop: i32;

@compute @workgroup_size(64)
fn cs_main(
@builtin(local_invocation_id) local_id: vec3<u32>,
@builtin(workgroup_id) wg_id: vec3<u32>,
) {
let width_in_tiles = config.width_in_tiles;
let row_index = wg_id.x;
if row_index >= config.height_in_tiles {
return;
}

if local_id.x == 0u {
running_backdrop = 0;
}
workgroupBarrier();

var chunk_start = 0u;
loop {
if chunk_start >= width_in_tiles {
break;
}

let count = min(WG_SIZE, width_in_tiles - chunk_start);
var backdrop = 0;
if local_id.x < count {
let ix = row_index * width_in_tiles + chunk_start + local_id.x;
backdrop = tiles[ix].backdrop;
}

sh_backdrop[local_id.x] = backdrop;
for (var i = 0u; i < firstTrailingBit(WG_SIZE); i += 1u) {
workgroupBarrier();
if local_id.x >= (1u << i) {
backdrop += sh_backdrop[local_id.x - (1u << i)];
}

workgroupBarrier();
sh_backdrop[local_id.x] = backdrop;
}

workgroupBarrier();
if local_id.x < count {
let ix = row_index * width_in_tiles + chunk_start + local_id.x;
let accumulated = sh_backdrop[local_id.x] + running_backdrop;
tiles[ix].backdrop = accumulated;
if local_id.x + 1u == count {
running_backdrop = accumulated;
}
}

workgroupBarrier();
chunk_start += WG_SIZE;
}
}
"""u8,
0
];

/// <summary>Gets the WGSL source for this shader as a null-terminated UTF-8 span.</summary>
public static ReadOnlySpan<byte> Code => CodeBytes;
}
Loading
Loading