Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
36b1ecf
Match Skia output (numerous bug fixes and API additions)
JimBobSquarePants Jun 17, 2026
bd2b146
Normalize and pre-transform brushes and add ClipOperation
JimBobSquarePants Jun 19, 2026
58e7243
Support region-path clipping; remove batcher tracing
JimBobSquarePants Jun 19, 2026
031a57b
Add coverage-aware blending and refactor brushes
JimBobSquarePants Jun 22, 2026
42d7517
Add shared DrawingTextCache and integrate into canvases
JimBobSquarePants Jun 22, 2026
08c32c9
Add GpuImageSource and external texture support
JimBobSquarePants Jun 22, 2026
07827a3
Optimize rectangular clip handling
JimBobSquarePants Jun 22, 2026
a220f1a
Per-fill aliased coverage support and blit
JimBobSquarePants Jun 23, 2026
abba2d0
Use TryGetPathPointAtDistance pattern
JimBobSquarePants Jun 24, 2026
45c92cf
Add scale-aware geometry APIs and linear ops
JimBobSquarePants Jun 24, 2026
b68e8b9
Refactor clipping API and add difference clip support
JimBobSquarePants Jul 2, 2026
cfb4830
Update WebGPUSceneEncoder.cs
JimBobSquarePants Jul 2, 2026
b5992b5
Document backend pipelines and trim dead code
JimBobSquarePants Jul 2, 2026
f7bfece
Remove ShapeOptions from drawing APIs
JimBobSquarePants Jul 2, 2026
ddf323a
Fix elliptic gradients and clip layers + document.
JimBobSquarePants Jul 3, 2026
3dc498a
Defer WebGPU readbacks and text caching
JimBobSquarePants Jul 3, 2026
2d7f21c
Pool WorkerState instances across parallel flushes
JimBobSquarePants Jul 3, 2026
1bd0120
Fix decoration stitching across layered glyphs
JimBobSquarePants Jul 5, 2026
6acecd2
Add TextContrast coverage boost for text
JimBobSquarePants Jul 5, 2026
a6d49d9
Fix stack overflow with huge path coordinates (#403)
JimBobSquarePants Jul 5, 2026
60791df
Refresh text rendering reference images
JimBobSquarePants Jul 5, 2026
b7c4604
Update refs to handle local font fixes
JimBobSquarePants Jul 5, 2026
b6f02b0
Improve WebGPU scene sizing and pipeline warmup
JimBobSquarePants Jul 6, 2026
c391918
Fix WebGPU chunking and status readback handling
JimBobSquarePants Jul 7, 2026
b97f8fa
Clarify polygon clip normalization
JimBobSquarePants Jul 7, 2026
3f545d2
Expose drawing backend APIs
JimBobSquarePants Jul 8, 2026
c9072cc
Share WebGPU instance across surfaces
JimBobSquarePants Jul 8, 2026
0554014
Refactor skip-ink carving and fix decoration pen width
JimBobSquarePants Jul 8, 2026
2d3bc39
Add layer and backdrop effect support
JimBobSquarePants Jul 9, 2026
8b95253
Apply transform to path bounds in rasterizer options
JimBobSquarePants Jul 10, 2026
8d4d500
Fix glyph area heuristic for decoration layers
JimBobSquarePants Jul 10, 2026
462e78d
Fix path text decoration transforms
JimBobSquarePants Jul 10, 2026
23c59ec
Fix WebGPU tile boundary winding inversions
JimBobSquarePants Jul 12, 2026
4684dd1
Scope DevicePoll to submission index for readback
JimBobSquarePants Jul 12, 2026
c04dc71
Refactor ordered WebGPU scene execution
JimBobSquarePants Jul 13, 2026
a7db60a
Keep WebGPU loaders alive until process exit
JimBobSquarePants Jul 13, 2026
17b1c49
Add WebGPU surface alpha mode option
JimBobSquarePants Jul 13, 2026
2b361b7
Add premultiplied rendering and native WebGPU interop
JimBobSquarePants Jul 17, 2026
3239307
Add WebGPU shader effects and native API refactor
JimBobSquarePants Jul 20, 2026
4a88695
Merge path-text decorations into continuous bands
JimBobSquarePants Jul 21, 2026
787de2f
Fix text culling and glyph subpixel batching
JimBobSquarePants Jul 22, 2026
c9a9e4d
Expand layer effect bounds using reach
JimBobSquarePants Jul 22, 2026
9c4d7fb
Merge remote-tracking branch 'origin/main' into js/skia-parity
JimBobSquarePants Jul 30, 2026
da11e33
Refactor glyph drawing API and clipping flow
JimBobSquarePants Jul 30, 2026
ad186e5
Resolve WebGPU native assets across deployment layouts
JimBobSquarePants Jul 30, 2026
bfb298a
Update BenchmarkDotNet to 0.15.8
JimBobSquarePants Jul 30, 2026
332e360
Merge branch 'main' into js/skia-parity
JimBobSquarePants Jul 30, 2026
b87370f
Probe render support before reporting WebGPU availability
JimBobSquarePants Jul 30, 2026
36eef59
Stage the WebGPU support probe and name each failing capability
JimBobSquarePants Jul 31, 2026
817df78
Document staged probe constraints
JimBobSquarePants Jul 31, 2026
1e262e3
Capture WebGPU crash dumps from Windows CI
JimBobSquarePants Jul 31, 2026
c17df3d
Fix arena double-release on ordered flush failure and harden disposal
JimBobSquarePants Jul 31, 2026
0d80bcf
Compute stroke arc sweeps from relative angles
JimBobSquarePants Jul 31, 2026
185aa27
Admit cross-adapter rounding in reference-output tolerances
JimBobSquarePants Jul 31, 2026
c3a6909
Tighten reference tolerances to measured difference plus one
JimBobSquarePants Jul 31, 2026
0e4ebc5
Include the WebGPU assembly in coverage and close the largest test gaps
JimBobSquarePants Jul 31, 2026
959d2df
Cover the small zero-coverage types and exclude submodule sources
JimBobSquarePants Jul 31, 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
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,15 @@
# treat as binary
###############################################################################
*.basis binary
*.a binary
*.dll binary
*.dylib binary
*.exe binary
*.pdf binary
*.ppt binary
*.pptx binary
*.pvr binary
*.so binary
*.snk binary
*.xls binary
*.xlsx binary
Expand Down
3 changes: 3 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# GitHub Copilot Instructions

Read and follow [AGENTS.md](../AGENTS.md) as the repository-wide source of coding, performance, and verification requirements. Prefer existing local patterns and repository configuration whenever generated code or suggestions are accepted.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,6 @@ SixLabors.Shapes.Coverage.xml
.dotnet
.codex-*
.claude
/plans/

samples/Avalonia**
41 changes: 41 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Six Labors AI Coding Guidelines

These instructions apply to the entire repository. More-specific `AGENTS.md` files may add to or override them for their directory tree.

## Working Practices

- Inspect the relevant implementation, tests, benchmarks, project files, and nearby code before proposing or making changes. Do not infer current behavior when the source is available.
- Make the smallest complete change that solves the requested problem. Avoid unrelated cleanup, speculative abstractions, and formatting churn.
- Match established architecture, naming, formatting, documentation, and test patterns. Treat `.editorconfig`, analyzers, and repository build settings as authoritative.
- Preserve public API and observable behavior unless the task explicitly requires a change. Public API documentation must describe observable behavior, not implementation details.
- Do not use reflection against built assemblies, ad hoc assembly loading, or temporary probe projects unless explicitly requested.
- Build .NET projects in Release configuration unless explicitly instructed otherwise.

## Performance

- Treat throughput, latency, memory use, and binary size as design constraints, especially in pixel-processing, drawing, parsing, encoding, and other hot paths.
- Avoid unnecessary allocations, copies, boxing, closures, interface dispatch, repeated enumeration, and extra passes over data.
- Reuse the repository's existing memory ownership, pooling, span, vectorization, and parallelization patterns. Do not introduce a new mechanism when an established one fits.
- Keep hot loops simple and bounds-check-friendly. Hoist invariant work, preserve locality, and use the narrowest suitable data types without sacrificing correctness.
- Do not trade correctness or maintainability for assumed speed. Support non-obvious optimizations with measurements or clear evidence, and add or update benchmarks when performance is the purpose of the change.
- Consider all supported target frameworks and runtime capabilities. Do not regress fallback paths while optimizing newer runtimes.

## C# Conventions

- Follow the existing code around the change; local patterns take precedence over generic preferences.
- Do not use `record` or `record struct` types.
- Prefer established invariants over redundant guards. Validate at real external boundaries and do not add defensive checks for internally controlled states.
- Do not extract single-use helpers merely to name a block. Extract only for genuine reuse, an established local pattern, or meaningful complexity reduction.
- Add vertical whitespace after multi-line statements and declarations and between distinct logical stages. Never add trailing whitespace.
- Document every method, constructor, and property, regardless of whether it is public, internal, protected, or private. Keep public API documentation limited to observable behavior; use private and internal documentation to capture the contract and intent needed to maintain the code.
- Add inline comments throughout complex code. Explain algorithms, formulas, invariants, ownership, compatibility behavior, and performance tradeoffs at the operations and decisions they govern. Explain why the code is shaped that way rather than narrating the syntax.
- Document SIMD code especially thoroughly. Explain the vector layout, lane meaning, widening or narrowing, masks, shuffles, constants, alignment or remainder handling, supported instruction paths, scalar equivalence, and the reason each non-obvious operation is correct.
- Write algorithm and SIMD comments for a maintainer who is unfamiliar with the implementation. The reader should not need to reconstruct intent from external documentation, issue history, or benchmark results.

## Verification

- Add or update focused tests when behavior changes, following the test framework and conventions already used by the project.
- Never hack, weaken, skip, conditionally bypass, or otherwise manipulate a test to make it pass. Fix the production defect or the genuine test defect while preserving the test's intended coverage and sensitivity.
- Do not update golden files, reference images, snapshots, baselines, or expected-output artifacts to resolve a test failure. Treat a mismatch as evidence to investigate and correct the implementation.
- Run the narrowest relevant formatting, test, and Release build commands, then expand verification in proportion to the risk and scope of the change.
- Report what changed, the verification performed, and any remaining risks or unverified assumptions.
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Claude Code Instructions

Read and follow [AGENTS.md](AGENTS.md) as the repository-wide source of coding, performance, and verification requirements. Apply any more-specific `AGENTS.md` or `CLAUDE.md` found below the files being changed.
3 changes: 3 additions & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Gemini CLI Instructions

Read and follow [AGENTS.md](AGENTS.md) as the repository-wide source of coding, performance, and verification requirements. Apply any more-specific `AGENTS.md` or `GEMINI.md` found below the files being changed.
34 changes: 10 additions & 24 deletions samples/DrawShapesWithImageSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,24 +157,15 @@ private static void DrawPosterComposition()

canvas.Fill(Brushes.Solid(Color.SeaGreen), shorelineShape);

// Clipping demo: canvas.Save accepts a clip path plus DrawingOptions whose
// BooleanOperation controls how the new clip combines with the existing one. Using
// Intersection means subsequent draws are masked to the oval highlight shape, so a
// rectangular gradient fill can be reused without re-shaping it as an ellipse.
EllipsePolygon lakeHighlight = new(725, 512, 285, 86);
DrawingOptions lakeHighlightClipOptions = new()
{
ShapeOptions = new ShapeOptions
{
BooleanOperation = BooleanOperation.Intersection
}
};

RectangleF lakeHighlightBounds = lakeHighlight.Bounds;

// Save pushes a clipping state; Restore pops it. Anything drawn between the two is
// Save pushes a state; Clip narrows it. Anything drawn between the two is
// confined to lakeHighlight even though the brush spans its full bounding rectangle.
canvas.Save(lakeHighlightClipOptions, lakeHighlight);
canvas.Save();
canvas.Clip(lakeHighlight);

canvas.Fill(Brushes.ForwardDiagonal(Color.White.WithAlpha(.36F), Color.Transparent), new RectanglePolygon(lakeHighlightBounds));
canvas.Restore();

Expand Down Expand Up @@ -910,7 +901,7 @@ private static void DrawImageProcessingMask()

canvas.DrawText(
new RichTextOptions(subtitleFont) { Origin = new PointF(40, 66) },
"Apply() runs a processor inside an IPath, ImageBrush fills one with a photo, and Save() clips drawing to one.",
"Apply() runs a processor inside an IPath, ImageBrush fills one with a photo, and Clip() limits drawing to one.",
Brushes.Solid(secondaryColor),
pen: null);

Expand Down Expand Up @@ -1068,20 +1059,15 @@ static void DrawPhotoInTextPanel(
imageArea.Y + ((imageArea.Height - textBounds.Height) / 2F) - textBounds.Y),
};

