May 23rd, 2017

Deep Dive into SkiaSharp with Xamarin.Forms

Xamarin.Forms is a versatile cross-platform user-interface toolkit that allows a single program to run on iOS, Android, the Universal Windows Platform, and (most recently) macOS.

However, one feature missing from Xamarin.Forms is a graphics system. Sure, Xamarin.Forms can render bitmaps, and you can size and rotate a BoxView for some simple blocky images, but for those of us who love graphics programming, Xamarin.Forms has a big polygon-shaped hole.

But don’t fret! A very compelling solution for Xamarin.Forms graphics is SkiaSharp, Xamarin’s C# library that incorporates Google’s powerful Skia Graphics Engine.Old-school graphics with SkiaSharp.

SkiaSharp is a cross-platform, high-performance 2D API for .NET developers perfect for mobile development. The API is straightforward to work with and can be used to do simple things, like drawing shapes or text, all the way to complex real-time drawing applications. The Xamarin team has even created a WYSIWYG graphics designer that puts out SkiaSharp graphics code.

As a member of Xamarin’s documentation team, I’ve been engaged in writing what I hope to be an extensive series of bite-sized tutorials on using SkiaSharp in Xamarin.Forms. These articles represent a deep dive into SkiaSharp, but one that I hope can also serve as an introduction to 2D graphics for newcomers. SkiaSharp shares many concepts with other 2D graphics drawing systems, so learning SkiaSharp is a terrific way to get a general education in 2D vector graphics.

Most recently, I’ve finished several articles on SkiaSharp Transforms. SkiaSharp supports all the basic graphics transforms found in other graphics systems, including translation, scaling, rotation, and skewing, which tilts graphics objects such as this shadow:

Example of drawing shadows in SkiaSharp.

That image uses an SKPaint object to define the characteristics of the text output. For the shadow (which is drawn first), the code sets a different color and a combination of translation, scaling, and skew to enlarge the text and tilt it to one side:

using (SKPaint textPaint = new SKPaint())
{
    textPaint.Style = SKPaintStyle.Fill;
    textPaint.TextSize = screenWidth / 6;
    textPaint.IsAntialias = true;

    // Common to shadow and text
    string text = "shadow";
    float xText = 20;
    float yText = screenHeight / 2;

    // Shadow
    textPaint.Color = SKColors.Gray;
    canvas.Save();
    canvas.Translate(xText, yText);
    canvas.Skew((float)Math.Tan(-Math.PI / 4), 0);
    canvas.Scale(1, 3);
    canvas.Translate(-xText, -yText);
    canvas.DrawText(text, xText, yText, textPaint);
    canvas.Restore();

    // Text
    textPaint.Color = SKColors.Blue;
    canvas.DrawText(text, xText, yText, textPaint);
}

SkiaSharp also supports standard matrix transforms, including non-affine transforms, which can achieve perspective and 3D rotation effects, such as the image at the top of this blog.

On mobile platforms and other modern devices, graphics is often intimately related to touch. Xamarin.Forms supports some rudimentary touch events, but not the essential tool of tracking individual fingers. More sophisticated touch tracking for Xamarin.Forms is now available with a Xamarin.Forms effect described in Invoking Events from Effects.

Combining SkiaSharp with touch tracking can result in some fun applications. I’ve recently created a Xamarin.Forms version of SpinPaint, based on a program I first wrote seven years ago for the Microsoft tabletop computer (originally called Surface and later renamed PixelSense).

SpinPaint simulates a rotating disk. Touch it or move your fingers on it, and it draws not only a line corresponding to your finger but three other lines that are mirror images, creating pretty patterns. Here’s SpinPaint running on iOS, Android, and the Universal Windows Platform:

Painting graphics with SkiaSharp

I think what impresses me most about SkiaSharp is its versatility. For example, other 2D graphics systems aren’t in total agreement about the best way to draw an arc, which is a curve on the circumference of an ellipse. Different graphics systems define the arc in very dissimilar ways.

SkiaSharp doesn’t force you into thinking of arcs in a particular way; rather, it supports three different ways to define an arc in a graphics path, so you can use the one that’s best for your particular application:

The angle arc requires that you specify a bounding rectangle for an ellipse, along with start angles and sweep angles:

path.AddArc(rect, startAngle, sweepAngle);

The resultant arc is shown here in red:

Angle arc drawn with SkiaSharp

This is identical to the AddArc and ArcTo methods of the Android Path object, and similar to the AddArc method of the iOS CGPath (although the iOS version is restricted to arcs on the circumference of a circle). This type of arc is great for pie charts.

The tangent arc is similar to the PostScript arct function and the iOS AddArcToPoint method. A radius of a circle is specified that is then fit snugly between two connected lines:

path.ArcTo(pointCorner, pointDestination, radius);

SkiaSharp draws a line and the arc, again shown in red:

Drawing a tangent arc with SkiaSharp.

The tangent arc is ideal for making rounded corners.

Finally, the elliptical arc allows you to specify two radii of an ellipse, as well as a rotation angle. The resultant tilted ellipse can fit between two points in two different ways, resulting in four different arcs between the two points, shown here in four different colors:

Drawing an elliptical arc with SkiaSharp.

You specify which of these four possible arcs you want with two further parameters:

ArcTo (radii, rotation, largeArcFlag, sweepDirectionFlag, point)

This type of arc specification is consistent with the elliptical arc supported by Scalable Vector Graphics (SVG) as well as the ArcSegment supported by XAML-based Windows graphics systems, including the Universal Windows Platform.

If you think arc-drawing is versatile, wait until you see SkiaSharp’s shaders and path effects! The shaders let you specify various gradients for filling in areas, including a linear gradient, radial gradient, conical gradient, sweep gradient, and random patterns called Perlin noise. The path effect can render a dotted or dashed line with any pattern of dots or dashes you want, but also render a line composed of little patterns:

SkiaSharp drawing a patterned texture.

Wrapping Up

There are still more SkiaSharp articles to come, so check the Using SkiaSharp in Xamarin.Forms page often to see what’s new.

Meanwhile, you’ll also want to register for Xamarin University Presents, a series of five must-see webinars in July, including one I’ll be doing on SkiaSharp Graphics for Xamarin.Forms.

0 comments

Discussion are closed.