Back to Skiasharp

SkiaSharp Architecture

documentation/architecture.md

3.119.23.3 KB
Original Source

SkiaSharp Architecture

This document explains SkiaSharp's three-layer design and how data flows between C++, C, and C#.

Three-Layer Architecture

C# Wrapper Layer (binding/SkiaSharp/)
    ↓ P/Invoke
C API Layer (externals/skia/src/c/)
    ↓ Type casting
C++ Skia Library (externals/skia/)

Why this design?

  • C++ exceptions cannot cross P/Invoke boundaries
  • C APIs are ABI-stable
  • C# provides safety, validation, and idiomatic .NET patterns

Key Design Principles

  1. Handle-based: Native objects are IntPtr handles in C#
  2. Object identity: Global HandleDictionary ensures one C# wrapper per native handle
  3. Memory safety: IDisposable for cleanup, finalizers as backup
  4. Exception boundaries: C API never throws; C# validates and throws

Call Flow Example

csharp
// C# - validates then calls P/Invoke
public void DrawRect(SKRect rect, SKPaint paint) {
    if (paint == null)
        throw new ArgumentNullException(nameof(paint));
    SkiaApi.sk_canvas_draw_rect(Handle, &rect, paint.Handle);
}
cpp
// C API - minimal wrapper, trusts C# validation
void sk_canvas_draw_rect(sk_canvas_t* canvas, const sk_rect_t* rect, const sk_paint_t* paint) {
    AsCanvas(canvas)->drawRect(*AsRect(rect), *AsPaint(paint));
}

Type Mappings

C++C APIC# P/InvokeC# Wrapper
SkCanvas*sk_canvas_t*IntPtrSKCanvas
SkPaint*sk_paint_t*IntPtrSKPaint
sk_sp<SkImage>sk_image_t*IntPtrSKImage
SkRectsk_rect_tSKRectSKRect
SkColorsk_color_tuintSKColor

Naming pattern: SkTypesk_type_tSKType

Function Naming

C++C APIC#
SkCanvas::drawRect()sk_canvas_draw_rect()SKCanvas.DrawRect()
SkPaint::getColor()sk_paint_get_color()SKPaint.Color (get)
SkPaint::setColor()sk_paint_set_color()SKPaint.Color (set)

Type Conversion Macros (C API)

cpp
// In sk_types_priv.h - converts between C and C++ types
AsCanvas(sk_canvas_t*) → SkCanvas*
ToCanvas(SkCanvas*)sk_canvas_t*
AsPaint(sk_paint_t*)   → SkPaint*
AsRect(sk_rect_t*)     → SkRect*

File Organization

LayerFiles
C++ APIexternals/skia/include/core/SkCanvas.h
C API headerexternals/skia/include/c/sk_canvas.h
C API implexternals/skia/src/c/sk_canvas.cpp
P/Invokebinding/SkiaSharp/SkiaApi.generated.cs
C# wrapperbinding/SkiaSharp/SKCanvas.cs

Threading Model

Object TypeThread-Safe?Notes
SKCanvas, SKPaint, SKPath❌ NoKeep thread-local
SKImage, SKShader, SKTypeface✅ YesImmutable, can share
Object creation✅ YesCreating different objects on different threads is safe

Rule: Don't use the same mutable object (Canvas/Paint/Path) from multiple threads.

Code Generation

  • C API layer: Hand-written (externals/skia/src/c/*.cpp)
  • P/Invoke: Auto-generated from C headers (SkiaApi.generated.cs)
  • C# wrappers: Hand-written (binding/SkiaSharp/SK*.cs)
pwsh
pwsh ./utils/generate.ps1                              # Regenerate all
pwsh ./utils/generate.ps1 -Config libSkiaSharp.json    # Regenerate specific config