Skip to content
Open
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
75 changes: 75 additions & 0 deletions samples/Basic/UnoPlatform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# SkiaSharp Uno Platform Sample

Demonstrates SkiaSharp views in an Uno Platform app with `NavigationView` top tabs, XAML layouts, and cross-platform targeting from a single codebase.

## Sample Pages

This sample shows how to integrate SkiaSharp views into an Uno Platform app using XAML. The `SKXamlCanvas` and `SKSwapChainPanel` controls are placed declaratively in `.xaml` files alongside standard Uno controls, targeting multiple platforms (Windows, WebAssembly, iOS, Android, macOS, Linux) from a single codebase.

Navigation uses a `NavigationView` with `PaneDisplayMode="Top"` for a clean tab bar that works well on both mobile and desktop.

### CPU

A static scene rendered on the CPU — a radial gradient background overlaid with semi-transparent colored circles and centered "SkiaSharp" text.

**Features:**

- **`SKXamlCanvas`** — Software-rendered canvas that integrates into XAML layout on all Uno target platforms.
- **`SKShader`** — Radial gradient background created with `SKShader.CreateRadialGradient`.
- **`SKCanvas.DrawCircle`** — Semi-transparent colored circles composited over the gradient.
- **`SKCanvas.DrawText`** — Centered "SkiaSharp" text rendered with measured alignment.

### GPU

A real-time animated shader running at full frame rate on the GPU, with pointer interaction that adds a white-hot blob to the metaball field.

**Features:**

- **`SKSwapChainPanel`** — Hardware-accelerated canvas using ANGLE on Windows/Android, WebGL on WebAssembly.
- **`SKRuntimeEffect`** — SkSL metaball "lava lamp" shader compiled at runtime with `SKRuntimeEffect.BuildShader`.
- **Render loop** — Continuous animation with `EnableRenderLoop="True"` and an FPS counter overlay.
- **Pointer interaction** — Pointer position is passed as a shader uniform.

> **Note:** The GPU page is hidden at runtime on Mac Catalyst where `SKSwapChainPanel` is not supported.

### Drawing

A freehand drawing canvas with a color palette, brush size slider, and clear button.

**Features:**

- **`SKXamlCanvas`** — Software-rendered canvas invalidated on demand after each stroke or clear.
- **`SKPath`** — Freehand strokes captured as paths with `MoveTo` and `LineTo` from pointer events.
- **Pointer events** — `PointerPressed`, `PointerMoved`, `PointerReleased` for cross-device input.
- **`PointerWheelChanged`** — Scroll wheel to adjust brush size (desktop).
- **Color palette** — Six selectable colors with dark/light mode variants.
- **Responsive layout** — Toolbox adapts between vertical and horizontal orientation based on window width.

## Screenshots

| CPU | GPU | Drawing |
|---|---|---|
| <img src="screenshots/cpu.png" width="250" alt="CPU"> | <img src="screenshots/gpu.png" width="250" alt="GPU"> | <img src="screenshots/drawing.png" width="250" alt="Drawing"> |

## Requirements

