Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SkiaSharp mega-sample #411

Merged
merged 6 commits into from
Jan 9, 2024
Merged
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
20 changes: 12 additions & 8 deletions .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ jobs:
}
}

# Only proceed when his project has not been built yet
if (-not ($processedProjects -contains $file)) {
if ([string]::IsNullOrEmpty($projectToBuild)) {
Write-Output "::warning:: Found no csproj for file $file"
}
else {
$projectToBuild = (Resolve-Path -Path $projectToBuild -Relative).Replace("\", "/")
if (-not [string]::IsNullOrEmpty($projectToBuild)) {
$projectToBuild = (Resolve-Path -Path $projectToBuild -Relative).Replace("\", "/")

# Only proceed when this project has not been built yet
if (-not ($processedProjects -contains $projectToBuild)) {
Write-Output "::notice:: $projectToBuild is not in processed builds yet"

$processedProjects += $projectToBuild
Write-Output "::notice:: Added $projectToBuild to processed builds"

if ($excluded_projects -contains $projectToBuild) {
Write-Output "::notice:: Skipping build for excluded project: $projectToBuild"
Expand All @@ -95,7 +97,6 @@ jobs:
Write-Output "::group:: Building $projectToBuild"

dotnet build $projectToBuild
$processedProjects += $projectToBuild

if ($LASTEXITCODE -gt 0) {
Write-Output "::error:: Build failed for $projectToBuild"
Expand All @@ -116,6 +117,9 @@ jobs:
}
}
}
else {
Write-Output "::warning:: Found no csproj for file $file"
}
}

if ($failedProjectCount -gt 0) {
Expand Down
20 changes: 20 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: .NET MAUI - SkiaSharp
description: "This sample demonstrates the use of SkiaSharp in a .NET MAUI app."
page_type: sample
languages:
- csharp
- xaml
products:
- xamarin
- dotnetmaui
urlFragment: skiasharpmaui-demos
---

# .NET MAUI and SkiaSharp

SkiaSharp is a 2D graphics system for .NET and C# powered by the open-source Skia graphics engine that's used extensively in Google products. You can use SkiaSharp in your .NET Multi-platform App UI (.NET MAUI) apps to draw 2D vector graphics, bitmaps, and text.

This sample demonstrates the use of SkiaSharp in a .NET MAUI app.

[!INCLUDE [Install SkiaSharp](../includes/install-skiasharp.md)]
26 changes: 26 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharpDemos", "SkiaSharpDemos\SkiaSharpDemos.csproj", "{63B3F69B-D30F-480D-B914-7390F2D1F52E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{63B3F69B-D30F-480D-B914-7390F2D1F52E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63B3F69B-D30F-480D-B914-7390F2D1F52E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63B3F69B-D30F-480D-B914-7390F2D1F52E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{63B3F69B-D30F-480D-B914-7390F2D1F52E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63B3F69B-D30F-480D-B914-7390F2D1F52E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9E97E4D6-1629-4123-95D7-1B7108AD826A}
EndGlobalSection
EndGlobal
27 changes: 27 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SkiaSharpDemos"
x:Class="SkiaSharpDemos.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>

<Style TargetType="StackLayout">
<Setter Property="Spacing" Value="6" />
</Style>
<Style TargetType="Grid">
<Setter Property="ColumnSpacing" Value="6" />
<Setter Property="RowSpacing" Value="6" />
</Style>
<Style TargetType="HorizontalStackLayout">
<Setter Property="Spacing" Value="6" />
</Style>

</ResourceDictionary>
</Application.Resources>
</Application>

12 changes: 12 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace SkiaSharpDemos;

public partial class App : Application
{
public App()
{
InitializeComponent();

MainPage = new AppShell();
}
}

16 changes: 16 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/AppShell.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="SkiaSharpDemos.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SkiaSharpDemos"
Shell.FlyoutBehavior="Disabled"
Title="SkiaSharpDemos">

<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />

</Shell>

10 changes: 10 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/AppShell.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace SkiaSharpDemos;

public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}

20 changes: 20 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/BasePage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Windows.Input;

namespace SkiaSharpDemos
{
public class BasePage : ContentPage
{
public ICommand NavigateCommand { get; private set; }

public BasePage()
{
NavigateCommand = new Command<Type>(async (Type pageType) =>
{
Page page = (Page)Activator.CreateInstance(pageType);

Check warning on line 13 in 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/BasePage.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Converting null literal or possible null value to non-nullable type.

Check warning on line 13 in 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/BasePage.cs

View workflow job for this annotation

GitHub Actions / build (macos-13)

Converting null literal or possible null value to non-nullable type.
await Navigation.PushAsync(page);
});

BindingContext = this;
}
}
}
128 changes: 128 additions & 0 deletions 8.0/SkiaSharp/SkiaSharpDemos/SkiaSharpDemos/Basics/BasicBitmapsPage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using System.Reflection;
using SkiaSharp;
using SkiaSharp.Views.Maui;
using SkiaSharp.Views.Maui.Controls;

namespace SkiaSharpDemos.Basics
{
public class BasicBitmapsPage : ContentPage
{
SKCanvasView canvasView;

HttpClient httpClient = new HttpClient();

SKBitmap webBitmap;
SKBitmap resourceBitmap;
SKBitmap libraryBitmap;

public BasicBitmapsPage()
{
Title = "Basic Bitmaps";

canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;

// Load resource bitmap
Assembly assembly = GetType().GetTypeInfo().Assembly;

using (Stream stream = assembly.GetManifestResourceStream("SkiaSharpDemos.Media.monkey.png"))
{
resourceBitmap = SKBitmap.Decode(stream);
}

// Add tap gesture recognizer
TapGestureRecognizer tapRecognizer = new TapGestureRecognizer();
tapRecognizer.Tapped += async (sender, args) =>
{
// Load bitmap from photo library
FileResult photo = await MediaPicker.Default.PickPhotoAsync();
if (photo != null)
{
using (Stream stream = await photo.OpenReadAsync())
{
if (stream != null)
{
libraryBitmap = SKBitmap.Decode(stream);
canvasView.InvalidateSurface();
}
}
}
};
canvasView.GestureRecognizers.Add(tapRecognizer);
}

protected override async void OnAppearing()
{
base.OnAppearing();

// Load web bitmap.
string url = "https://learn.microsoft.com/en-us/dotnet/maui/media/what-is-maui/maui-overview.png";

try
{
using (Stream stream = await httpClient.GetStreamAsync(url))
using (MemoryStream memStream = new MemoryStream())
{
await stream.CopyToAsync(memStream);
memStream.Seek(0, SeekOrigin.Begin);

webBitmap = SKBitmap.Decode(memStream);
canvasView.InvalidateSurface();
}
}
catch
{
}
}

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;

canvas.Clear();

if (webBitmap != null)
{
float x = (info.Width - webBitmap.Width) / 2;
float y = (info.Height / 3 - webBitmap.Height) / 2;
canvas.DrawBitmap(webBitmap, x, y);
}

if (resourceBitmap != null)
{
canvas.DrawBitmap(resourceBitmap,
new SKRect(0, info.Height / 3, info.Width, 2 * info.Height / 3));
}

if (libraryBitmap != null)
{
float scale = Math.Min((float)info.Width / libraryBitmap.Width,
info.Height / 3f / libraryBitmap.Height);

float left = (info.Width - scale * libraryBitmap.Width) / 2;
float top = (info.Height / 3 - scale * libraryBitmap.Height) / 2;
float right = left + scale * libraryBitmap.Width;
float bottom = top + scale * libraryBitmap.Height;
SKRect rect = new SKRect(left, top, right, bottom);
rect.Offset(0, 2 * info.Height / 3);

canvas.DrawBitmap(libraryBitmap, rect);
}
else
{
using (SKPaint paint = new SKPaint())
{
paint.Color = SKColors.Blue;
paint.TextAlign = SKTextAlign.Center;
paint.TextSize = 48;

canvas.DrawText("Tap to load bitmap", info.Width / 2, 5 * info.Height / 6, paint);
}
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8" ?>
<local:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SkiaSharpDemos"
x:Class="SkiaSharpDemos.Basics.BasicsMenuPage"
Title="SkiaSharp Drawing Basics">
<TableView Intent="Menu">
<TableRoot>
<TableSection Title="Drawing a Simple Circle">
<TextCell Text="Simple Circle"
Detail="Outline and fill a circle with SkiaSharp"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.SimpleCirclePage}" />
</TableSection>
<TableSection Title="Integrating with .NET MAUI">
<TextCell Text="Tap Toggle Fill"
Detail="Tap the screen to toggle the circle fill"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.TapToggleFillPage}" />
<TextCell Text="Color Explore"
Detail="Use sliders to select color"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.ColorExplorePage}" />
</TableSection>
<TableSection Title="Pixels and Device-Independent Units">
<TextCell Text="Surface Size"
Detail="Display the surface size three different ways"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.SurfaceSizePage}" />
<TextCell Text="Ellipse Fill"
Detail="Fill the surface with an ellipse"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.EllipseFillPage}" />
</TableSection>
<TableSection Title="Basic Animation">
<TextCell Text="Pulsating Ellipse"
Detail="Animate the axes of an ellipse"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.PulsatingEllipsePage}" />
<TextCell Text="Expanding Circles"
Detail="Animate circles to expand forever"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.ExpandingCirclesPage}" />
</TableSection>
<TableSection Title="Integrating Text and Graphics">
<TextCell Text="Framed Text"
Detail="Draw a rounded rectangle around a text string"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.FramedTextPage}" />
<TextCell Text="Outlined Text"
Detail="Outline text characters"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.OutlinedTextPage}" />
</TableSection>
<TableSection Title="Bitmap Basics">
<TextCell Text="Basic Bitmaps"
Detail="Load and display bitmaps"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.BasicBitmapsPage}" />
</TableSection>
<TableSection Title="Transparency">
<TextCell Text="Code More Code"
Detail="Animate the alpha channel to fade between text strings"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.CodeMoreCodePage}" />
<TextCell Text="Bitmap Dissolve"
Detail="Use a transparency to dissolve between one bitmap and another"
Command="{Binding NavigateCommand}"
CommandParameter="{x:Type local:Basics.BitmapDissolvePage}" />
</TableSection>
</TableRoot>
</TableView>
</local:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace SkiaSharpDemos.Basics;

public partial class BasicsMenuPage : BasePage
{
public BasicsMenuPage()
{
InitializeComponent();
}
}
Loading
Loading