Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
214 commits
Select commit Hold shift + click to select a range
09663c7
Update to .NET 10 SDK and restructure TFMs
mattleibow Feb 11, 2026
1f9d00c
Fix SDK version pinning and update Docker images to .NET 10
mattleibow Feb 11, 2026
10d7642
Remove workloadVersion from global.json
mattleibow Feb 11, 2026
b98548c
Fix workload install: use explicit workload IDs instead of restore
mattleibow Feb 11, 2026
61264c6
Pin workload set version via pipeline variable
mattleibow Feb 11, 2026
2b7c5d5
Fix Tizen workload install for .NET 10 SDK
mattleibow Feb 11, 2026
fd790f2
Fix Apple TPVs for .NET 10 and SupportedOSPlatformVersion minimums
mattleibow Feb 11, 2026
9cba20b
Fix Tizen workload: install before official workloads
mattleibow Feb 11, 2026
b215a61
Fix Tizen workload-set conflict, Windows SDK version, and Apple TPV f…
mattleibow Feb 11, 2026
f484d0c
Retrigger CI: tvOS agent died (infra issue)
mattleibow Feb 12, 2026
d26508d
Fix Tizen workload-set mode and use net9 TFMs for Apple device tests
mattleibow Feb 12, 2026
74b6afc
Add Tizen workload diagnostics: manifest locations and workload list
mattleibow Feb 12, 2026
2283745
Reuse native artifacts from build 155857 and fix test projects
mattleibow Feb 12, 2026
0b0cdc5
Fix buildExternals: use build 155848 (all native stages passed)
mattleibow Feb 12, 2026
67748c0
Fix Tizen TPVs: 7.0 → 8.0 (Previous) and 10.0 (Current)
mattleibow Feb 12, 2026
e24534d
Fix workload install and WASM test type conflict
mattleibow Feb 12, 2026
a9c8b1e
Fix WASM P/Invoke: align return type with binding (int, not uint)
mattleibow Feb 12, 2026
236a735
Remove Tizen from MAUI target frameworks
mattleibow Feb 12, 2026
4ef8a15
Fix MAUI Compatibility.Hosting removal and Tizen sample api-version
mattleibow Feb 12, 2026
e7e0f6f
Fix Blazor static assets compression, API diff VERSIONS.txt, and Appl…
mattleibow Feb 12, 2026
89d2f70
Fix Apple sample TFMs: use explicit TPV 18.0 with net9.0
mattleibow Feb 12, 2026
e5d433b
Fix iOS sample SupportedOSPlatformVersion: 11.0 → 12.2
mattleibow Feb 12, 2026
f454427
Fix AssertSimilar to use rounding instead of truncation
mattleibow Feb 12, 2026
ae01b58
Trigger CI rebuild with AssertSimilar fix
mattleibow Feb 12, 2026
025c201
Update .NET upgrade documentation with lessons learned
mattleibow Feb 12, 2026
25e8bcc
Remove disallowed nuget.org source from nuget.config
mattleibow Feb 12, 2026
f7de54b
Remove TypeScriptCompileBlocked workaround - test if still needed
mattleibow Feb 12, 2026
64ce8be
Update Microsoft.TypeScript.MSBuild to 5.9.3
mattleibow Feb 12, 2026
0759a4f
Add pre-merge checklist and nuget.org guidance to docs
mattleibow Feb 12, 2026
a7b7026
Reduce float comparison precision for colorspace tests
mattleibow Feb 13, 2026
cdd3eb8
Fix workload install script error handling and remove nuget.org
mattleibow Feb 13, 2026
5f7efb1
Simplify Tizen workload error handling with LASTEXITCODE
mattleibow Feb 13, 2026
7b3ccc2
Use bash -e to force exit on error in Tizen install
mattleibow Feb 13, 2026
5308fdf
fix: pass explicit version band for Tizen workload on .NET 10
mattleibow Feb 13, 2026
5b03dff
fix: simplify Tizen install by extracting manifest directly
mattleibow Feb 13, 2026
1b55907
refactor: simplify workload install script
mattleibow Feb 13, 2026
6fec363
Simplify extraction of Tizen manifest package
mattleibow Feb 13, 2026
19be11a
fix: update sample SupportedOSPlatformVersion minimums
mattleibow Feb 13, 2026
a43eabc
fix: restore TypeScriptCompileBlocked for Blazor build race condition
mattleibow Feb 13, 2026
124ec5a
Try building on macOS 15
mattleibow Feb 13, 2026
e531e78
Try building with the new TFMs
mattleibow Feb 13, 2026
356163d
Just build the latest for device tests
mattleibow Feb 13, 2026
e4940fd
Update xharness to 11.0.0 and iOS simulator to 26.2
mattleibow Feb 13, 2026
bbeb752
Skip Uno sample in CI (needs nuget.org) and fix tvOS MinimumOSVersion
mattleibow Feb 13, 2026
2ed3aef
Re-enable Uno sample and update package versions
mattleibow Feb 13, 2026
064b996
Fix Uno 6.5 migration: SkiaHostBuilder -> UnoPlatformHostBuilder
mattleibow Feb 13, 2026
d82ea9b
Migrate device tests from xharness to DeviceRunners CLI
mattleibow Mar 9, 2026
2dbbbac
Revert "Migrate device tests from xharness to DeviceRunners CLI"
mattleibow Mar 9, 2026
8be1707
Update DeviceRunners to 0.1.0-preview.6 and move Android tests to Linux
mattleibow Mar 9, 2026
3884c40
Add diagnostic logging to Android test build
mattleibow Mar 10, 2026
74fda4e
Add dotnetWorkloads param to override workload install
mattleibow Mar 10, 2026
64e1abf
Exclude iOS/macCatalyst MAUI TFMs on Linux
mattleibow Mar 10, 2026
88c4622
Remove test-device-runners NuGet feed
mattleibow Mar 10, 2026
befa571
Merge remote-tracking branch 'origin/main' into update/dotnet-10
mattleibow Mar 10, 2026
fea7ca8
Update Gtk4 sample to net10.0 TFM
mattleibow Mar 10, 2026
4d96132
Trigger CI rebuild for DeviceRunners package availability
mattleibow Mar 10, 2026
3d867b3
Bump xunit to 2.9.3 in device tests for DeviceRunners compatibility
mattleibow Mar 10, 2026
26ef27c
Add Android to PlatformTargetFrameworks on Linux
mattleibow Mar 10, 2026
16bc6f1
Fix Xcode version mismatch and Linux Android PlatformTargetFrameworks
mattleibow Mar 10, 2026
2399381
Add AVD debug logging for Android emulator issue
mattleibow Mar 10, 2026
375e687
Fix Android emulator AVD path on Linux (XDG mismatch)
mattleibow Mar 10, 2026
94fb6ef
Bump Apple TPVs to 26.2 and free disk for Android emulator
mattleibow Mar 10, 2026
431efbb
Retry CI (transient network failure on Win32 x64)
mattleibow Mar 10, 2026
7be1aa9
Pin workload set version 10.103.0 in global.json
mattleibow Mar 10, 2026
67a8354
Update Blazor sample with revamped pages from PR #3541
mattleibow Mar 6, 2026
f2e55e5
Update Android sample to 4-page multi-view layout
mattleibow Mar 6, 2026
344433f
Update iOS sample to 4-page multi-view layout
mattleibow Mar 6, 2026
c01aa9f
Update macOS sample to 4-page multi-view layout
mattleibow Mar 6, 2026
946f78c
Update tvOS sample to 3-page multi-view layout
mattleibow Mar 6, 2026
4964340
Update MacCatalyst sample to 3-page multi-view layout
mattleibow Mar 6, 2026
065c902
Update MAUI sample to 3-page multi-view layout
mattleibow Mar 6, 2026
e817158
Update WPF sample to 3-page multi-view layout
mattleibow Mar 6, 2026
7b19eb8
Update WinUI sample to 3-page multi-view layout
mattleibow Mar 6, 2026
19df959
Update WindowsForms sample to 3-page multi-view layout
mattleibow Mar 6, 2026
c8a99e9
Update UnoPlatform sample to 3-page multi-view layout
mattleibow Mar 6, 2026
41a6a45
Update Tizen sample to 3-page multi-view layout
mattleibow Mar 6, 2026
35e333a
Update Gtk3 sample to 2-page layout
mattleibow Mar 6, 2026
6e7aab5
Update Gtk4 sample to 2-page layout
mattleibow Mar 6, 2026
c2c1500
Use XML layout files for Android fragments
mattleibow Mar 6, 2026
22f20ea
Use storyboard for tvOS tab bar and view controllers
mattleibow Mar 6, 2026
d4b69ff
Use WinForms designer pattern for SkiaSharp views
mattleibow Mar 6, 2026
4e96ff1
Remove programmatic view recreation workarounds in UnoPlatform
mattleibow Mar 6, 2026
25d2d5b
Use Glade file for Gtk3 layout structure
mattleibow Mar 6, 2026
4b86e9e
Use UI file for Gtk4 layout structure
mattleibow Mar 6, 2026
ebc315a
Use storyboard for iOS split view and view controllers
mattleibow Mar 6, 2026
6896723
Use storyboard for MacCatalyst split view and view controllers
mattleibow Mar 6, 2026
1074395
Use storyboard for macOS split view and view controllers
mattleibow Mar 6, 2026
00966cc
Fix Android package version conflicts for Material design
mattleibow Mar 6, 2026
46ad674
Fix WinUI cross-thread crash and double navigation
mattleibow Mar 6, 2026
2b3ff5d
Fix UnoPlatform cross-thread crash, double navigation, and pointer exit
mattleibow Mar 6, 2026
4388974
Fix Gtk4 invisible canvases by setting Hexpand/Vexpand
mattleibow Mar 6, 2026
9f5862d
Fix MAUI per-frame shader leak and missing touch cancelled handler
mattleibow Mar 6, 2026
979d12d
Fix iOS and macOS SKPath resource leaks in drawing pages
mattleibow Mar 6, 2026
a8c6b46
Fix MacCatalyst render loop lifecycle and tvOS animation stutter
mattleibow Mar 6, 2026
843f0c9
Fix Android GPU shader error handling and navigation highlight
mattleibow Mar 6, 2026
7b99a5c
Fix Gtk3 smooth scrolling direction detection
mattleibow Mar 6, 2026
da87ebc
Fix Tizen path leaks, shader disposal, and hollow tap dots
mattleibow Mar 6, 2026
8ee2f46
Redesign Android sample with Material 3 theme and mobile drawing UI
mattleibow Mar 6, 2026
883adf5
Use Material 3 surface-style toolbar with light status/navigation bars
mattleibow Mar 6, 2026
27fc188
Android: add dark mode support with DayNight theme
mattleibow Mar 6, 2026
b6a0772
Android: use Material Theme Builder exported theme
mattleibow Mar 6, 2026
05abda7
Android: enable edge-to-edge display
mattleibow Mar 6, 2026
43969e1
Android: replace navigation drawer with M3 bottom navigation bar
mattleibow Mar 6, 2026
af2fc16
Android: file-scoped namespaces, floating pill UI overlays
mattleibow Mar 6, 2026
55cea76
Android: Material 3 Slider, circular swatches, polish
mattleibow Mar 6, 2026
ac2ae59
Android: float Clear button to top-right corner
mattleibow Mar 6, 2026
c7f39b2
Android: fix slider visibility and swatch clipping
mattleibow Mar 6, 2026
7950183
Android: upgrade to Material 1.12, remove unused DrawerLayout
mattleibow Mar 6, 2026
5bc55de
Android: use Noto Sans font on CPU page
mattleibow Mar 6, 2026
975b2f9
Android: scale CPU page text to 12% of canvas width
mattleibow Mar 6, 2026
5d47e34
Android: add subtle border to FPS pill and toolbox overlays
mattleibow Mar 6, 2026
1d311c8
Blazor WASM: complete visual redesign matching Android polish
mattleibow Mar 6, 2026
6c1d3ab
Blazor WASM: restore sidebar navigation with modern styling
mattleibow Mar 6, 2026
f6e7e2e
Blazor: migrate to Microsoft Fluent UI 2 design system
mattleibow Mar 6, 2026
4a2ef95
Blazor: replace Fluent UI with MudBlazor for responsive Material Design
mattleibow Mar 6, 2026
c70687e
Blazor: add themed drawer, nav spacing, and theme toggle button
mattleibow Mar 6, 2026
b82bd1e
Blazor Drawing: stack toolbox with colors above slider
mattleibow Mar 6, 2026
77a18b8
Blazor Drawing: dark-aware canvas, color swaps, and brush size label
mattleibow Mar 6, 2026
1f6244d
Blazor Drawing: align dark palette with Android Material 3
mattleibow Mar 6, 2026
9c14545
Add dark mode drawing palette to all platform samples
mattleibow Mar 6, 2026
a082fcd
Fix review issues: palette consistency, memory leaks, MacCatalyst rew…
mattleibow Mar 6, 2026
35637e7
MacCatalyst: create SkiaSharp views programmatically
mattleibow Mar 6, 2026
ae014fd
iOS: convert from storyboard to programmatic view creation
mattleibow Mar 7, 2026
d3bd5c0
MAUI: fix build errors (empty Icon attribute, missing using)
mattleibow Mar 7, 2026
4f62b81
Fix consistency: standard gradient colors and GPU shader across all p…
mattleibow Mar 7, 2026
7a2c9fb
Fix cross-platform consistency issues found in 3-model review
mattleibow Mar 7, 2026
a2234f2
Android: normalize brush slider range to 1-50 (was 1-30) with default 4
mattleibow Mar 7, 2026
0cfe649
MacCatalyst: fix GPU sidebar icon to use valid SF Symbol
mattleibow Mar 7, 2026
6f2dc56
Fix shader builder leak in GPU pages (Android, WinForms, WinUI, Uno)
mattleibow Mar 7, 2026
b6b21ac
Fix PaintSurface event subscription leaks in Apple platform samples
mattleibow Mar 7, 2026
985ee8e
MAUI: fix brush label text to match 4f default (was showing 8px)
mattleibow Mar 7, 2026
a09e566
Consistency fixes: add brush size labels (WinForms, GTK3), standardiz…
mattleibow Mar 7, 2026
83fc9da
Fix shader Uniforms indexer and macOS NeedsDisplay assignment
mattleibow Mar 7, 2026
6b9c017
Rewrite macOS sample to fully programmatic views
mattleibow Mar 7, 2026
4dcc8a6
Update Blazor screenshots with latest styling
mattleibow Mar 7, 2026
3c94c78
Add Android sample README with screenshots
mattleibow Mar 7, 2026
2ccf8d4
Add Blazor WebAssembly sample README with screenshots
mattleibow Mar 7, 2026
d2ad0d2
Add iOS sample README with screenshots
mattleibow Mar 7, 2026
be7df4e
Add Mac Catalyst sample README with screenshots
mattleibow Mar 7, 2026
54996dd
Add macOS sample README
mattleibow Mar 7, 2026
a5bdf34
Add MAUI sample README with screenshots
mattleibow Mar 7, 2026
beda1ff
Add tvOS sample README
mattleibow Mar 7, 2026
a02f9b2
Add GTK 3 sample README with screenshots
mattleibow Mar 7, 2026
c2d916f
Add GTK 4 sample README with screenshots
mattleibow Mar 7, 2026
e36ffe5
Add WPF sample README
mattleibow Mar 7, 2026
94d1a1b
Add WinUI 3 sample README
mattleibow Mar 7, 2026
f701175
Add Windows Forms sample README
mattleibow Mar 7, 2026
2cf43a0
Add Tizen sample README
mattleibow Mar 7, 2026
2d564c8
Add Uno Platform sample README
mattleibow Mar 7, 2026
33d1f92
macOS: restore storyboard as app entry point
mattleibow Mar 7, 2026
b9e8fa0
Fix Tizen README: correct SKRuntimeEffect API name
mattleibow Mar 8, 2026
39fe45d
macOS: fix window creation with programmatic NSWindow
mattleibow Mar 8, 2026
340dd50
Add macOS sample screenshots and update README
mattleibow Mar 8, 2026
95180a9
Fix rendering consistency across all samples
mattleibow Mar 8, 2026
3aba88c
Refactor Android sample: code style and shader uniforms
mattleibow Mar 8, 2026
e5a564d
Extract FpsCounter helper for Android GPU fragments
mattleibow Mar 8, 2026
45dc500
Use SKPoint for touch position in GPU fragments
mattleibow Mar 8, 2026
ebcdd99
Simplify shader builder access in GPU fragments
mattleibow Mar 8, 2026
73b4193
Use bool for touchActive in GPU fragments
mattleibow Mar 8, 2026
dec93fa
Refactor all samples: FpsCounter helper, shader uniforms, code style
mattleibow Mar 8, 2026
af44c06
Add SamplePage enum and DefaultPage property to all samples
mattleibow Mar 8, 2026
6ef8c3b
Fix Blazor font loading: cache typeface statically
mattleibow Mar 8, 2026
a228454
Align all CPU pages to Android reference pattern
mattleibow Mar 8, 2026
03a375a
Improve Gtk3 Drawing toolbar: circular swatches, slider, layout
mattleibow Mar 8, 2026
4204e52
Add Requirements section to all 18 sample READMEs
mattleibow Mar 8, 2026
0fa298d
Floating translucent toolbox for all Drawing pages
mattleibow Mar 8, 2026
6000bce
Use AdaptiveTrigger instead of OnIdiom for MAUI Drawing toolbox
mattleibow Mar 9, 2026
b37a387
Refactor Gtk3 sample into separate page files with Glade layouts
mattleibow Mar 9, 2026
dc324fe
Refactor Gtk4 sample into separate page files with UI layouts
mattleibow Mar 9, 2026
1ac86b2
Update Gtk3/Gtk4 screenshots and fix GTK4 Cairo assertion crash
mattleibow Mar 9, 2026
80af0e5
Refactor iOS sample to UITabBarController with storyboard-driven views
mattleibow Mar 9, 2026
4010709
Use DefaultPage static property instead of launch args for iOS tab se…
mattleibow Mar 9, 2026
c5c0ce0
MacCatalyst: tab bar navigation, 3 pages, light/dark screenshots
mattleibow Mar 9, 2026
650e0ce
Update macOS sample with storyboard tab bar, FPS pills, and dark mode…
mattleibow Mar 9, 2026
43849d6
Fix macOS GPU animation resume and drawing after tab switch
mattleibow Mar 9, 2026
9f488a0
Simplify GPU shader builder to readonly field initializer
mattleibow Mar 9, 2026
88543f2
Format SkSL shader code with proper indentation
mattleibow Mar 10, 2026
ceb8ff1
Update MAUI sample with FPS pill, dark mode swatches, and iOS screens…
mattleibow Mar 10, 2026
eee018f
Fix MAUI GPU render loop lifecycle and FPS pill styling
mattleibow Mar 10, 2026
b9b75e7
MAUI: Drawing toolbox border, selection ring, OnIdiom layout
mattleibow Mar 10, 2026
1622fe4
MAUI: Shell flyout icons, Grid toolbox, GPU lifecycle fixes
mattleibow Mar 10, 2026
d81cd71
Update tvOS sample: formatting, storyboard cleanup, README, screenshots
mattleibow Mar 10, 2026
d8f2b72
Fix Uno Platform sample: build error and README accuracy
mattleibow Mar 10, 2026
74498c4
Uno Platform: upgrade to SDK 5.6, LeftCompact nav, fix drawing colors
mattleibow Mar 10, 2026
217b6c2
Fix .NET 10 build: remove workload pin, tvOS simulator split, Uno pac…
mattleibow Mar 10, 2026
1592fe5
Uno Platform: top tabs for mobile, SkiaSharp app icons
mattleibow Mar 10, 2026
fed0126
Uno Platform: update README, remove unused SamplePage enum
mattleibow Mar 10, 2026
efb0860
Uno Platform: use pencil sharpener icon to match MAUI sample
mattleibow Mar 10, 2026
cde911b
Fix app icons: use foreground image with #F8F8F8 background
mattleibow Mar 10, 2026
f7cafe5
MAUI: scale app icon foreground to 80% for better padding
mattleibow Mar 10, 2026
58e11f2
MAUI: adjust icon foreground scale to 90%
mattleibow Mar 10, 2026
83c2372
MAUI: platform-conditional icon scaling (0.90 Android, 1.30 iOS)
mattleibow Mar 10, 2026
c0ede56
Samples: file-scoped namespaces and consistent formatting
mattleibow Mar 10, 2026
0bf6442
Uno Platform: add iOS screenshots for CPU, GPU, and Drawing pages
mattleibow Mar 11, 2026
f36bcf6
Merge origin/main into samples
mattleibow Mar 13, 2026
7ab6b7e
Fix Tizen build: replace removed Naviframe.TopItem with depth counter
mattleibow Mar 13, 2026
973ab15
Fix tvOS: split device/simulator native builds to match iOS pattern
mattleibow Mar 13, 2026
b1d446b
Fix tvOS GnNinja to use min_tvos_version instead of min_ios_version
mattleibow Mar 13, 2026
ac49d7b
Fix sample build failures: Uno duplicate PackageRef and WinForms dupl…
mattleibow Mar 13, 2026
50afe4b
Fix WinForms GpuPage.Designer.cs: remove non-existent 'effect' field …
mattleibow Mar 14, 2026
300218b
Merge remote-tracking branch 'origin/main' into samples
mattleibow Mar 14, 2026
308ba49
Cleanup: add nuget.org feed, update Uno packages, revert CI and Blazo…
mattleibow Mar 15, 2026
46d11aa
Revert Gallery/Uno samples to main (out of date, not building)
mattleibow Mar 15, 2026
43fbc65
Merge remote-tracking branch 'origin/main' into samples
mattleibow Mar 15, 2026
8b49cf1
Merge remote-tracking branch 'origin/main' into samples
mattleibow Mar 18, 2026
e496d26
Revert source/ Uno.WinUI to 5.2.175 (6.5.153 has breaking API changes)
mattleibow Mar 19, 2026
5500b2f
Update Uno.WinUI to 6.5.153 and remove net*-macos from Uno TFMs
mattleibow Mar 19, 2026
d7be36a
Revert Uno Platform changes (moved to PR #3565)
mattleibow Mar 19, 2026
c8f450e
Remove nuget.org feed (all packages available on dotnet-public)
mattleibow Mar 19, 2026
45330ce
Update WinUI sample: WASDK 1.8, top tabs, Maui parity, screenshots
mattleibow Mar 20, 2026
e7861ef
Update WPF sample: top tabs, Maui parity, screenshots
mattleibow Mar 20, 2026
1f92c96
WPF: Enable Fluent theme with system dark/light mode
mattleibow Mar 20, 2026
3ffdb88
WPF: Fix theme switch input, clean up code, retake screenshots
mattleibow Mar 20, 2026
4fa366b
Update WindowsForms sample: dark mode, tabs, Designer layouts, screen…
mattleibow Mar 20, 2026
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
81 changes: 81 additions & 0 deletions samples/Basic/Android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# SkiaSharp Android Sample

Demonstrates all SkiaSharp Android view types with Material 3 bottom navigation, dark/light theming, and touch interaction.

## Sample Pages

This sample shows how to integrate SkiaSharp views into an Android app using native XML layouts, AndroidX fragments, and Material 3 theming. Each view type can be placed in a layout file and configured in the Android designer, just like any other Android view.

### CPU

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

**Features:**

- **`SKCanvasView`** — Software-rendered canvas that draws each frame on the CPU, ideal for static or infrequently updated content.
- **`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.
- **`SKTypeface`** — Custom Noto Sans font loaded from an embedded asset via `SKTypeface.FromStream`.

### GPU (Surface)

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

**Features:**

- **`SKGLSurfaceView`** — Hardware-accelerated OpenGL ES canvas backed by Android's `SurfaceView`, suitable for full-screen GPU content.
- **`SKRuntimeEffect`** — SkSL metaball "lava lamp" shader compiled at runtime with `SKRuntimeEffect.BuildShader`.
- **Render loop** — Continuous animation driven by `Rendermode.Continuously` with an FPS counter overlay.
- **Touch interaction** — Touch position is passed as a shader uniform, adding a white-hot blob to the metaball field.

### GPU (Texture)

A real-time animated shader running at full frame rate on the GPU, rendered into a `TextureView` that supports transparency, transforms, and compositing within the view hierarchy.

**Features:**

- **`SKGLTextureView`** — Hardware-accelerated OpenGL ES canvas backed by Android's `TextureView`, which supports transparency, transforms, and compositing within the view hierarchy.
- **`SKRuntimeEffect`** — SkSL metaball "lava lamp" shader compiled at runtime with `SKRuntimeEffect.BuildShader`.
- **Render loop** — Continuous animation with an FPS counter overlay.
- **Touch interaction** — Touch position is passed as a shader uniform, adding a white-hot blob to the metaball field.

### Drawing

A freehand drawing canvas with a floating toolbox for choosing colors and brush sizes. Strokes persist across color and size changes.

**Features:**

- **`SKCanvasView`** — Software-rendered canvas used for on-demand drawing, invalidated after each stroke or clear.
- **`SKPath`** — Freehand strokes captured as paths with `MoveTo` and `LineTo` from touch events.
- **`SKTouchEventArgs`** — Pointer tracking for press, move, and release across the canvas.
- **Color palette** — Six selectable colors with dark/light mode variants.
- **Brush size** — Adjustable stroke width (1–50px) via a Material 3 slider.

## Requirements

- [.NET 8 SDK](https://dotnet.microsoft.com/download) or later
- Android workload: `dotnet workload install android`

## Running the Sample

Build and deploy to a connected device or emulator:

```bash
dotnet build -t:Install -f net8.0-android
```

To start on a different page, change `DefaultPage` in `MainActivity.cs`:

```csharp
public static SamplePage DefaultPage { get; set; } = SamplePage.GpuSurface;
```

Available pages: `Cpu` (default), `GpuSurface`, `GpuTexture`, `Drawing`

## Screenshots

| CPU | GPU (Surface) | GPU (Texture) | Drawing |
|---|---|---|---|
| <img src="screenshots/cpu-light.png" width="250" alt="CPU Light"> | <img src="screenshots/surface-light.png" width="250" alt="GPU Surface Light"> | <img src="screenshots/texture-light.png" width="250" alt="GPU Texture Light"> | <img src="screenshots/drawing-light.png" width="250" alt="Drawing Light"> |
| <img src="screenshots/cpu-dark.png" width="250" alt="CPU Dark"> | <img src="screenshots/surface-dark.png" width="250" alt="GPU Surface Dark"> | <img src="screenshots/texture-dark.png" width="250" alt="GPU Texture Dark"> | <img src="screenshots/drawing-dark.png" width="250" alt="Drawing Dark"> |
Binary file not shown.
96 changes: 96 additions & 0 deletions samples/Basic/Android/SkiaSharpSample/CpuFragment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System;
using Android.OS;
using Android.Views;
using AndroidX.Fragment.App;

using SkiaSharp;
using SkiaSharp.Views.Android;

namespace SkiaSharpSample;

public class CpuFragment : Fragment
{
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),
};

private SKCanvasView skiaView;
private SKTypeface typeface;

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.fragment_cpu, container, false);
skiaView = view.FindViewById<SKCanvasView>(Resource.Id.skiaView);
skiaView.PaintSurface += OnPaintSurface;

using var stream = Context.Assets.Open("Fonts/NotoSans-Regular.ttf");
typeface = SKTypeface.FromStream(stream);

return view;
}

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(typeface, width * 0.12f);
canvas.DrawText("SkiaSharp", center.X, center.Y + font.Size / 3f, SKTextAlign.Center, font, textPaint);
}

public override void OnDestroyView()
{
if (skiaView != null)
{
skiaView.PaintSurface -= OnPaintSurface;
skiaView = null;
}
typeface?.Dispose();
typeface = null;
base.OnDestroyView();
}
}
201 changes: 201 additions & 0 deletions samples/Basic/Android/SkiaSharpSample/DrawingFragment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Views;
using AndroidX.Fragment.App;
using Google.Android.Material.Button;

using SkiaSharp;
using SkiaSharp.Views.Android;

namespace SkiaSharpSample;

public class DrawingFragment : Fragment
{
private static readonly int[] SwatchIds = new[]
{
Resource.Id.colorBlack,
Resource.Id.colorRed,
Resource.Id.colorBlue,
Resource.Id.colorGreen,
Resource.Id.colorOrange,
Resource.Id.colorPurple,
};

private SKCanvasView skiaView;
private View selectedSwatch;
private Google.Android.Material.Slider.Slider brushSlider;
private readonly List<(SKPath Path, SKColor Color, float StrokeWidth)> strokes = new();
private SKPath currentPath;
private SKColor currentColor = SKColors.Black;
private SKColor canvasBackground = SKColors.White;

private float BrushSize => brushSlider?.Value ?? 4f;

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.fragment_drawing, container, false);

// Resolve theme surface color for canvas background
var typedValue = new Android.Util.TypedValue();
if (Context.Theme.ResolveAttribute(Resource.Attribute.colorSurface, typedValue, true))
{
var c = new Color(typedValue.Data);
canvasBackground = new SKColor((byte)c.R, (byte)c.G, (byte)c.B, (byte)c.A);
}

// Resolve theme onSurface color as default drawing color
if (Context.Theme.ResolveAttribute(Resource.Attribute.colorOnSurface, typedValue, true))
{
var c = new Color(typedValue.Data);
currentColor = new SKColor((byte)c.R, (byte)c.G, (byte)c.B, (byte)c.A);
}

skiaView = view.FindViewById<SKCanvasView>(Resource.Id.skiaView);
skiaView.PaintSurface += OnPaintSurface;
skiaView.Touch += OnTouch;

// Wire up color swatches — resolve SKColor from each swatch's background
foreach (var viewId in SwatchIds)
{
var swatch = view.FindViewById<View>(viewId);
SKColor bgColor = SKColors.Black;
if (OperatingSystem.IsAndroidVersionAtLeast(24)
&& swatch.Background is GradientDrawable gd && gd.Color != null)
{
var c = new Color(gd.Color.DefaultColor);
bgColor = new SKColor((byte)c.R, (byte)c.G, (byte)c.B);
}
else if (swatch.Background is ColorDrawable cd)
{
bgColor = new SKColor((byte)cd.Color.R, (byte)cd.Color.G, (byte)cd.Color.B);
}
var captured = bgColor;
swatch.Click += (s, e) =>
{
currentColor = captured;
SetSelectedSwatch(swatch);
};
}

// Select first swatch by default
selectedSwatch = view.FindViewById<View>(Resource.Id.colorBlack);
SetSelectedSwatch(selectedSwatch);

// Brush size slider
brushSlider = view.FindViewById<Google.Android.Material.Slider.Slider>(Resource.Id.brushSlider);

// Clear button
var clearBtn = view.FindViewById<MaterialButton>(Resource.Id.btnClear);
clearBtn.Click += (s, e) =>
{
foreach (var (path, _, _) in strokes)
path.Dispose();
strokes.Clear();
currentPath?.Dispose();
currentPath = null;
skiaView?.Invalidate();
};

return view;
}

private void SetSelectedSwatch(View swatch)
{
// Reset previous selection
if (selectedSwatch != null)
{
selectedSwatch.ScaleX = 1.0f;
selectedSwatch.ScaleY = 1.0f;
selectedSwatch.Elevation = 0;
}

// Highlight selected with scale and elevation
swatch.ScaleX = 1.3f;
swatch.ScaleY = 1.3f;
swatch.Elevation = 8;
selectedSwatch = swatch;
}

private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e)
{
var canvas = e.Surface.Canvas;
canvas.Clear(canvasBackground);

if (skiaView.Width <= 0 || skiaView.Height <= 0)
return;

using var paint = new SKPaint
{
IsAntialias = true,
Style = SKPaintStyle.Stroke,
StrokeCap = SKStrokeCap.Round,
StrokeJoin = SKStrokeJoin.Round,
};

float sx = (float)e.Info.Width / skiaView.Width;
float sy = (float)e.Info.Height / skiaView.Height;
canvas.Scale(sx, sy);

foreach (var (path, color, strokeWidth) in strokes)
{
paint.Color = color;
paint.StrokeWidth = strokeWidth;
canvas.DrawPath(path, paint);
}

if (currentPath != null)
{
paint.Color = currentColor;
paint.StrokeWidth = BrushSize;
canvas.DrawPath(currentPath, paint);
}
}

private void OnTouch(object sender, View.TouchEventArgs e)
{
var x = e.Event.GetX();
var y = e.Event.GetY();

switch (e.Event.Action)
{
case MotionEventActions.Down:
currentPath = new SKPath();
currentPath.MoveTo(x, y);
skiaView.Invalidate();
break;
case MotionEventActions.Move:
currentPath?.LineTo(x, y);
skiaView.Invalidate();
break;
case MotionEventActions.Up:
case MotionEventActions.Cancel:
if (currentPath != null)
{
strokes.Add((currentPath, currentColor, BrushSize));
currentPath = null;
skiaView.Invalidate();
}
break;
}
e.Handled = true;
}

public override void OnDestroyView()
{
if (skiaView != null)
{
skiaView.PaintSurface -= OnPaintSurface;
skiaView.Touch -= OnTouch;
skiaView = null;
}
foreach (var (path, _, _) in strokes)
path.Dispose();
strokes.Clear();
currentPath?.Dispose();
currentPath = null;
base.OnDestroyView();
}
}
Loading