goskia

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 11, 2026 License: GPL-3.0 Imports: 5 Imported by: 0

README

goskia

A Go binding to Skia's 2D graphics API, via a C ABI shim. (cgo cannot call C++ name-mangled symbols, so extern "C" trampolines wrap the C++ API.)

Why this exists

A standalone, separately-versioned, separately-CI'd binding so a browser engine (or any Go project) can opt into Skia via a -tags skia build without the engine's default build ever touching cgo/Skia. Mirrors the layout and distribution model of github.com/MrSametBurgazoglu/v8go.

Dependency direction (load-bearing)

goskia depends on nothing downstream. It exposes a Skia-shaped Go API. A consumer implements its own Canvas interface against this API behind a //go:build skia adapter. goskia never imports the consumer.

Status

Drawing pipeline working — cgo -> libskia.a -> render -> readback, proven by tests. Backed by the rust-skia 0.99.0 prebuilt libskia.a (Skia milestone m150), which is committed to the repo (14 MB) so the module builds straight out of the read-only Go module cache with zero setup.

Go API:

  • NewRasterSurface(w, h) — CPU-backed N32-premul surface; ReadPixels() []byte (BGRA-8888 on linux-le), SavePNG.
  • Canvas state: Save / Restore / RestoreToCount / SaveLayerAlpha, Translate, ClipRect, ClipRRect (per-corner), Clear.
  • Geometry: DrawRect, DrawRRect, DrawRRectCorners (per-corner), DrawDRRect (border rings), DrawPath (move/line/quad/close).
  • Paint: color (0xAARRGGBB), anti-alias, fill/stroke style, stroke width.
  • Images: NewImageRGBA (unpremul, copied), NewImageA8 (mask; draws in the paint's colour), Canvas.DrawImage / DrawImageRect (nearest sampling — consumers scale on the CPU with the filter they want before upload).

GPU (GL) and text (SkShaper / SkParagraph) are later phases; text deliberately so — the consuming engine shapes with HarfBuzz and rasterises with FreeType, and hands this library finished masks.

Quick start (linux x86_64)

git clone https://github.com/MrSametBurgazoglu/goskia.git
cd goskia
CC=clang CXX=clang++ go test .    # needs system freetype + fontconfig

libskia.a distribution

libskia.a is not committed (~15 MB binary; committing it would bloat every clone permanently). scripts/fetch-libskia.go downloads the pinned rust-skia prebuilt, verifies its SHA-256, and extracts libskia.a + LICENSE_SKIA into deps/linux_amd64/. The Rust-specific libskia-bindings.a / bindings.rs in the archive are dropped — goskia writes its own extern "C" trampolines over raw Skia C++.

License

  • goskia binding code: see LICENSE.
  • Skia (libskia.a + deps/include/): BSD-3-Clause — see deps/linux_amd64/LICENSE_SKIA.

Documentation

Overview

Package goskia is a Go binding to Skia's 2D graphics API via a C ABI shim.

Skia's C++ API cannot be called directly from cgo (name mangling), so this package links against libskia.a and exposes SkCanvas/SkSurface/SkPaint/SkPath through extern "C" trampolines. The dependency direction is deliberately one-way: goskia has NO dependency on any browser engine. An engine consumes it by implementing its own Canvas interface against this API.

STATUS: skeleton. This placeholder exists so the module is resolvable (`go mod download`) from day one — the lesson from the v8go fork, whose repo was created late and broke CI resolution. The C ABI shim and libskia.a distribution are being wired up; no drawing API is exported yet.

Index

Constants

View Source
const Version = "0.0.0-pre"

Version is the binding version. Pre-release until the first drawing subset (Surface/Canvas/DrawRect/Flush) lands and is golden-tested.

Variables

This section is empty.

Functions

This section is empty.

Types

type Canvas

type Canvas struct {
	// contains filtered or unexported fields
}

Canvas is the Skia drawing context for a Surface. Non-owning: the underlying SkCanvas is owned by the Surface, so a Canvas must not outlive its Surface.

func (*Canvas) Clear

func (c *Canvas) Clear(argb uint32)

Clear replaces every pixel inside the clip with the colour, ignoring blending.

func (*Canvas) ClipRRect

func (c *Canvas) ClipRRect(l, t, r, b float32, radii CornerRadii)

ClipRRect intersects the clip with a rounded rectangle, anti-aliased. This is what rounds an `overflow: hidden` box's children — a rectangular clip cannot.

func (*Canvas) ClipRect

func (c *Canvas) ClipRect(l, t, r, b float32)

ClipRect intersects the clip with a rectangle, anti-aliased.

func (*Canvas) DrawDRRect

func (c *Canvas) DrawDRRect(
	ol, ot, or_, ob float32, outer CornerRadii,
	il, it, ir, ib float32, inner CornerRadii, p *Paint)

DrawDRRect fills the region between an outer and an inner rounded rect — a border ring in one call, with the corner geometry Skia derives rather than the caller approximating.

func (*Canvas) DrawImage

func (c *Canvas) DrawImage(img *Image, x, y float32, p *Paint)

DrawImage blits img with its top-left corner at (x, y). A nil paint is a plain source-over blit; a paint carries alpha (and, for A8 images, colour). Sampling is nearest — a consumer that wants filtering scales before upload.

func (*Canvas) DrawImageRect

func (c *Canvas) DrawImageRect(img *Image, sl, st, sr, sb, dl, dt, dr, db float32, p *Paint)

DrawImageRect blits the src rectangle of img onto the dst rectangle, scaling with nearest sampling if the two differ.

func (*Canvas) DrawPath

func (c *Canvas) DrawPath(path *Path, p *Paint)

DrawPath fills (or strokes, per p) the given path.

func (*Canvas) DrawRRect

func (c *Canvas) DrawRRect(l, t, r, b, rx, ry float32, p *Paint)

DrawRRect fills (or strokes, per p) a rounded rectangle over [l, t, r, b] with corner radii (rx, ry).

func (*Canvas) DrawRRectCorners

func (c *Canvas) DrawRRectCorners(l, t, r, b float32, radii CornerRadii, p *Paint)

DrawRRectCorners fills (or strokes, per p) a rounded rectangle with independent corner radii.

func (*Canvas) DrawRect

func (c *Canvas) DrawRect(l, t, r, b float32, p *Paint)

DrawRect fills the rectangle [l, t, r, b] with p.

func (*Canvas) Restore

func (c *Canvas) Restore()

Restore pops to the most recent Save.

func (*Canvas) RestoreToCount

func (c *Canvas) RestoreToCount(count int)

RestoreToCount pops until the stack is `count` deep.

func (*Canvas) Save

func (c *Canvas) Save() int

Save pushes the current matrix and clip. Returns the depth to restore to.

func (*Canvas) SaveLayerAlpha

func (c *Canvas) SaveLayerAlpha(l, t, r, b float32, alpha uint8) int

SaveLayerAlpha begins an offscreen layer over bounds; the matching Restore composites it at alpha (0..255). This is group opacity: everything drawn inside blends normally with itself, and the *result* is faded as one.

func (*Canvas) Translate

func (c *Canvas) Translate(dx, dy float32)

Translate moves the origin. Subsequent draws and clips are offset by (dx, dy).

type CornerRadii

type CornerRadii [8]float32

CornerRadii is per-corner x/y radii in SkRRect order: upper-left, upper-right, lower-right, lower-left.

func Radii

func Radii(upperLeft, upperRight, lowerRight, lowerLeft float32) CornerRadii

Radii builds a CornerRadii with circular corners.

type Image

type Image struct {
	// contains filtered or unexported fields
}

Image is an immutable raster image on the Skia side.

Both constructors copy: the Go slice is free the moment they return, and the Image can be drawn any number of times without touching Go memory again — which is what makes caching one per texture worthwhile for a consumer.

func NewImageA8

func NewImageA8(alpha []byte, width, height, rowBytes int) *Image

NewImageA8 copies an 8-bit coverage mask into an Image. An A8 image draws in the paint's colour — the glyph-mask and shadow-mask path.

func NewImageRGBA

func NewImageRGBA(pix []byte, width, height, rowBytes int) *Image

NewImageRGBA copies non-premultiplied RGBA8888 pixels into an Image.

Non-premultiplied because that is what image.RGBA-shaped callers hold; Skia premultiplies on draw. rowBytes is the source stride (width*4 for a tight slice).

func (*Image) Delete

func (img *Image) Delete()

Delete releases the image. Idempotent.

func (*Image) Height

func (img *Image) Height() int

func (*Image) Width

func (img *Image) Width() int

Width and Height are the image's pixel dimensions.

type Paint

type Paint struct {
	// contains filtered or unexported fields
}

Paint holds Skia fill/stroke state. Heap-allocated in C; call Delete when done.

func NewPaint

func NewPaint() *Paint

NewPaint constructs a default Paint (black fill, no anti-alias).

func (*Paint) Delete

func (p *Paint) Delete()

Delete releases the paint. Idempotent.

func (*Paint) SetAntiAlias

func (p *Paint) SetAntiAlias(on bool)

SetAntiAlias toggles edge anti-aliasing.

func (*Paint) SetColor

func (p *Paint) SetColor(argb uint32)

SetColor sets the paint color in Skia's 0xAARRGGBB order (e.g. 0xFFFF0000 = opaque red).

func (*Paint) SetStrokeWidth

func (p *Paint) SetStrokeWidth(width float32)

SetStrokeWidth sets the stroke width; only meaningful after SetStyle(PaintStroke).

func (*Paint) SetStyle

func (p *Paint) SetStyle(style PaintStyle)

SetStyle sets the paint to fill or stroke geometry.

type PaintStyle

type PaintStyle int

PaintStyle selects fill vs. stroke. Values match SkPaint::Style ordering (kFill_Style=0, kStroke_Style=1) as exposed by the C shim.

const (
	PaintFill   PaintStyle = 0
	PaintStroke PaintStyle = 1
)

type Path

type Path struct {
	// contains filtered or unexported fields
}

Path wraps an in-progress Skia path. In m150, path construction lives on SkPathBuilder; the C shim holds one and snapshots it to an immutable SkPath at DrawPath time. Delete when done (the builder is heap-allocated in C).

func NewPath

func NewPath() *Path

NewPath constructs an empty Path.

func (*Path) Close

func (p *Path) Close()

Close closes the current contour with a line back to its MoveTo point.

func (*Path) Delete

func (p *Path) Delete()

Delete releases the path. Idempotent.

func (*Path) LineTo

func (p *Path) LineTo(x, y float32)

LineTo appends a line from the current point to (x, y).

func (*Path) MoveTo

func (p *Path) MoveTo(x, y float32)

MoveTo starts a new contour at (x, y).

func (*Path) QuadTo

func (p *Path) QuadTo(x1, y1, x2, y2 float32)

QuadTo appends a quadratic curve from the current point to (x2, y2), using (x1, y1) as the control point.

type Surface

type Surface struct {
	// contains filtered or unexported fields
}

Surface is a CPU-backed Skia drawing target.

func NewRasterSurface

func NewRasterSurface(width, height int) *Surface

NewRasterSurface creates a CPU-backed N32-premul surface width x height.

func (*Surface) Canvas

func (s *Surface) Canvas() *Canvas

Canvas returns the surface's drawing context. Non-owning: the Canvas dies with the Surface. Cached: repeated calls return the same Canvas.

func (*Surface) Delete

func (s *Surface) Delete()

Delete releases the surface. Idempotent.

func (*Surface) Height

func (s *Surface) Height() int

Height returns the surface height in pixels.

func (*Surface) ReadPixels

func (s *Surface) ReadPixels() []byte

ReadPixels copies the whole surface into a new width*height*4 byte slice (N32 premul). Returns nil on failure.

func (*Surface) ReadPixelsInto

func (s *Surface) ReadPixelsInto(dst []byte, rowBytes int) bool

ReadPixelsInto copies the whole surface into dst (N32 premul, width*height*4 bytes) without allocating. Reports success. The allocating ReadPixels stays for callers that want a fresh slice; a per-frame reader reuses one buffer through this instead.

func (*Surface) ReadPixelsRGBAInto added in v0.1.1

func (s *Surface) ReadPixelsRGBAInto(dst []byte, rowBytes int) bool

ReadPixelsRGBAInto copies the whole surface into dst as straight (non-premultiplied) RGBA — the layout image.RGBA uses — rather than the surface's own N32 premultiplied BGRA. Same buffer requirements as ReadPixelsInto; reports success.

Skia does the swizzle and the unpremultiply during the read, which is why this exists: a caller doing it afterwards pays a second pass over the whole window and needs a staging buffer to do it in.

func (*Surface) SavePNG

func (s *Surface) SavePNG(path string) error

SavePNG reads the surface pixels and writes them to path as an 8-bit RGBA PNG. The surface stores N32 premul which reads back as BGRA-8888 on little-endian; the B and R channels are swapped to produce a correct RGBA image.

func (*Surface) Width

func (s *Surface) Width() int

Width returns the surface width in pixels.

Directories

Path Synopsis
Command fetch-libskia downloads the pinned rust-skia prebuilt libskia.a for the current platform into deps/<GOOS>_<GOARCH>/, checksum-verified against a pinned SHA-256.
Command fetch-libskia downloads the pinned rust-skia prebuilt libskia.a for the current platform into deps/<GOOS>_<GOARCH>/, checksum-verified against a pinned SHA-256.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL