A little back story…
Few months back our company was asked to do a graphics application, so we decided to take a look into graphics rendering libraries available for Xamarin.Forms, given the limited time, we thought of going for SkiaSharp over other alternatives, which we had very little knowledge of how to work with.
But to our surprise we managed to build an incredible app with beautiful interactive graphics and animations completely using SkiaSharp with Xamarin.Forms. So I thought of sharing my experience with the fellow dev community. 😀
Opportunity…
So few weeks back (18th June, 2017), I had the opportunity to give a tech talk-hands on demos, at Singapore Mobile .Net Developers meetup, under the topic “2D Graphics Rendering in Xamarin.Forms with SkiaSharp”!
So I’m about to share some of the stuff I presented at this meetup, although I will not be diving into every single detail I talked about there, only be focusing on the key points (mostly on the hands on demo bits). If you’re interested in learning SkiaSharp for Xamarin.Forms, go ahead to the the incredible documentation provided by Xamarin: https://developer.xamarin.com/skiasharp/
Here’s the short recap of the presentation I did over there! 😉
2D Graphics Rendering in Xamarin.Forms with SkiaSharp!
So let’s get started off with the Slideshow Presentation…
And you may grab the live hands on demo code I did at the presentation from my github repo: https://github.com/UdaraAlwis/XFSkiaSharpDemo
Now let’s recap…
Behold the incredible 2D Rendering Engine for Xamarin and Xamarin.Forms, SkiaSharp!
An open source project originally developed by Google(Thank you <3), from C++ language, by the name Skia. It is used across a huge variety of Google’s products, including web graphics rendering and so on. This is a Immediate mode 2D vector graphics rendering system, this framework allows you to do 2D graphics, handling and manipulating image resources and text and a lot of cool stuff. 😀
So SkiaSharp is the C# and DotNet wrapper of Skia framework allowing us to use it right on top of Xamarin, a mono based open source project, where you could add your own contribution to it via: github.com/mono/SkiaSharp!
SkiaSharp for Xamarin.Forms comes with the SKCanvasView that inherits from Xamarin.Forms.View which allows you to use it as just another View in your PCL code, and you don’t have to handle any native implementation, everything is accomplished right in your PCL code. 😉
SkiaSharp basics Demo..
For setting up SkiaSharp, open your nuget manager and install “SkiaSharp.Views.Forms” across your Xamarin.Forms solution, including PCL and platform specific projects.
Add the SKCanvasView to your page as you wish.
<ContentPage
x:Class="XFSkiaSharpDemo.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:forms="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
xmlns:local="clr-namespace:XFSkiaSharpDemo">
<forms:SKCanvasView x:Name="SkCanvasView" PaintSurface="SkCanvasView_OnPaintSurface" />
</ContentPage>
Notice the PaintSurface event, the most important execution point you need to handle in order to render your graphics on the SKCanvas. Every time you need to do any kind of a drawing or rendering of 2D graphics on your Canvas, you need to do it in this event, this method is first invoked when the Page appears on the screen, and then if the orientation changes or you could even manually invoke it by calling InvalidateSurface() of your SkCanvasView.
Let’s do that…
public partial class
MainPage : ContentPage
{
...
private void SkCanvasView_OnPaintSurface
(object sender, SKPaintSurfaceEventArgs e)
{
// Init skcanvas
SKImageInfo skImageInfo = e.Info;
SKSurface skSurface = e.Surface;
SKCanvas skCanvas = skSurface.Canvas;
// clear the canvas surface
skCanvas.Clear(SKColors.SkyBlue);
// retrieve the canvas info
var skCanvasWidth = skImageInfo.Width;
var skCanvasheight = skImageInfo.Height;
}
}
This event provides you with all the required properties and values to execute your 2D rendering, such as the SKCanvas instance, which is the actual canvas you’re going to do the 2D drawing on, SKImageInfo instance which provides you with details such as actual Width and Height by pixels and so on.
The Clear() method call, clears up the canvas surface and prepare it for rendering new content, by passing it a SKColor object, you can paint it with that color.
2D Graphics with SkiaSharp..
The SKCanvasView is actually a placeholder for the SKCanvas which you can access in the PainSurface() event.
There’s many ways to draw or render stuff on our Canvas, but SkiaSharp also provides us predefined methods that allows us to draw simple types of shapes such as Circles, Lines and Texts, etc.
So usually when you are to do some complex drawings you would be using a combination of all those drawing methods at a given rendering cycle.
Transform Operations…
SkiaSharp allows you to do all kinds of Translations, Scaling, Rotating and even Skewing on the Canvas.
Usually on the Canvas, the X,Y coordinate system starts from the top left most corner and Y axis increments vertically and X axis increments horizontally.
So lets see how we could manipulate this in our favor and do some basic Translation and Scaling on the Canvas.
private void SkCanvasView_OnPaintSurface
(object sender, SKPaintSurfaceEventArgs e)
{
...
// move canvas's X,Y to center of screen
skCanvas.Translate((float)skCanvasWidth / 2,
(float)skCanvasheight / 2);
// set the pixel scale of the canvas
skCanvas.Scale(skCanvasWidth / 200f);
}
There we are Translating the Canvas’s X,Y coordinate system to be started off of the center of the screen, and then Scaling the Canvas to the ratio of 200 pixels according to the actual canvas Width.
SKPaint object..
SKPaint object is one of the most important element in SkiaSharp, it holds the configuration for any given type of 2D rendering, so you’ll be storing your drawing configuration in that object, such as Color, Style, Stroke Width/Height, Anti Alias and so on.
SKPaint skPaint = new SKPaint()
{
Style = SKPaintStyle.Fill,
IsAntialias = true,
Color = SKColors.Blue,
};
There’s how you instantiate a SKPaint object which you’ll using to render your 2D graphics, it’s got all kinds of drawing properties and configurations you can play around with. 🙂
Draw a simple Circle (Filled and Non-Filled)
Let’s get our hands dirty with some actual 2D drawing eh! 😉
// Drawing a Circle
using (SKPaint skPaint = new SKPaint())
{
skPaint.Style = SKPaintStyle.Fill;
skPaint.IsAntialias = true;
skPaint.Color = SKColors.Blue;
skPaint.StrokeWidth = 10;
skCanvas.DrawCircle(0, 0, 50, skPaint);
}
...
// Drawing a Circle Stroke
using (SKPaint skPaint = new SKPaint())
{
skPaint.Style = SKPaintStyle.Stroke;
skPaint.IsAntialias = true;
skPaint.Color = SKColors.Red;
skPaint.StrokeWidth = 10;
skCanvas.DrawCircle(0, 0, 70, skPaint);
}
We shall be using the DrawCircle() whilst passing in the Circle’s center XY position and desired radius for it. To define whether its a Filled or Non-Filled circle we’ll be using Style property in our SKPaint configuration.
Look how simple and beautiful eh 😉
Since SkiaSharp support pure Xamarin.Forms you can straight away run all your native projects without any hassle of handling native code.
To learn more about drawing on the Canvas you can check out the official Documentation: https://developer.xamarin.com/guides/cross-platform/drawing/
Handling User Interactions…
When it comes to most Xamarin.Forms components, they do not have touch handlers, however the SKCanvasView comes default with a Touch event handler, Touch and a boolean property to enable or disable Touch Events, EnableTouchEvents.
You can straightaway use that even and property to handle touch events on the SKCanvas.
<forms:SKCanvasView x:Name="SkCanvasView"
EnableTouchEvents="True"
Touch="SkCanvasView_Touch"
PaintSurface="SkCanvasView_OnPaintSurface" />
You can subscribe to it and look for the type of touch event and handle it.
private void SkCanvasView_Touch(
object sender, SKTouchEventArgs e)
{
if (e.ActionType ==
SkiaSharp.Views.Forms.SKTouchAction.Pressed)
{
_lastTouchPoint = e.Location;
e.Handled = true;
}
_lastTouchPoint = e.Location;
// update the Canvas as you wish
SkCanvasView.InvalidateSurface();
}
As you can see it gives you the Touch point location. You can get a hold of the event and the touch point and you want to do some drawing on the SKCanvasView, then you could call the InvalidateSurface().
private SKPoint _lastTouchPoint = new SKPoint();
private void SkCanvasView_OnPaintSurface
(object sender, SKPaintSurfaceEventArgs e)
{
...
using (SKPaint paintTouchPoint = new SKPaint())
{
paintTouchPoint.Style = SKPaintStyle.Fill;
paintTouchPoint.Color = SKColors.Red;
skCanvas.DrawCircle(
_lastTouchPoint.X,
_lastTouchPoint.Y,
50, paintTouchPoint); // 45
}
}
Here it is in action… pretty simple eh! 😉
But this touch handler is very primitive, as in if you want to handle multiple concurrent touch points, or special gesture touches, pan, or zoom and so on, then you need to implement a more advanced low level touch handler, something described as here:
https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/effects/touch-tracking/
That way you could simply attach the above TouchEffect just as a normal effect and see the complex touch events in action.
<Grid>
<skia:SKCanvasView x:Name="SkCanvasView"
PaintSurface="SkCanvasView_OnPaintSurface" />
<Grid.Effects>
<tt:TouchEffect Capture="True"
TouchAction="OnTouchEffectAction" />
</Grid.Effects>
</Grid>
There you go! 😀
Bitmap Image Handling….
Images are pretty crucial when it comes to 2D Graphics, it gives more of added advantage over your design idea.
As of Xamarin.Forms, the conventional the conventional way of loading an image is, either as an Embedded Resource or Platform Specific Resource.
So in SkiaSharp for Xamarin.Forms, provides you SKBitmap or SKImage for handling your image resources. You have few options to load an image, from a data stream, file path and so on.
The most common way in the sense of Xamarin.Forms architecture, you have the option of loading your Images directly from PCL as Embedded Resources, and then convert it to a SKBitmap or SKImage.
string resourceID = "XFSkiaSharpDemo.Resources.xamarinmonkey.png";
Assembly assembly = GetType().GetTypeInfo().Assembly;
SKBitmap skBitmap;
using (Stream stream
= assembly.GetManifestResourceStream(resourceID))
using (SKManagedStream skStream
= new SKManagedStream(stream))
{
skBitmap = SKBitmap.Decode(skStream);
}
skCanvas.DrawBitmap(skBitmap,
SKRect.Create(-50, -50, 100, 100), null);
There you have it, we are using the DrawBitmap() method for drawing the image on canvas.
But if you have a Xamarin.Forms ImageSource at hand and you need to use in SKCanvas, then you have convert it a Stream object and convert it to SKBitmap, which you could use to manipulate or draw using SkiaSharp on the Canvas. 😉
Image Filters..
Thanks to SkiaSharp you don’t have to manually implement image filters at all, since it packs a pretty cool set of Image Filters out of the box. 😀
Here’s a small sample of a blur image filter implementation…
// built-it blur image Filter
var filter = SKImageFilter.CreateBlur(5, 5);
var skPaint = new SKPaint();
skPaint.ImageFilter = filter;
skCanvas.DrawBitmap(skBitmap,
SKRect.Create(-50, -50, 100, 100), null);
SKImageFilters is the class that provides the built in filters. 🙂 You attach that object to a SKPaint configuration and draw the Bitmap with it!
Keep in mind, there’s a lot more default Image Filters you could play around with! 😉
*drum beat*! 😀
Rendering Animations…
Although Xamarin.Forms packs some pretty decent set of Animations out of the box, we don’t much control over the animation for customization.
But using something like a 2D Rendering Engine, we could create whatever the animation or customization as we wish. SkiaSharp of course is a great option, but that being said, there’s no direct Animation handling available. Because it’s simply a 2D vector rendering engine.
So this means if you want to render some continuous animation with SkiaSharp, you need to handle every single frame of it manually from your code.
So by actual implementation there’s few ways to do this, but the actual underlying idea is to repeatedly render a given set of values on the Canvas, preferably triggered by a continuous timer of sorts.
Stopwatch stopwatch = new Stopwatch();
bool pageIsActive;
float t;
const double cycleTime = 1000; // in milliseconds
private void InitAnimation()
{
pageIsActive = true;
stopwatch.Start();
Device.StartTimer(TimeSpan.FromMilliseconds(33), () =>
{
// calculate t for current
// tick with regards to cycletime
t = (float)(stopwatch.Elapsed.TotalMilliseconds
% cycleTime / cycleTime);
// invoke redraw on canvas
SkCanvasView.InvalidateSurface();
if (!pageIsActive)
{
stopwatch.Stop();
}
return pageIsActive;
});
}
The above shows you you could create a simple continuous pulse generator relative to milliseconds and execute a continuous animation. In simple terms the Timer is running each 33 milliseconds, calculates a value (t) based on the total elapsed milliseconds on the stopwatch, relative to the cycle time (controls the speed of animation) and repeats. Then calls the SKCanvas redraw. Make sure to call this method on PageAppearing() to start the timer and set the pageIsActive = false on PageDisappearing() to the timer stops.
private void SkCanvasView_OnPaintSurface
(object sender, SKPaintSurfaceEventArgs e)
{
...
// calculate circle radius for this cycle
float radius = 70 * t;
// Drawing a Circle Stroke
using (SKPaint skPaint = new SKPaint())
{
skPaint.Style = SKPaintStyle.Stroke;
skPaint.IsAntialias = true;
skPaint.Color = SKColors.Red;
skPaint.StrokeWidth = 10;
skCanvas.DrawCircle(0, 0, radius, skPaint);
}
}
There as you can see we are drawing the Circle at the given rendering cycle with relative to the generate “t” value at the Timer. So the Circle’s radius will keep on varying from 0 – 70, thus creating the animation effect.
Now keep in mind there’s nothing to worry about the rendering performance, since SkaiSharp is a rendering engine. 🙂 You can configure the animation even more faster as you wish, it wouldn’t make much effect on app’s performance! 😉
More Awesome Stuff…
If you want to learn more, check out Xamarin official documentation: https://developer.xamarin.com/guides/skiasharp/
If you need to check out sample code and demos : https://developer.xamarin.com/SkiaSharpFormsDemos/
This presentation’s demo on github…
That’s right, you can get the full demo code I’ve showcased in the presentation up in my github: https://github.com/UdaraAlwis/XFSkiaSharpDemo
I haven’t shared all the demo code I’ve presented in this blog post, but you call find all of the demo code from my git repo above! 🙂
Conclusion…
Yep that’s pretty much it, just get out of here and build something awesome with SkiaSharp! 😉
Share the love! 😀
Cheers!
– Udara Alwis