// GeneratePaths returns one IPath per glyph. canvas.Save accepts params IPath[] so
// the whole collection becomes a compound clip, but ShapeOptions.BooleanOperation
// must be set to Intersection: the default Difference would cut the glyph shapes
// OUT of the photograph, the opposite of "image inside text".
// GeneratePaths returns one IPath per glyph. Clip accepts params IPath[] so
// the whole collection becomes one compound clip for the photograph.
IPathCollection letters = TextBuilder.GeneratePaths("MASK", glyphOptions);
IPath[] glyphClips = [.. letters];

DrawingOptions clipToGlyphs = new()
{
ShapeOptions = new ShapeOptions { BooleanOperation = BooleanOperation.Intersection },
};

canvas.Fill(Brushes.Solid(Color.ParseHex("#E2DCC2")), new RectanglePolygon(imageArea));
canvas.Save(clipToGlyphs, glyphClips);
canvas.Save();
canvas.Clip(glyphClips);

canvas.DrawImage(source, source.Bounds, imageArea, null);
canvas.Restore();

Expand Down
4 changes: 2 additions & 2 deletions samples/DrawShapesWithImageSharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Poster-style landscape that exercises the building blocks:

- `LinearGradientBrush` and `RadialGradientBrush` for the sky, lake, and sun.
- `PathBuilder` + `CloseFigure` for the mountain ridges, lake, and shoreline.
- `canvas.Save(options, IPath)` with `BooleanOperation.Intersection` for the lake highlight clip.
- `canvas.Save()` plus `canvas.Clip(IPath)` for the lake highlight clip.
- `canvas.Save` + `canvas.SaveLayer` to apply a Z rotation and then composite the title panel and text together with `GraphicsOptions.BlendPercentage`.
- `TextMeasurer.MeasureRenderableBounds` to size the title panel to the laid-out text.

Expand Down Expand Up @@ -38,7 +38,7 @@ Image-compositing scene demonstrating four ways a photograph (`tests/Images/Inpu
- **Before / after wipe** — `canvas.Apply(rightHalfRect, ctx => ctx.OilPaint(15, 5))` scopes an `OilPaint` processor to the right half of the photograph.
- **Privacy redaction** — `canvas.Apply(ellipse, ctx => ctx.Pixelate(10))` pixelates an elliptical face-shaped region and leaves the rest untouched.
- **Image as a brush** — `new ImageBrush<Rgba32>(source, source.Bounds, brushOffset)` wraps the photograph as a `Brush` so a `StarPolygon` path can be filled with it as a texture; the brush offset aligns the mountain in the photograph with the star's centre.
- **Photo in text** — `TextBuilder.GeneratePaths("MASK", ...)` produces one `IPath` per glyph; `canvas.Save(intersectionOptions, glyphPaths)` uses them as a compound clip so `DrawImage` only renders inside the letterforms.
- **Photo in text** — `TextBuilder.GeneratePaths("MASK", ...)` produces one `IPath` per glyph; `canvas.Clip(glyphPaths)` uses them as a compound clip so `DrawImage` only renders inside the letterforms.

## Running

Expand Down
18 changes: 13 additions & 5 deletions samples/WebGPUExternalSurfaceDemo/Controls/WebGPURenderControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public sealed partial class WebGPURenderControl : Control
{
private const int WM_MOVING = 0x0216;
private const int WM_EXITSIZEMOVE = 0x0232;
private static readonly nint ProcessModuleHandle = GetModuleHandle(null);

private readonly WebGPUSurfaceSession surfaceSession;
private WebGPUExternalSurface? surface;
private Size framebufferSize;
private bool idleHooked;
Expand All @@ -29,8 +31,11 @@ public sealed partial class WebGPURenderControl : Control
/// <summary>
/// Initializes a new instance of the <see cref="WebGPURenderControl"/> class.
/// </summary>
public WebGPURenderControl()
/// <param name="surfaceSession">The session shared by the application's related presentation surfaces.</param>
public WebGPURenderControl(WebGPUSurfaceSession surfaceSession)
{
this.surfaceSession = surfaceSession;

// WebGPU presents directly to the native surface. Normal WinForms buffering and background
// painting would add flicker or unnecessary work, so the control opts into direct user painting.
this.SetStyle(
Expand Down Expand Up @@ -81,12 +86,12 @@ protected override void OnHandleCreated(EventArgs e)
Math.Max(this.framebufferSize.Width, 1),
Math.Max(this.framebufferSize.Height, 1));

// The module handle is required by the Win32 surface descriptor. It identifies the process module
// that owns the window class backing this control.
this.surface = new WebGPUExternalSurface(
// WinForms registers its window classes against the process executable module. Use that same
// HINSTANCE in the WebGPU descriptor instead of the managed assembly's module handle.
this.surface = this.surfaceSession.CreateSurface(
WebGPUSurfaceHost.Win32(
this.Handle,
Marshal.GetHINSTANCE(typeof(WebGPURenderControl).Module)),
ProcessModuleHandle),
initialFramebufferSize,
new WebGPUExternalSurfaceOptions
{
Expand Down Expand Up @@ -322,5 +327,8 @@ private struct NativeMessage
[LibraryImport("user32.dll", EntryPoint = "PeekMessageW")]
private static partial int PeekMessage(out NativeMessage msg, nint hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

[LibraryImport("kernel32.dll", EntryPoint = "GetModuleHandleW", StringMarshalling = StringMarshalling.Utf16)]
private static partial nint GetModuleHandle(string? moduleName);

private static bool IsApplicationIdle() => PeekMessage(out _, 0, 0, 0, 0) == 0;
}
Loading
Loading