Skip to content

Commit

Permalink
Add support for converting ImageSource to SKImage (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattleibow authored Sep 9, 2020
1 parent 6d1c064 commit d75db72
Show file tree
Hide file tree
Showing 26 changed files with 633 additions and 11 deletions.
File renamed without changes.
27 changes: 27 additions & 0 deletions SkiaSharp.Extended.sln
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Extended.UI.WPF",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharpDemo.WPF", "samples\SkiaSharpDemo.WPF\SkiaSharpDemo.WPF.csproj", "{97749245-E046-4722-947F-F618DE04512D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Extended.UI.WPF.Tests", "tests\SkiaSharp.Extended.UI.WPF.Tests\SkiaSharp.Extended.UI.WPF.Tests.csproj", "{0F0EA738-BDEA-4820-8FDD-953482D3754C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -653,6 +655,30 @@ Global
{97749245-E046-4722-947F-F618DE04512D}.Release|x64.Build.0 = Release|Any CPU
{97749245-E046-4722-947F-F618DE04512D}.Release|x86.ActiveCfg = Release|Any CPU
{97749245-E046-4722-947F-F618DE04512D}.Release|x86.Build.0 = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|ARM.Build.0 = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|iPhone.Build.0 = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|x64.ActiveCfg = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|x64.Build.0 = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|x86.ActiveCfg = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Debug|x86.Build.0 = Debug|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|Any CPU.Build.0 = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|ARM.ActiveCfg = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|ARM.Build.0 = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|iPhone.ActiveCfg = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|iPhone.Build.0 = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|x64.ActiveCfg = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|x64.Build.0 = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|x86.ActiveCfg = Release|Any CPU
{0F0EA738-BDEA-4820-8FDD-953482D3754C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -678,6 +704,7 @@ Global
{B5A95CCE-FF80-4ACA-AA49-F150C23C65D5} = {5555F827-12DF-4D15-BF07-3A720FC2EF3F}
{33B974F8-1076-4D66-A212-75614C888B26} = {5555F827-12DF-4D15-BF07-3A720FC2EF3F}
{97749245-E046-4722-947F-F618DE04512D} = {51B0C2C7-732B-4A5C-A4F2-55655D147866}
{0F0EA738-BDEA-4820-8FDD-953482D3754C} = {5555F827-12DF-4D15-BF07-3A720FC2EF3F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {08D78153-5DD7-4C52-A348-46AA448B2CFC}
Expand Down
10 changes: 7 additions & 3 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Task("libs")

var sln = IsRunningOnWindows()
? "./SkiaSharp.Extended.sln"
: "./SkiaSharp.Extended.macOS.sln";
: "./SkiaSharp.Extended.Unix.sln";

MSBuild(sln, settings);
});
Expand All @@ -27,7 +27,7 @@ Task("nugets")
{
var sln = IsRunningOnWindows()
? "./source/Source.sln"
: "./source/Source.macOS.sln";
: "./source/Source.Unix.sln";

MSBuild(sln, new MSBuildSettings()
.EnableBinaryLogger("./output/binlogs/nugets.binlog")
Expand Down Expand Up @@ -57,6 +57,10 @@ Task("tests")
var failed = 0;

foreach (var csproj in GetFiles("./tests/*/*.csproj")) {
// skip WPF on non-Windows
if (!IsRunningOnWindows() && csproj.GetFilename().FullPath.Contains(".WPF."))
continue;

try {
DotNetCoreTest(csproj.FullPath, new DotNetCoreTestSettings {
Configuration = "Release",
Expand Down Expand Up @@ -87,7 +91,7 @@ Task("samples")

var sln = IsRunningOnWindows()
? "./SkiaSharp.Extended.sln"
: "./SkiaSharp.Extended.macOS.sln";
: "./SkiaSharp.Extended.Unix.sln";

MSBuild(sln, settings);
});
Expand Down
67 changes: 67 additions & 0 deletions samples/SkiaSharpDemo/Demos/Helpers/ImageSourceToImagePage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:SkiaSharpDemo.Views"
x:Class="SkiaSharpDemo.Demos.ImageSourceToImagePage"
Title="ToImage">

<ScrollView>
<StackLayout Padding="12" Spacing="6" HeightRequest="-1">
<StackLayout.Resources>
<ResourceDictionary>
<Style TargetType="StackLayout">
<Setter Property="HeightRequest" Value="100" />
</Style>
<Style TargetType="Image">
<Setter Property="HorizontalOptions" Value="FillAndExpand" />
<Setter Property="WidthRequest" Value="100" />
</Style>
<Style TargetType="views:SKImageView">
<Setter Property="HorizontalOptions" Value="FillAndExpand" />
<Setter Property="WidthRequest" Value="100" />
</Style>
</ResourceDictionary>
</StackLayout.Resources>

<StackLayout Orientation="Horizontal" Spacing="12" HeightRequest="-1">
<Label Text="ImageSource"
FontAttributes="Bold" HorizontalTextAlignment="Center"
HorizontalOptions="FillAndExpand" WidthRequest="100" />
<Label Text="SkiaSharp"
FontAttributes="Bold" HorizontalTextAlignment="Center"
HorizontalOptions="FillAndExpand" WidthRequest="100" />
</StackLayout>

<Label Text="File" />
<StackLayout Orientation="Horizontal" Spacing="12">
<Image Source="{Binding FileImage}" />
<views:SKImageView Source="{Binding FileImage}" />
</StackLayout>

<Label Text="Stream" />
<StackLayout Orientation="Horizontal" Spacing="12">
<Image Source="{Binding StreamImage}" />
<views:SKImageView Source="{Binding StreamImage}" />
</StackLayout>

<Label Text="Uri" />
<StackLayout Orientation="Horizontal" Spacing="12">
<Image Source="{Binding UriImage}" />
<views:SKImageView Source="{Binding UriImage}" />
</StackLayout>

<Label Text="Font" />
<StackLayout Orientation="Horizontal" Spacing="12">
<Image Source="{Binding FontImage}" />
<views:SKImageView Source="{Binding FontImage}" />
</StackLayout>

<Label Text="SkiaSharp" />
<StackLayout Orientation="Horizontal" Spacing="12">
<Image Source="{Binding SkiaSharpImage}" />
<views:SKImageView Source="{Binding SkiaSharpImage}" />
</StackLayout>

</StackLayout>
</ScrollView>

</ContentPage>
33 changes: 33 additions & 0 deletions samples/SkiaSharpDemo/Demos/Helpers/ImageSourceToImagePage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using SkiaSharp;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;

namespace SkiaSharpDemo.Demos
{
public partial class ImageSourceToImagePage : ContentPage
{
public ImageSourceToImagePage()
{
InitializeComponent();

BindingContext = this;
}

public ImageSource FileImage =>
new FileImageSource { File = "logo.png" };

public ImageSource StreamImage =>
new StreamImageSource { Stream = token => Task.Run(() => App.GetImageResourceStream("logo.png")) };

public ImageSource UriImage =>
new UriImageSource { Uri = new Uri("https://raw.githubusercontent.com/mono/SkiaSharp.Extended/main/images/logo.png") };

public ImageSource FontImage =>
new FontImageSource { Glyph = "S", FontFamily = "Arial", Color = Color.Black };

public ImageSource SkiaSharpImage =>
new SKBitmapImageSource { Bitmap = SKBitmap.Decode(App.GetImageResourceStream("logo.png")) };
}
}
7 changes: 7 additions & 0 deletions samples/SkiaSharpDemo/MainPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ public MainPage()
PageType = typeof(ConfettiPage),
Color = Color.SteelBlue,
},
new Demo
{
Title = "ToImage",
Description = "You have that ImageSource and you really want an actual image... What do you do? Well, you ToSKImageAsync that puppy!",
PageType = typeof(ImageSourceToImagePage),
Color = Color.Orange,
},
},
new DemoGroup("TEXT & EMOJI")
{
Expand Down
72 changes: 72 additions & 0 deletions samples/SkiaSharpDemo/Views/SKImageView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Diagnostics;
using SkiaSharp;
using SkiaSharp.Extended.UI;
using SkiaSharp.Extended.UI.Extensions;
using SkiaSharp.Views.Forms;
using Xamarin.Forms;

namespace SkiaSharpDemo.Views
{
public class SKImageView : SKCanvasView
{
private SKImage? image;

public static readonly BindableProperty SourceProperty = BindableProperty.Create(
nameof(Source),
typeof(ImageSource),
typeof(SKImageView),
null,
propertyChanged: OnSourceChanged);

public ImageSource? Source
{
get => (ImageSource?)GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}

protected override void OnPaintSurface(SKPaintSurfaceEventArgs e)
{
base.OnPaintSurface(e);

var info = e.Info;
var canvas = e.Surface.Canvas;

canvas.Clear(SKColors.Transparent);

if (image != null)
{
var rect = info.Rect.AspectFit(new SKSizeI(image.Width, image.Height));

canvas.DrawImage(image, rect);
}
}

private static async void OnSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is SKImageView view)
{
var source = newValue as ImageSource;

if (source == null)
view.image = null;
else
{
try
{
var img = await source.ToSKImageAsync();
view.image = img;
}
catch (Exception ex)
{
Debug.WriteLine($"Unable to load image: " + ex);

view.image = null;
}
}

view.InvalidateSurface();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

<PropertyGroup>
<!--<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>-->
<TargetFrameworks>net462</TargetFrameworks>
<TargetFrameworks>net462;netcoreapp3.1</TargetFrameworks>
<UseWpf>true</UseWpf>
<AssemblyName>SkiaSharp.Extended.UI</AssemblyName>
<RootNamespace>SkiaSharp.Extended.UI</RootNamespace>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<AssemblyFileVersion>1.0.0.0</AssemblyFileVersion>
<AssemblyVersion>2.0.0.0</AssemblyVersion>
<AssemblyFileVersion>2.0.0.0</AssemblyFileVersion>
<Version>2.0.0</Version>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using SkiaSharp.Views.Android;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.Android;

namespace SkiaSharp.Extended.UI.Extensions
{
public static partial class SKImageSourceExtensions
{
private static async Task<SKImage?> PlatformToSKImageAsync(ImageSource imageSource, CancellationToken cancellationToken = default)
{
var handler = Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(imageSource);
if (handler == null)
throw new InvalidOperationException($"Unable to determine the handler for the image source ({imageSource.GetType().Name}).");

using var bitmap = await handler.LoadImageAsync(imageSource, Android.App.Application.Context, cancellationToken).ConfigureAwait(false);
if (bitmap == null)
return null;

var image = bitmap.ToSKImage();

bitmap.Recycle();

return image;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
#if __MOBILE__
using SkiaSharp.Views.iOS;
using Xamarin.Forms.Platform.iOS;
#else
using SkiaSharp.Views.Mac;
using Xamarin.Forms.Platform.MacOS;
#endif

namespace SkiaSharp.Extended.UI.Extensions
{
public static partial class SKImageSourceExtensions
{
private static async Task<SKImage?> PlatformToSKImageAsync(ImageSource imageSource, CancellationToken cancellationToken = default)
{
var handler = Xamarin.Forms.Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(imageSource);
if (handler == null)
throw new InvalidOperationException($"Unable to determine the handler for the image source ({imageSource.GetType().Name}).");

using var nativeImage = await handler.LoadImageAsync(imageSource, cancellationToken).ConfigureAwait(false);
if (nativeImage == null)
return null;

var image = nativeImage.ToSKImage();

return image;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace SkiaSharp.Extended.UI.Extensions
{
public static partial class SKImageSourceExtensions
{
private static Task<SKImage?> PlatformToSKImageAsync(ImageSource imageSource, CancellationToken cancellationToken = default) =>
throw new NotImplementedException();
}
}
Loading

0 comments on commit d75db72

Please sign in to comment.