- [.NET 10 SDK](https://dotnet.microsoft.com/download) or later
- Uno Platform workload: `dotnet workload install uno-platform`
- Platform-specific workloads for target platforms (e.g., `android`, `ios`, `maccatalyst`, `wasm-tools`)

## Running the Sample

Build and run the desktop target:

```bash
dotnet run -f net10.0-desktop
```

Or target a specific platform:

```bash
dotnet build -f net10.0-browserwasm # WebAssembly
dotnet build -f net10.0-android # Android
dotnet build -f net10.0-ios # iOS
dotnet build -f net10.0-maccatalyst # Mac Catalyst
```
47 changes: 5 additions & 42 deletions samples/Basic/UnoPlatform/SkiaSharpSample/Assets/Icons/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions samples/Basic/UnoPlatform/SkiaSharpSample/CpuPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Page
x:Class="SkiaSharpSample.CpuPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:skia="using:SkiaSharp.Views.Windows">

<skia:SKXamlCanvas PaintSurface="OnPaintSurface" />

</Page>
69 changes: 69 additions & 0 deletions samples/Basic/UnoPlatform/SkiaSharpSample/CpuPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using SkiaSharp;
using SkiaSharp.Views.Windows;

namespace SkiaSharpSample;

public sealed partial class CpuPage : Page
{
static readonly (float X, float Y, float R, SKColor Color)[] circles =
{
(0.20f, 0.30f, 0.10f, new SKColor(0xFF, 0x4D, 0x66, 0xCC)),
(0.75f, 0.25f, 0.08f, new SKColor(0x4D, 0xB3, 0xFF, 0xCC)),
(0.15f, 0.70f, 0.07f, new SKColor(0xFF, 0x99, 0x1A, 0xCC)),
(0.80f, 0.70f, 0.12f, new SKColor(0x66, 0xFF, 0xB3, 0xCC)),
(0.50f, 0.15f, 0.06f, new SKColor(0xB3, 0x4D, 0xFF, 0xCC)),
(0.40f, 0.80f, 0.09f, new SKColor(0xFF, 0xE6, 0x33, 0xCC)),
};

static readonly SKColor[] gradientColors =
{
new SKColor(0x44, 0x88, 0xFF),
new SKColor(0x88, 0x33, 0xCC),
};

public CpuPage()
{
InitializeComponent();
}

private void OnPaintSurface(object? sender, SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
var width = e.Info.Width;
var height = e.Info.Height;
var center = new SKPoint(width / 2f, height / 2f);
var radius = Math.Max(width, height) / 2f;

canvas.Clear(SKColors.White);

// Background gradient
using var shader = SKShader.CreateRadialGradient(center, radius, gradientColors, SKShaderTileMode.Clamp);
using var bgPaint = new SKPaint
{
IsAntialias = true,
Shader = shader,
};
canvas.DrawRect(0, 0, width, height, bgPaint);

// Circles
using var circlePaint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Fill,
};
foreach (var (x, y, r, color) in circles)
{
circlePaint.Color = color;
canvas.DrawCircle(x * width, y * height, r * Math.Min(width, height), circlePaint);
}

// Centered text
using var textPaint = new SKPaint
{
Color = SKColors.White,
IsAntialias = true,
};
using var font = new SKFont { Size = width * 0.12f };
canvas.DrawText("SkiaSharp", center.X, center.Y + font.Size / 3f, SKTextAlign.Center, font, textPaint);
}
}
97 changes: 97 additions & 0 deletions samples/Basic/UnoPlatform/SkiaSharpSample/DrawingPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<Page
x:Class="SkiaSharpSample.DrawingPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:skia="using:SkiaSharp.Views.Windows">

<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ToolboxPanel.Orientation" Value="Vertical" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="600" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ToolboxPanel.Orientation" Value="Horizontal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>

<skia:SKXamlCanvas x:Name="skiaView" PaintSurface="OnPaintSurface"
Background="Transparent"
ManipulationMode="All"
PointerPressed="OnPointerPressed"
PointerMoved="OnPointerMoved"
PointerReleased="OnPointerReleased"
PointerWheelChanged="OnPointerWheelChanged"
PointerExited="OnPointerExited" />

<!-- Floating Clear button — top-right -->
<Button Content="Clear"
Click="OnClearClicked"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="0,12,12,0"
Foreground="White"
FontSize="13"
Padding="16,6"
CornerRadius="16">
<Button.Background>
<SolidColorBrush Color="#1E1E1E" Opacity="0.8" />
</Button.Background>
</Button>

<!-- Floating toolbox — bottom-center overlay -->
<Border x:Name="ToolboxBorder"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,16"
CornerRadius="24"
Padding="20,14">
<Border.Background>
<SolidColorBrush Color="#1E1E1E" Opacity="0.8" />
</Border.Background>
<StackPanel x:Name="ToolboxPanel" Orientation="Horizontal" Spacing="14">
<!-- Color swatches -->
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
<Border Tapped="OnColorTapped" Tag="Black" Width="36" Height="36"
CornerRadius="18" Background="Black" />
<Border Tapped="OnColorTapped" Tag="Red" Width="36" Height="36"
CornerRadius="18" Background="#E53935" />
<Border Tapped="OnColorTapped" Tag="Blue" Width="36" Height="36"
CornerRadius="18" Background="#1E88E5" />
<Border Tapped="OnColorTapped" Tag="Green" Width="36" Height="36"
CornerRadius="18" Background="#43A047" />
<Border Tapped="OnColorTapped" Tag="Orange" Width="36" Height="36"
CornerRadius="18" Background="#FB8C00" />
<Border Tapped="OnColorTapped" Tag="Purple" Width="36" Height="36"
CornerRadius="18" Background="#8E24AA" />
</StackPanel>

<!-- Brush size slider + label -->
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center"
HorizontalAlignment="Center">
<Slider x:Name="brushSlider"
Minimum="1" Maximum="50" Value="4"
Width="160"
VerticalAlignment="Center"
ValueChanged="OnSliderValueChanged" />
<TextBlock x:Name="brushText" Text="4"
Foreground="White" FontSize="13"
VerticalAlignment="Center"
Width="24" />
</StackPanel>
</StackPanel>
</Border>
</Grid>

</Page>
Loading