Skip to content

SkiaSharp with Python

Matthew Leibowitz edited this page Mar 1, 2017 · 3 revisions

One of the popular requests with skia is a Python API. I am no Python expert - I literally know nothing, and Bing-ed everything - but using the same native C libraries that SkiaSharp uses, we can create a Python script.

Forgive this awful code I am sure I have just released on the world, but here is one way in which to make use of the native skia bits in your Python app. You can just build the native bits you need:

# Windows
.\bootstrapper.ps1 -Target externals-windows

# macOS
./bootstrapper.sh -t externals-mac

# Linux
./bootstrapper.sh -t externals-linux

This will pop out the native bits in the <skiasharp-root>/output/<platform>/libSkiaSharp.xxx path. These are just plain old C/C++ binaries that anyone should be able to use from any language. Note: you can also download and extract the NuGet (http://www.nuget.org/packages/SkiaSharp). All the binaries are in the runtimes folder in the root of the zip.

Here is a sample script that will draw a nice little image:

# create the surface
info = SKImageInfo(512, 512, SKPlatformColorType, SKAlphaType.Premul)
surface = SKSurface(info)

# get and clear the canvas for drawing
canvas = surface.canvas()
canvas.clear(0xFF3498DB) # "Xamarin Light Blue"

# create the paint fill
paint = SKPaint()
paint.setAntiAlias(True)
paint.setStyle(SKPaintStyle.Fill)

# draw a circle
paint.setColor(0xFF2C3E50) # "Xamarin Dark Blue"
canvas.drawCircle(256, 256, 200, paint)

# draw text
center_text = 256 + 16
paint.setTextSize(64)
paint.setTextAlign(SKTextAlign.Center)
paint.setColor(0xFFFFFFFF) # "White"
canvas.drawText("SkiaSharp", 256, center_text - 64, paint)
canvas.drawText("+", 256, center_text, paint)
canvas.drawText("Python", 256, center_text + 64, paint)

# create a PNG from the surface
image = surface.snapshot()
data = image.encode()

# save the PNG to disk
stream = SKFileWStream("image.png")
stream.writeData(data)
stream.flush()

# clean up
del paint
del stream
del data
del image
del canvas
del surface

The definitions part of the script is very simple - just like the SkiaSharp C# API:

from ctypes import *

def enum(**enums):
    return type('Enum', (), enums)

'''
SkiaApi.cs
'''

libSkiaSharp = cdll.LoadLibrary("libSkiaSharp.dll")

'''
Declarations.cs
'''

SKAlphaType = enum(Unknown = 0, Opaque = 1, Premul = 2, Unpremul = 3)
SKTextAlign = enum(Left = 0, Center = 1, Right = 2)
SKPaintStyle = enum(Fill = 0, Stroke = 1, StrokeAndFill = 2)

SKPlatformColorType = libSkiaSharp.sk_colortype_get_default_8888()

class SKImageInfo(Structure):
    _fields_ = [("width", c_int),
                ("height", c_int),
                ("colorType", c_int),
                ("alphaType", c_int)]

class SKSurface(object):
    def __init__(self, info):
        self.__HANDLE = libSkiaSharp.sk_surface_new_raster(byref(info), 0)

    def handle(self):
        return self.__HANDLE

    def canvas(self):
        cnv = libSkiaSharp.sk_surface_get_canvas(self.__HANDLE)
        return SKCanvas(cnv)

    def snapshot(self):
        img = libSkiaSharp.sk_surface_new_image_snapshot(self.__HANDLE)
        return SKImage(img)

    def __del__(self): 
        libSkiaSharp.sk_surface_unref(self.__HANDLE)

class SKCanvas(object):
    def __init__(self, handle):
        self.__HANDLE = handle

    def handle(self):
        return self.__HANDLE

    def clear(self, color):
        libSkiaSharp.sk_canvas_draw_color(self.__HANDLE, color)

    def drawCircle(self, cx, cy, radius, paint):
        libSkiaSharp.sk_canvas_draw_circle(self.__HANDLE, c_float(cx), c_float(cy), c_float(radius), paint.handle())

    def drawText(self, text, x, y, paint):
        libSkiaSharp.sk_canvas_draw_text(self.__HANDLE, text, len(text), c_float(x), c_float(y), paint.handle())

    def __del__(self): 
        # do nothing
        return False

class SKPaint(object):
    def __init__(self):
        self.__HANDLE = libSkiaSharp.sk_paint_new()

    def handle(self):
        return self.__HANDLE

    def setAntiAlias(self, value):
        libSkiaSharp.sk_paint_set_antialias(self.__HANDLE, c_bool(value))

    def setStyle(self, style):
        libSkiaSharp.sk_paint_set_style(self.__HANDLE, style)

    def setColor(self, color):
        libSkiaSharp.sk_paint_set_color(self.__HANDLE, color)

    def setTextSize(self, size):
        libSkiaSharp.sk_paint_set_textsize(self.__HANDLE, c_float(size))

    def setTextAlign(self, alignment):
        libSkiaSharp.sk_paint_set_text_align(self.__HANDLE, alignment)

    def __del__(self): 
        libSkiaSharp.sk_paint_delete(self.__HANDLE)

class SKImage(object):
    def __init__(self, handle):
        self.__HANDLE = handle

    def handle(self):
        return self.__HANDLE

    def encode(self):
        data = libSkiaSharp.sk_image_encode(self.__HANDLE)
        return SKData(data)

    def __del__(self): 
        libSkiaSharp.sk_image_unref(self.__HANDLE)

class SKData(object):
    def __init__(self, handle):
        self.__HANDLE = handle

    def handle(self):
        return self.__HANDLE

    def data(self):
        return libSkiaSharp.sk_data_get_data(self.__HANDLE)

    def size(self):
        return libSkiaSharp.sk_data_get_size(self.__HANDLE)

    def __del__(self): 
        libSkiaSharp.sk_data_unref(self.__HANDLE)

class SKFileWStream(object):
    def __init__(self, filename):
        self.__HANDLE = libSkiaSharp.sk_filewstream_new(filename)

    def handle(self):
        return self.__HANDLE

    def writeData(self, data):
        return self.write(data.data(), data.size())

    def write(self, data, size):
        return libSkiaSharp.sk_wstream_write(self.__HANDLE, data, size)

    def flush(self):
        libSkiaSharp.sk_wstream_flush(self.__HANDLE)

    def __del__(self): 
        libSkiaSharp.sk_filewstream_destroy(self.__HANDLE)

The full script can be found here: https://gist.github.com/mattleibow/d4fa01931b2f8e283b61f5fa294dd894