Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
position: relative;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}

main {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}

.sidebar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> SkiaSharp
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="raster">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Raster Canvas
<NavLink class="nav-link" href="gpu">
<span class="bi bi-gpu-card-nav-menu" aria-hidden="true"></span> GPU
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="gpu">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> GPU Canvas
<NavLink class="nav-link" href="drawing">
<span class="bi bi-pencil-nav-menu" aria-hidden="true"></span> Drawing
</NavLink>
</div>
</nav>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}

.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
.bi-gpu-card-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-gpu-card' viewBox='0 0 16 16'%3E%3Cpath d='M4 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0m7.5-1.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3'/%3E%3Cpath d='M0 1.5A.5.5 0 0 1 .5 1h1a.5.5 0 0 1 .5.5V4h13.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5H2v2.5a.5.5 0 0 1-1 0V2H.5a.5.5 0 0 1-.5-.5m5.5 4a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M9 8a2.5 2.5 0 1 0 5 0 2.5 2.5 0 0 0-5 0'/%3E%3Cpath d='M3 12.5h3.5v1a.5.5 0 0 1-.5.5H3.5a.5.5 0 0 1-.5-.5zm4 0h4v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}

.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
.bi-pencil-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-pencil-fill' viewBox='0 0 16 16'%3E%3Cpath d='M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.5.5 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11z'/%3E%3C/svg%3E");
}

.nav-item {
Expand Down
159 changes: 159 additions & 0 deletions samples/Basic/BlazorWebAssembly/SkiaSharpSample/Pages/Drawing.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
@page "/drawing"

@* Drawing Canvas Demo
Demonstrates SKCanvasView with pointer events, wheel delta for
brush size control, and basic stroke rendering.
Shows multi-stroke drawing with color picker and on-demand rendering
via manual Invalidate() (no render loop). *@

<h1>Drawing Canvas</h1>

<p>
Draw with touch or mouse. Use the scroll wheel to change brush size.
</p>

<div class="canvas-container border rounded p-2" @onwheel="OnWheel" @onwheel:preventDefault>

<SKCanvasView @ref="skiaView"
OnPaintSurface="OnPaintSurface" IgnorePixelScaling="true"
@onpointerdown="OnPointerDown" @onpointermove="OnPointerMove" @onpointerup="OnPointerUp" />

<div class="overlay-info">
Brush: @(brushSize.ToString("F2"))px
</div>

<div class="overlay-toolbar">
@foreach (var (name, color) in colors)
{
<div class="color-swatch @(currentColor == color ? "selected" : "")"
style="background:@name"
@onclick="() => SetColor(color)" />
}
<button class="btn btn-outline-light btn-sm" @onclick="ClearCanvas">Clear</button>
</div>

</div>

@code {

SKCanvasView skiaView = null!;
float brushSize = 25f;
SKColor currentColor = SKColors.Black;
List<DrawingStroke> strokes = [];
DrawingStroke? currentStroke = null;
SKPoint lastPoint;

// The paint that is reused for drawing strokes.
readonly SKPaint strokePaint = new()
{
IsAntialias = true,
Style = SKPaintStyle.Stroke,
StrokeCap = SKStrokeCap.Round,
StrokeJoin = SKStrokeJoin.Round,
};

// The available brush colors with their CSS names.
static readonly (string Name, SKColor Color)[] colors =
{
("black", SKColors.Black),
("red", SKColors.Red),
("blue", SKColors.Blue),
("green", SKColors.Green),
("orange", SKColors.Orange),
("purple", SKColors.Purple),
};

void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;

// Clear to white before drawing the strokes.
canvas.Clear(SKColors.White);

// Draw completed strokes.
foreach (var stroke in strokes)
{
strokePaint.Color = stroke.Color;
strokePaint.StrokeWidth = stroke.StrokeWidth;
canvas.DrawPath(stroke.Path, strokePaint);
}

// Draw in-progress stroke.
if (currentStroke is not null)
{
strokePaint.Color = currentStroke.Color;
strokePaint.StrokeWidth = currentStroke.StrokeWidth;
canvas.DrawPath(currentStroke.Path, strokePaint);
}

// Brush size indicator at last pointer position.
strokePaint.Color = SKColors.Gray;
strokePaint.StrokeWidth = 1;
canvas.DrawCircle(lastPoint, brushSize / 2f, strokePaint);
}

void OnPointerDown(PointerEventArgs e)
{
if (!e.IsPrimary)
return;
lastPoint = new SKPoint((float)e.OffsetX, (float)e.OffsetY);
currentStroke = new DrawingStroke(new(), currentColor, brushSize);
currentStroke.Path.MoveTo(lastPoint);
skiaView.Invalidate();
}

void OnPointerMove(PointerEventArgs e)
{
if (!e.IsPrimary)
return;
lastPoint = new SKPoint((float)e.OffsetX, (float)e.OffsetY);
if (currentStroke is not null)
currentStroke.Path.LineTo(lastPoint);
skiaView.Invalidate();
}

void OnPointerUp(PointerEventArgs e)
{
if (!e.IsPrimary)
return;
if (currentStroke is not null)
{
strokes.Add(currentStroke);
currentStroke = null;
skiaView.Invalidate();
}
}

void OnWheel(WheelEventArgs e)
{
// DeltaY > 0 = scroll down = decrease brush size; DeltaY < 0 = scroll up = increase.
// Normalize ~100 CSS pixels per notch to ~1px brush change per notch.
brushSize -= (float)(e.DeltaY / 100.0);
brushSize = Math.Clamp(brushSize, 1f, 50f);
StateHasChanged();
}

void SetColor(SKColor color)
{
currentColor = color;
skiaView.Invalidate();
StateHasChanged();
}

void ClearCanvas()
{
foreach (var stroke in strokes)
{
stroke.Path.Dispose();
}
strokes.Clear();
currentStroke?.Path.Dispose();
currentStroke = null;
skiaView.Invalidate();
StateHasChanged();
}

// Represents a single drawing stroke with its path, color, and width.
// Path is owned — callers must dispose when removing strokes.
record DrawingStroke(SKPath Path, SKColor Color, float StrokeWidth = 25);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
.canvas-container {
position: relative;
}

.overlay-info {
position: absolute;
top: 12px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 8px;
padding: 6px 16px;
backdrop-filter: blur(4px);
color: white;
font-size: 0.85rem;
font-weight: 500;
}

.overlay-toolbar {
position: absolute;
bottom: 16px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 8px;
align-items: center;
z-index: 1;
background: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 8px;
padding: 8px 16px;
backdrop-filter: blur(4px);
}

.color-swatch {
width: 26px;
height: 26px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
}

.color-swatch.selected {
border-color: white;
}
Loading