8.8 KiB
SkiaSharp
Skia# is a cross-platform, managed binding for the Skia Graphics Library (https://skia.org/)
What is Included
Skia# provides a PCL and platform-specific bindings for:
- Mac OS X
- Xamarin.Android
- Xamarin.iOS
- Windows Desktop
- Mac Desktop
Using Skia#
Creating a SKCanvas
Although all platforms can use the same drawing logic, each platform has a different method for obtaining the surface and canvas.
iOS
var screenScale = UIScreen.MainScreen.Scale;
var width = (int)(Bounds.Width * screenScale);
var height = (int)(Bounds.Height * screenScale);
IntPtr buff = System.Runtime.InteropServices.Marshal.AllocCoTaskMem (width * height * 4);
try {
using (var surface = SKSurface.Create (width, height, SKColorType.N_32, SKAlphaType.Premul, buff, width * 4)) {
var skcanvas = surface.Canvas;
skcanvas.Scale ((float)screenScale, (float)screenScale);
using (new SKAutoCanvasRestore (skcanvas, true)) {
DoDraw (skcanvas);
}
}
using (var colorSpace = CGColorSpace.CreateDeviceRGB ())
using (var bContext = new CGBitmapContext (buff, width, height, 8, width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo))
using (var image = bContext.ToImage ())
using (var context = UIGraphics.GetCurrentContext ()) {
// flip the image for CGContext.DrawImage
context.TranslateCTM (0, Frame.Height);
context.ScaleCTM (1, -1);
context.DrawImage (Bounds, image);
}
} finally {
if (buff != IntPtr.Zero)
System.Runtime.InteropServices.Marshal.FreeCoTaskMem (buff);
}
Android
var width = (float)skiaView.Width;
var height = (float)skiaView.Height;
using (var bitmap = Bitmap.CreateBitmap (canvas.Width, canvas.Height, Bitmap.Config.Argb8888)) {
try {
using (var surface = SKSurface.Create (canvas.Width, canvas.Height, SKColorType.Rgba_8888, SKAlphaType.Premul, bitmap.LockPixels (), canvas.Width * 4)) {
var skcanvas = surface.Canvas;
skcanvas.Scale (((float)canvas.Width)/width, ((float)canvas.Height)/height);
DoDraw (skcanvas);
}
}
finally {
bitmap.UnlockPixels ();
}
canvas.DrawBitmap (bitmap, 0, 0, null);
}
OS X
var screenScale = (int)NSScreen.MainScreen.BackingScaleFactor * 2;
var width = (int)Bounds.Width * screenScale;
var height = (int)Bounds.Height * screenScale;
IntPtr buff = System.Runtime.InteropServices.Marshal.AllocCoTaskMem (width * height * 4);
try {
using (var surface = SKSurface.Create (width, height, SKColorType.Rgba_8888, SKAlphaType.Premul, buff, width * 4)) {
var skcanvas = surface.Canvas;
skcanvas.Scale (screenScale, screenScale);
DoDraw (skcanvas);
}
int flag = ((int)CoreGraphics.CGBitmapFlags.ByteOrderDefault) | ((int)CoreGraphics.CGImageAlphaInfo.PremultipliedLast);
using (var colorSpace = CoreGraphics.CGColorSpace.CreateDeviceRGB ())
using (var bContext = new CoreGraphics.CGBitmapContext (buff, width, height, 8, width * 4, colorSpace, (CoreGraphics.CGImageAlphaInfo) flag))
using (var image = bContext.ToImage ())
using (var context = NSGraphicsContext.CurrentContext.GraphicsPort) {
context.DrawImage (Bounds, image);
}
} finally {
if (buff != IntPtr.Zero)
System.Runtime.InteropServices.Marshal.FreeCoTaskMem (buff);
}
Windows Desktop / Mac Desktop
var width = Width;
var height = Height;
using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppPArgb)) {
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
using (var surface = SKSurface.Create(width, height, SKColorType.Bgra_8888, SKAlphaType.Premul, data.Scan0, width * 4)) {
var skcanvas = surface.Canvas;
onDrawCallback(skcanvas, width, height);
}
bitmap.UnlockBits(data);
e.Graphics.DrawImage(bitmap, new Rectangle(0, 0, Width, Height));
}
Drawing on SKCanvas
Below are just a few of the many different things that can be done with Skia#:
Drawing Xamagon
SKCanvas canvas = ...;
// clear the canvas / fill with white
canvas.Clear (SKColors.White);
// set up drawing tools
using (var paint = new SKPaint ()) {
paint.IsAntialias = true;
paint.Color = new SKColor (0x2c, 0x3e, 0x50);
paint.StrokeCap = SKStrokeCap.Round;
// create the Xamagon path
using (var path = new SKPath ()) {
path.MoveTo (71.4311121f, 56f);
path.CubicTo (68.6763107f, 56.0058575f, 65.9796704f, 57.5737917f, 64.5928855f, 59.965729f);
path.LineTo (43.0238921f, 97.5342563f);
path.CubicTo (41.6587026f, 99.9325978f, 41.6587026f, 103.067402f, 43.0238921f, 105.465744f);
path.LineTo (64.5928855f, 143.034271f);
path.CubicTo (65.9798162f, 145.426228f, 68.6763107f, 146.994582f, 71.4311121f, 147f);
path.LineTo (114.568946f, 147f);
path.CubicTo (117.323748f, 146.994143f, 120.020241f, 145.426228f, 121.407172f, 143.034271f);
path.LineTo (142.976161f, 105.465744f);
path.CubicTo (144.34135f, 103.067402f, 144.341209f, 99.9325978f, 142.976161f, 97.5342563f);
path.LineTo (121.407172f, 59.965729f);
path.CubicTo (120.020241f, 57.5737917f, 117.323748f, 56.0054182f, 114.568946f, 56f);
path.LineTo (71.4311121f, 56f);
path.Close ();
// draw the Xamagon path
canvas.DrawPath (path, paint);
}
}
Drawing Text
SKCanvas canvas = ...;
// clear the canvas / fill with white
canvas.DrawColor (SKColors.White);
// set up drawing tools
using (var paint = new SKPaint ()) {
paint.TextSize = 64.0f;
paint.IsAntialias = true;
paint.Color = new SKColor (0x42, 0x81, 0xA4);
paint.IsStroke = false;
// draw the text
canvas.DrawText ("Skia", 0.0f, 64.0f, paint);
}
Drawing Bitmaps
SKCanvas canvas = ...;
Stream fileStream = ...; // open a stream to an image file
// clear the canvas / fill with white
canvas.DrawColor (SKColors.White);
// decode the bitmap from the stream
using (var stream = new SKManagedStream(fileStream))
using (var bitmap = SKBitmap.Decode(stream))
using (var paint = new SKPaint()) {
canvas.DrawBitmap(bitmap, SKRect.Create(Width, Height), paint);
}
Drawing Image Filters
SKCanvas canvas = ...;
Stream fileStream = ...; // open a stream to an image file
// clear the canvas / fill with white
canvas.DrawColor (SKColors.White);
// decode the bitmap from the stream
using (var stream = new SKManagedStream(fileStream))
using (var bitmap = SKBitmap.Decode(stream))
using (var paint = new SKPaint()) {
// create the image filter
using (var filter = SKImageFilter.CreateBlur(5, 5)) {
paint.ImageFilter = filter;
// draw the bitmap through the filter
canvas.DrawBitmap(bitmap, SKRect.Create(width, height), paint);
}
}
How Skia# is Built
Skia# provides the same features as the native C++ library through a method of wrapping the C++ API with a dumb C API. Then C# wraps the C API to provide an API similar to that of the C++.
For example, given the C++ API:
class SK_API SkPaint {
public:
bool isAntiAlias() const;
void setAntiAlias(bool aa);
};
This is then wrapped in a C API:
bool sk_paint_is_antialias(const sk_paint_t* cpaint) {
return AsPaint(*cpaint).isAntiAlias();
}
void sk_paint_set_antialias(sk_paint_t* cpaint, bool aa) {
AsPaint(cpaint)->setAntiAlias(aa);
}
Which is then pulled into the C# project via P/Invoke:
public extern static bool sk_paint_is_antialias(sk_paint_t t);
public extern static void sk_paint_set_antialias(sk_paint_t t, bool aa);
Finally, this is wrapped into a neat C# class:
public class SKPaint : SKObject
{
public bool IsAntialias {
get { return SkiaApi.sk_paint_is_antialias (Handle); }
set { SkiaApi.sk_paint_set_antialias (Handle, value); }
}
}
As a result, the C# API functions and appears the same as the C++ API.
Where is Windows Phone / Store
At this time, Windows Phone and Windows Store apps are not supported. This is due to the native library not supporting those platforms: