Skia Ruby
Skia bindings for Ruby, providing 2D drawing, image processing, PDF generation, runtime shaders, skottie animation, and SVG path/DOM rendering.
Status
This project provides a practical, SkiaSharp-compatible API surface in Ruby, with a mix of:
- native-backed features through SkiaSharp C API
- Ruby convenience/typed layers for ergonomic usage
Native-backed (current)
-
Surface/Canvas/Paint/Path/PathMeasure/Region/Image/Shader - Shape and path drawing (rect, rrect, arc, points, softened shadows, picture playback)
- Mesh and sprite drawing (
draw_vertices,draw_atlas,draw_patch) -
DocumentPDF and XPS output- file output
- memory stream output
- metadata (
title,author,creation,pdfa, etc.)
-
Picturerecording / playback / serialization -
RuntimeEffect(SkSL compile and shader creation) -
Skottie(Lottie JSON animation load/seek/render, font and external/data-URI resource providers) - Encoders (
PNG,JPEG,WEBP) -
Codecmetadata, EXIF orientation, and frame decoding for animated GIF/WebP inputs - SVG canvas output and sampled image resize/scale helpers
Ruby layer (current)
- Geometry/value types
-
Point,Rect,RRect,Matrix,ImageInfo,ColorSpace
-
- Effect wrappers
-
MaskFilter, color-matrix/tableColorFilter,ImageFilter,PathEffect,Blender
-
- Pixel APIs
-
Bitmap,Pixmap,read_pixelshelpers, optionalPixmap/Numo::NArrayconversion
-
- Text primitives
-
Font,Typeface,FontManagersystem-font enumeration/fallback,TextBlob
-
- Structured helpers
-
Textlayout(Shaper,Paragraph) -
Svg::Dom(<path>load and draw)
-
Native build note
- Text layout modules (
skunicode,skshaper,skparagraph) are generally not exposed inlibSkiaSharpbuilds. - This gem checks symbol availability at runtime and raises
UnsupportedOperationErrorwith the missing symbol names.
Requirements
- Ruby 3.2+
- SkiaSharp native library (
libSkiaSharp)- macOS:
libSkiaSharp.dylib - Linux:
libSkiaSharp.so - Windows:
libSkiaSharp.dll
- macOS:
Installation
Add this line to your application's Gemfile:
gem 'skia'Then run:
bundle installInstall the native library once in your user data directory:
bundle exec skia-install-nativeThe loader discovers that directory automatically; no environment variables are required.
Installing Native Library
The gem loads libSkiaSharp from SKIA_LIBRARY_PATH, the user data directory, project/vendor directories,
the current working directory, or the system path. If loading fails, the error lists every searched path.
Recommended (script-based)
Use the bundled installer scripts (defaults to prebuilt download):
scripts/install_native_skia.sh prebuilt
export SKIA_NATIVE_SOURCE=prebuilt
export SKIA_PREBUILT_DIR="$PWD/vendor/native/$(uname | tr '[:upper:]' '[:lower:]')"For repository development, the equivalent Rake task is:
bundle exec rake skia:install_nativeOn Windows:
.\scripts\install_native_skia.ps1 -Mode prebuilt
$env:SKIA_NATIVE_SOURCE = "prebuilt"
$env:SKIA_PREBUILT_DIR = "$pwd\\vendor\\native\\windows"SKIA_NATIVE_SOURCE modes:
-
auto: explicit path / local / prebuilt / system fallback -
local: use local library only (SKIA_LIBRARY_PATHrequired) -
prebuilt: use prebuilt vendor path (SKIA_PREBUILT_DIRorvendor/native/<platform>)
macOS
curl -L -o skiasharp.nupkg \
https://api.nuget.org/v3-flatcontainer/skiasharp.nativeassets.macos/3.119.2/skiasharp.nativeassets.macos.3.119.2.nupkg
unzip skiasharp.nupkg -d skiasharp-extract
cp skiasharp-extract/runtimes/osx/native/libSkiaSharp.dylib .Linux
curl -L -o skiasharp.nupkg https://www.nuget.org/api/v2/package/SkiaSharp.NativeAssets.Linux.x64
unzip skiasharp.nupkg -d skiasharp-extract
cp skiasharp-extract/runtimes/linux-x64/native/libSkiaSharp.so .Windows
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/SkiaSharp.NativeAssets.Win32 -OutFile skiasharp.nupkg
Expand-Archive skiasharp.nupkg -DestinationPath skiasharp-extract
copy skiasharp-extract\runtimes\win-x64\native\libSkiaSharp.dll .Quick Start
require 'skia'
surface = Skia::Surface.make_raster(640, 480)
surface.draw do |canvas|
canvas.clear(Skia::Color::WHITE)
paint = Skia::Paint.new
paint.antialias = true
paint.color = Skia::Color::RED
canvas.draw_circle(320, 240, 100, paint)
end
surface.save_png('output.png')Render a script whose final expression is a Surface, Image, or encoded Data:
skia render drawing.rb -o output.png
skia info output.png
skia lottie animation.json --frame 12 -o frame.webpGenerate an Open Graph/social card with the high-level helper:
Skia::Card.new(
title: 'Shipping fast graphics from Ruby',
author: '@rubyist',
site_name: 'example.com',
tags: %w[Ruby Graphics]
).save('social-card.png')For Rails Active Storage variants, configure the transformer in an initializer after Active Storage is loaded:
ActiveStorage.variant_transformer = Skia::ActiveStorageTransformerThe adapter supports resize_to_limit, resize_to_fit, resize_to_fill, resize_and_pad, crop, and rotate.
Unsupported operations fail before processing instead of being silently ignored.
For deterministic cleanup, every Surface factory also accepts a block:
Skia::Surface.make_raster(640, 480) do |surface|
surface.canvas.clear(Skia::Color::WHITE)
surface.save_png('output.png')
end # native surface is released here, including when the block raisesPDF Example
require 'skia'
Skia::Document.create_pdf('output.pdf', metadata: {
title: 'My Report',
author: 'skia-ruby',
creation: Time.now,
raster_dpi: 144.0,
encoding_quality: 90
}) do |doc|
doc.page(612, 792) do |canvas|
canvas.clear(Skia::Color::WHITE)
font = Skia::Font.new(nil, 24.0)
paint = Skia::Paint.new
paint.color = Skia::Color::BLACK
canvas.draw_text('Hello, PDF!', 50, 100, font, paint)
end
endRuntime Shader Example
require 'skia'
sksl = <<~SKSL
half4 main(float2 coord) {
half r = coord.x / 640.0;
half g = coord.y / 360.0;
return half4(r, g, 0.35, 1.0);
}
SKSL
effect = Skia::RuntimeEffect.make_for_shader(sksl)
shader = effect.make_shader
surface = Skia::Surface.make_raster(640, 360)
paint = Skia::Paint.new
paint.shader = shader
surface.draw do |canvas|
canvas.draw_rect(Skia::Rect.from_wh(640, 360), paint)
end
surface.save_png('runtime_effect.png')API Coverage (Skia docs parity)
| Area | Status | Notes |
|---|---|---|
Core drawing (Surface/Canvas/Paint/Path) |
Implemented | Includes PathOps, PathMeasure, Region, clip and quick-reject APIs |
Shape/text primitives (RRect, TextBlob) |
Implemented | Includes glyph-positioned and path-following text drawing |
Effects (MaskFilter, ColorFilter, ImageFilter, PathEffect, Blender) |
Implemented | Includes color matrices/tables and runtime color filters/blenders |
Pixel and color APIs (Bitmap, Pixmap, ImageInfo, ColorSpace) |
Implemented | Read/copy/access pixel-level data |
| Image and shader extensions | Implemented | Includes gradients and runtime shader compilation |
| PDF document APIs | Implemented | File/memory output and metadata are supported |
| XPS document APIs | Platform-specific | File/memory and multi-page output are supported by Windows libSkiaSharp builds |
skottie (Lottie) |
Implemented | JSON load/seek/render plus font manager and resource-provider builders |
| SVG path / SVG DOM | Implemented | SVG canvas output plus paths, shapes, text, groups, and linear/radial gradient fills |
| GPU-oriented surface constructors | Partial | Requires externally managed native GPU context pointers |
Text layout modules (skunicode, skshaper, skparagraph) |
Partial | Ruby API is available; native symbols may be absent depending on libSkiaSharp build |
Development
After checking out the repo, install dependencies:
bundle installRun examples:
ruby examples/basic_drawing.rb
ruby examples/gradient.rb
ruby examples/bar_chart.rb
ruby examples/advanced_features.rb
ruby examples/pdf_stream.rb
ruby examples/runtime_effect.rb
ruby examples/textlayout.rb
ruby examples/skottie.rb
ruby examples/svg_dom.rbAdditional guides:
- Feature support and native ABI limits
- Rails, static-site, and GPU integration recipes
- Release and upstream sync guidance
Run the optional resize comparison (installed adapters are detected automatically):
bundle exec ruby benchmark/image_resize.rbGenerate API documentation and validate the bundled RBS signatures with:
bundle exec rake docs
bundle exec rbs validate
bundle exec steep checkPublished API reference: rubydoc.info/gems/skia
Concurrency policy
- Independent
Surface,Canvas,Paint, and other native objects may be created and used in separate threads. - Do not use the same mutable native-backed object concurrently. Synchronization remains the caller's responsibility.
- A
Canvaskeeps its parentSurfacealive, and borrowed pixel/effect objects keep their native owner alive. - Native-backed objects are not Ractor-shareable. Build and render them inside the Ractor that owns them.
CI exercises independent surface rendering from multiple threads. Ractor support will require an isolation-safe FFI boundary and is not currently claimed.
License
This project is available under the MIT License.
Contributing
Bug reports and pull requests are welcome at: