February 6th, 2017

Cross-Platform Drawing with SkiaSharp

There are millions of mobile apps created every day that all do different things. However, they still have one thing in common: they will display some form of information to the user. Sometimes this will be a graph or a chart, sometimes it will be an interactive interface control. Sometimes it will just be plain text and sometimes it might be a fully interactive mobile game.

Each mobile platform has its own powerful graphics API, ranging from iOS’ CoreGraphics and Android’s graphics, to Windows’ media and imaging. There are also a variety of hardware accelerated graphics APIs with OpenGL, OpenGL ES, and DirectX. Although the various toolkits are powerful, they differ greatly in many important ways, making sharing logic and code almost impossible.

Even with advanced cross-platform libraries such as Xamarin.Forms, graphics processing and rendering is quite limited. This is where we introduce SkiaSharp.

Introducing SkiaSharp

SkiaSharpSkiaSharp is a fully cross-platform, rich 2D graphics drawing API powered by Google’s Skia library, the same library that drives Google Chrome, Mozilla Firefox, and Android’s graphics stack. Not only is SkiaSharp powerful, is’s also very simple to use—all you need to do is install the SkiaSharp NuGet.

SkiaSharp can be used to render images on many platforms, including iOS, macOS, tvOS, Android, and Universal Windows Platform (UWP). Not only does SkiaSharp support native mobile apps, it also supports .NET Standard 1.3 and .NET Core on macOS and Windows, as well as Classic Windows Desktop and WPF applications.

Installing SkiaSharp

Today, we’re going to look at creating a simple mobile app that demonstrates a few of the many SkiaSharp features. The source code for this app can be found on GitHub: mattleibow/SkiaSharpDemo.

We’re going to use XAML with Xamarin.Forms to create our app, but we could use any of the native Xamarin or Windows platforms. The app we create will draw shapes, paths, and text on the screen:

Final Drawing

To get started, let’s create a new Xamarin.Forms app in Xamarin Studio or Visual Studio. Once the app is created, install the SkiaSharp NuGet and the SkiaSharp.Views.Forms NuGet in all of the the projects.

The SkiaSharp NuGet contains the native rendering engine for all the platforms, but this is just for in-memory rendering. To display the rendered images on the screen, we can make use of the various views in the SkiaSharp.Views.Forms NuGet.

If we we weren’t using Xamarin.Forms, but rather going native, we would instead install the SkiaSharp.Views NuGet. This package contains views for all the platforms supported by SkiaSharp.

Adding SkiaSharp Views

Once the NuGets are installed, we can add the SkiaSharp view to the XAML. Here, we’re adding a SKCanvasView, the CPU-based drawing surface, but we could also use the SKGLView, the GPU-based drawing surface:

<ContentPage ... xmlns:views="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms">
    <Grid>
        <views:SKCanvasView PaintSurface="OnPainting" />
    </Grid>
</ContentPage>

Then we add the event handler in the code-behind for the page:

using SkiaSharp;
using SkiaSharp.Views.Forms;

private void OnPainting(object sender, SKPaintSurfaceEventArgs e)
{
    // we will draw here
}

Note: If we run our app now, we’ll see a plain screen and perhaps some artifacts or strange pixels. This is because we are not drawing anything yet, just rendering what was in memory last, which is random data.

Drawing with SkiaSharp

Now that we have the views in place, we can write the drawing code for the view. Drawing in SkiaSharp takes the form of three basic steps:

  1. Get a surface to hold the drawing
  2. Get a canvas to draw on
  3. Draw on the canvas with paint

When we use the SkiaSharp views, the first step (getting a surface) is done for us automatically, and we can simply query the surface for a canvas. The three main components are the SKSurface, the SKCanvas, and the SKPaint.

The SKSurface is the layer that directs drawing commands from SkiaSharp onto the underlying native drawing area. This area could be a memory location for software drawing, a framebuffer ID for an OpenGL framebuffer object, or a texture object. There are several ways to create this surface, either by creating a new surface or by using an existing memory location or framebuffer object. More information can be found by looking at the SKSurface.Create(...) APIs.

The next component is the SKCanvas. This is the layer that translates your drawing commands, such as DrawRect and DrawPath, into the appropriate commands for drawing on the current surface. Typically, you obtain the canvas from a surface directly using the SKSurface.Canvas property. The canvas has numerous methods, from drawing shapes (DrawRect, DrawOval, and DrawPath) to text (DrawText) and images (DrawBitmap and DrawBitmapLattice) to manipulating the drawing matrix and clipping (RotateDegrees, Skew, and ClipRect).

Finally, the SKPaint type is where you control what the drawing looks like. You can call the DrawRect method multiple times and by only changing the paint, the rectangle will look totally different. Some of the properties of SKPaint directly control the way things appear (IsAntialias, Color, Style, and StrokeWidth), some control how text is drawn (Typeface, UnderlineText, TextSize, and TextAlign), plus there are the shaders, mask filters, color filters, image filters, and blend modes.

Clearing the Canvas

In our OnPainting method, we first need to get the canvas for drawing, and then we can prepare the canvas by clearing it and painting it a nice white:

private void OnPainting(object sender, SKPaintSurfaceEventArgs e)
{
    // we get the current surface from the event args
    var surface = e.Surface;
    // then we get the canvas that we can draw on
    var canvas = surface.Canvas;
    // clear the canvas / view
    canvas.Clear(SKColors.White);
}

When we run this code, we’ll see a white screen, which we can start drawing shapes on:

Final Drawing

Drawing Shapes

The first thing we’ll draw is a simple circle using the SKCanvas.DrawCircle(cx, cy, radius, paint) method. As you can see, this method has an SKPaint parameter, which we’ll use to draw a filled circle with a border:

// create the paint for the filled circle
var circleFill = new SKPaint {
    IsAntialias = true,
    Style = SKPaintStyle.Fill,
    Color = SKColors.Blue
};

// draw the circle fill
canvas.DrawCircle(100, 100, 40, circleFill);

// create the paint for the circle border
var circleBorder = new SKPaint {
    IsAntialias = true,
    Style = SKPaintStyle.Stroke,
    Color = SKColors.Red,
    StrokeWidth = 5
};

// draw the circle border
canvas.DrawCircle(100, 100, 40, circleBorder);

We can see the circle on the screen when we run our app now:

Final Drawing

Drawing Paths

We can also draw more complex shapes using a SKPath and the SKCanvas.DrawPath(path, paint) method:

// create the paint for the path
var pathStroke = new SKPaint {
    IsAntialias = true,
    Style = SKPaintStyle.Stroke,
    Color = SKColors.Green,
    StrokeWidth = 5
};

// create a path
var path = new SKPath();
path.MoveTo(160, 60);
path.LineTo(240, 140);
path.MoveTo(240, 60);
path.LineTo(160, 140);

// draw the path
canvas.DrawPath(path, pathStroke);

Now we’ll see an X next to the circle we drew previously when we run the app:

Final Drawing

Drawing Text

The last thing we’ll look at here is text. It’s just as easy to draw text using SkiaSharp; all we need to do is use the SKCanvas.DrawText(text, x, y, paint) method:

// create the paint for the text
var textPaint = new SKPaint
{
    IsAntialias = true,
    Style = SKPaintStyle.Fill,
    Color = SKColors.Orange,
    TextSize = 80
};

// draw the text (from the baseline)
canvas.DrawText("SkiaSharp", 60, 160 + 80, textPaint);

We’ll now see orange text below our shapes and paths that were drawn earlier:

Final Drawing

Overview

Getting off the ground with SkiaSharp is easy—all you need is one NuGet: SkiaSharp. This package provides everything necessary to create and manipulate drawings on any platform.

If you’re looking to display your drawings on-screen, you can check out the SkiaSharp.Views NuGet package. This package has a set of views, layers, and controls that get you going with displaying your drawing on-screen. If you have a Xamarin.Forms app, then you can install the SkiaSharp.Views.Forms NuGet package, which provides the same views for Xamarin.Forms.

Finally, we have a lightweight SVG loading library that can be used to load an SVG into a SKPicture: SkiaSharp.Svg.

Documentation and Forums

We’ve documented all of the APIs of SkiaSharp on the Xamarin developer site, along with the developer guides, which provide even more information.

You can also ask questions on the Xamarin SkiaSharp forums. If you prefer StackOverflow, we’re there, too!

Hang out on Gitter to ask questions and add comments – here: xamarin/XamarinComponents.

Code, Issues and Suggestions

The SkiaSharp codebase is constantly growing and improving. If you want to contribute, you can do so by forking on GitHub: mono/SkiaSharp.

If you have any questions, bugs or suggestions, we welcome them as issues.

0 comments

Discussion are closed.