Documentation
¶
Overview ¶
Package surface provides a unified surface abstraction for 2D rendering.
Surface is the core rendering target abstraction that decouples drawing operations from their implementation. This allows the same drawing code to work with:
- CPU-based software rendering (ImageSurface)
- GPU-accelerated rendering (GPUSurface)
- Third-party backends via registry
Architecture ¶
The surface package follows the Cairo/Skia pattern where surfaces are rendering targets independent of the drawing context. This separation enables:
- Backend switching without code changes
- Testing with mock surfaces
- Third-party backend integration (RFC #46)
Surface Types ¶
- ImageSurface: CPU-based rendering to *image.RGBA using core.AnalyticFiller
- GPUSurface: GPU-accelerated rendering wrapper (requires external device)
Registry (RFC #46) ¶
Third-party backends can register surfaces via the registry:
surface.Register("vulkan", func(w, h int, opts Options) (Surface, error) {
return NewVulkanSurface(w, h)
})
// Later:
s, err := surface.NewSurfaceByName("vulkan", 800, 600, nil)
Usage ¶
Basic usage with ImageSurface:
// Create a CPU-based surface
s := surface.NewImageSurface(800, 600)
defer s.Close()
// Clear with white background
s.Clear(color.White)
// Create a path
path := surface.NewPath()
path.MoveTo(100, 100)
path.LineTo(200, 100)
path.LineTo(150, 200)
path.Close()
// Fill with red
s.Fill(path, surface.FillStyle{
Color: color.RGBA{255, 0, 0, 255},
Rule: surface.FillRuleNonZero,
})
// Get the result
img := s.Snapshot()
Integration with gg.Context ¶
The surface package is designed to integrate with gg.Context (see INT-001). Context will use Surface as its rendering target, allowing backend switching.
References ¶
- Cairo: https://cairographics.org/manual/cairo-Image-Surfaces.html
- Skia: https://skia.org/docs/user/api/skcanvas_overview/
- RFC #46: Third-party backend support
Index ¶
- Variables
- func Available() []string
- func List() []string
- func Register(name string, priority int, factory SurfaceFactory, available func() bool)
- func Unregister(name string)
- type BackendNotFoundError
- type BackendUnavailableError
- type BlendMode
- type BlendableSurface
- type Capabilities
- type CapableSurface
- type ClippableSurface
- type DrawImageOptions
- type FillRule
- type FillStyle
- type Filter
- type GPUBackend
- type GPUSurface
- func (s *GPUSurface) Backend() GPUBackend
- func (s *GPUSurface) Capabilities() Capabilities
- func (s *GPUSurface) Clear(c color.Color)
- func (s *GPUSurface) Close() error
- func (s *GPUSurface) DrawImage(img image.Image, at Point, opts *DrawImageOptions)
- func (s *GPUSurface) Fill(path *Path, style FillStyle)
- func (s *GPUSurface) Flush() error
- func (s *GPUSurface) Height() int
- func (s *GPUSurface) Snapshot() *image.RGBA
- func (s *GPUSurface) Stroke(path *Path, style StrokeStyle)
- func (s *GPUSurface) Width() int
- type ImageSurface
- func (s *ImageSurface) Capabilities() Capabilities
- func (s *ImageSurface) Clear(c color.Color)
- func (s *ImageSurface) Close() error
- func (s *ImageSurface) DrawImage(img image.Image, at Point, opts *DrawImageOptions)
- func (s *ImageSurface) Fill(path *Path, style FillStyle)
- func (s *ImageSurface) Flush() error
- func (s *ImageSurface) Height() int
- func (s *ImageSurface) Image() *image.RGBA
- func (s *ImageSurface) Snapshot() *image.RGBA
- func (s *ImageSurface) Stroke(path *Path, style StrokeStyle)
- func (s *ImageSurface) Width() int
- type LineCap
- type LineJoin
- type Options
- type Path
- func (p *Path) Arc(cx, cy, r, angle1, angle2 float64)
- func (p *Path) Bounds() (minX, minY, maxX, maxY float64)
- func (p *Path) Circle(cx, cy, r float64)
- func (p *Path) Clear()
- func (p *Path) Clone() *Path
- func (p *Path) Close()
- func (p *Path) CubicTo(c1x, c1y, c2x, c2y, x, y float64)
- func (p *Path) CurrentPoint() Point
- func (p *Path) Ellipse(cx, cy, rx, ry float64)
- func (p *Path) IsEmpty() bool
- func (p *Path) LineTo(x, y float64)
- func (p *Path) MoveTo(x, y float64)
- func (p *Path) Points() []float32
- func (p *Path) QuadTo(cx, cy, x, y float64)
- func (p *Path) Rectangle(x, y, w, h float64)
- func (p *Path) RoundedRectangle(x, y, w, h, r float64)
- func (p *Path) Verbs() []raster.PathVerb
- type Pattern
- type Point
- type Registry
- func (r *Registry) Available() []string
- func (r *Registry) Get(name string) (*RegistryEntry, bool)
- func (r *Registry) List() []string
- func (r *Registry) NewSurface(opts Options) (Surface, error)
- func (r *Registry) NewSurfaceByName(name string, opts Options) (Surface, error)
- func (r *Registry) Register(name string, priority int, factory SurfaceFactory, available func() bool)
- func (r *Registry) Unregister(name string)
- type RegistryEntry
- type ResizableSurface
- type SolidPattern
- type StrokeStyle
- func (s StrokeStyle) IsDashed() bool
- func (s StrokeStyle) WithCap(lineCap LineCap) StrokeStyle
- func (s StrokeStyle) WithColor(c color.Color) StrokeStyle
- func (s StrokeStyle) WithDash(pattern []float64, offset float64) StrokeStyle
- func (s StrokeStyle) WithJoin(join LineJoin) StrokeStyle
- func (s StrokeStyle) WithMiterLimit(limit float64) StrokeStyle
- func (s StrokeStyle) WithPattern(p Pattern) StrokeStyle
- func (s StrokeStyle) WithWidth(w float64) StrokeStyle
- type SubSurface
- type Surface
- type SurfaceFactory
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoBackendAvailable is returned when no surface backends are registered // or available on the current system. ErrNoBackendAvailable = errors.New("surface: no backend available") )
Errors.
Functions ¶
func Available ¶
func Available() []string
Available returns names of all available backends sorted by priority.
func List ¶
func List() []string
List returns all registered backend names sorted by priority (highest first).
func Register ¶
func Register(name string, priority int, factory SurfaceFactory, available func() bool)
Register adds a backend to the global registry.
Parameters:
- name: unique identifier (e.g., "vulkan", "metal", "software")
- priority: selection priority (higher = preferred)
- factory: function to create surface instances
- available: function to check if backend is available
If available is nil, the backend is assumed always available. Registering a name that already exists replaces the previous entry.
func Unregister ¶
func Unregister(name string)
Unregister removes a backend from the global registry.
Types ¶
type BackendNotFoundError ¶
type BackendNotFoundError struct {
Name string
}
BackendNotFoundError indicates a named backend is not registered.
func (*BackendNotFoundError) Error ¶
func (e *BackendNotFoundError) Error() string
type BackendUnavailableError ¶
type BackendUnavailableError struct {
}
BackendUnavailableError indicates a backend exists but is not available.
func (*BackendUnavailableError) Error ¶
func (e *BackendUnavailableError) Error() string
type BlendMode ¶
type BlendMode uint8
BlendMode specifies how source and destination colors are combined.
const ( // BlendModeSourceOver is the default Porter-Duff source-over mode. BlendModeSourceOver BlendMode = iota // BlendModeMultiply multiplies source and destination colors. BlendModeMultiply // BlendModeScreen is the inverse of multiply. BlendModeScreen // BlendModeOverlay combines multiply and screen. BlendModeOverlay // BlendModeClear clears the destination. BlendModeClear // BlendModeCopy replaces destination with source. BlendModeCopy )
type BlendableSurface ¶
type BlendableSurface interface {
Surface
// SetBlendMode sets the blend mode for subsequent operations.
SetBlendMode(mode BlendMode)
// BlendMode returns the current blend mode.
BlendMode() BlendMode
}
BlendableSurface is an optional interface for surfaces with blend mode support.
type Capabilities ¶
type Capabilities struct {
// SupportsSubSurface indicates CreateSubSurface is available.
SupportsSubSurface bool
// SupportsResize indicates Resize is available.
SupportsResize bool
// SupportsClipping indicates clipping operations are available.
SupportsClipping bool
// SupportsBlendModes indicates blend mode control is available.
SupportsBlendModes bool
// SupportsAntialias indicates anti-aliased rendering is available.
SupportsAntialias bool
// MaxWidth is the maximum supported width (0 = unlimited).
MaxWidth int
// MaxHeight is the maximum supported height (0 = unlimited).
MaxHeight int
}
Capabilities describes the optional features a surface supports.
type CapableSurface ¶
type CapableSurface interface {
Surface
// Capabilities returns the surface's capabilities.
Capabilities() Capabilities
}
CapableSurface is an optional interface for querying surface capabilities.
type ClippableSurface ¶
type ClippableSurface interface {
Surface
// SetClip sets the clipping region.
// Only pixels within the path will be affected by subsequent operations.
SetClip(path *Path)
// ClearClip removes the clipping region.
ClearClip()
// PushClip saves the current clip state.
PushClip()
// PopClip restores the previous clip state.
PopClip()
}
ClippableSurface is an optional interface for surfaces with clipping support.
type DrawImageOptions ¶
type DrawImageOptions struct {
// SrcRect is the source rectangle within the image.
// If nil, the entire image is used.
SrcRect *image.Rectangle
// DstRect is the destination rectangle on the surface.
// If nil, the image is drawn at At with its original size.
DstRect *image.Rectangle
// Alpha is the opacity (0.0 = transparent, 1.0 = opaque).
// Default: 1.0
Alpha float64
// Filter is the interpolation mode for scaling.
Filter Filter
}
DrawImageOptions defines options for drawing images.
func DefaultDrawImageOptions ¶
func DefaultDrawImageOptions() *DrawImageOptions
DefaultDrawImageOptions returns DrawImageOptions with default values.
type FillRule ¶
type FillRule uint8
FillRule specifies how to determine which areas are inside a path.
type FillStyle ¶
type FillStyle struct {
// Color is the fill color.
Color color.Color
// Rule is the fill rule (NonZero or EvenOdd).
Rule FillRule
// Pattern is an optional pattern brush for complex fills.
// When set, takes precedence over Color.
Pattern Pattern
}
FillStyle defines how to fill a path.
func DefaultFillStyle ¶
func DefaultFillStyle() FillStyle
DefaultFillStyle returns a FillStyle with default values. Uses black color and non-zero fill rule.
func (FillStyle) WithPattern ¶
WithPattern returns a copy with the specified pattern.
type GPUBackend ¶
type GPUBackend interface {
// Clear fills the surface with a color.
Clear(c color.Color)
// Fill fills a path with the given style.
Fill(path *Path, style FillStyle)
// Stroke strokes a path with the given style.
Stroke(path *Path, style StrokeStyle)
// DrawImage draws an image at the specified position.
DrawImage(img image.Image, at Point, opts *DrawImageOptions)
// Flush ensures all pending operations are submitted.
Flush() error
// Readback reads the surface contents to an image.
Readback() (*image.RGBA, error)
// Close releases GPU resources.
Close() error
}
GPUBackend is the interface that GPU implementations must provide.
This abstraction allows different GPU backends (Vulkan, Metal, D3D12) to be used with the same Surface API.
type GPUSurface ¶
type GPUSurface struct {
// contains filtered or unexported fields
}
GPUSurface is a GPU-accelerated surface wrapper.
This is a minimal stub implementation that wraps an external GPU backend. The actual GPU implementation is provided by the backend (e.g., gogpu/wgpu).
To use GPUSurface, you must provide a GPUBackend implementation. This allows gg to remain independent of specific GPU libraries.
Example integration with gogpu:
// In gogpu package:
type gogpuBackend struct {
device *wgpu.Device
queue *wgpu.Queue
// ...
}
func (b *gogpuBackend) Clear(c color.Color) { ... }
func (b *gogpuBackend) Fill(path *surface.Path, style surface.FillStyle) { ... }
// ... implement GPUBackend interface
// Register the backend:
surface.Register("vulkan", 100, func(opts surface.Options) (surface.Surface, error) {
backend := createVulkanBackend(opts)
return surface.NewGPUSurface(opts.Width, opts.Height, backend), nil
}, vulkanAvailable)
func NewGPUSurface ¶
func NewGPUSurface(width, height int, backend GPUBackend) (*GPUSurface, error)
NewGPUSurface creates a new GPU surface with the given backend. Returns an error if backend is nil.
func (*GPUSurface) Backend ¶
func (s *GPUSurface) Backend() GPUBackend
Backend returns the underlying GPU backend. Returns nil if the surface is closed.
func (*GPUSurface) Capabilities ¶
func (s *GPUSurface) Capabilities() Capabilities
Capabilities returns the surface capabilities.
func (*GPUSurface) Clear ¶
func (s *GPUSurface) Clear(c color.Color)
Clear fills the entire surface with the given color.
func (*GPUSurface) Close ¶
func (s *GPUSurface) Close() error
Close releases all resources associated with the surface.
func (*GPUSurface) DrawImage ¶
func (s *GPUSurface) DrawImage(img image.Image, at Point, opts *DrawImageOptions)
DrawImage draws an image at the specified position.
func (*GPUSurface) Fill ¶
func (s *GPUSurface) Fill(path *Path, style FillStyle)
Fill fills the given path using the specified style.
func (*GPUSurface) Flush ¶
func (s *GPUSurface) Flush() error
Flush ensures all pending operations are complete.
func (*GPUSurface) Snapshot ¶
func (s *GPUSurface) Snapshot() *image.RGBA
Snapshot returns the current surface contents as an image. This performs a GPU readback, which may be slow.
func (*GPUSurface) Stroke ¶
func (s *GPUSurface) Stroke(path *Path, style StrokeStyle)
Stroke strokes the given path using the specified style.
type ImageSurface ¶
type ImageSurface struct {
// contains filtered or unexported fields
}
ImageSurface is a CPU-based surface that renders to an *image.RGBA.
It uses raster.AnalyticFiller for high-quality anti-aliased rendering. This is the default surface implementation for software rendering.
Example:
s := surface.NewImageSurface(800, 600)
defer s.Close()
s.Clear(color.White)
path := surface.NewPath()
path.Circle(400, 300, 100)
s.Fill(path, surface.FillStyle{Color: color.RGBA{255, 0, 0, 255}})
img := s.Snapshot()
func NewImageSurface ¶
func NewImageSurface(width, height int) *ImageSurface
NewImageSurface creates a new CPU-based surface with the given dimensions.
func NewImageSurfaceFromImage ¶
func NewImageSurfaceFromImage(img *image.RGBA) *ImageSurface
NewImageSurfaceFromImage creates a surface backed by an existing image. The surface will render into the provided image directly.
func (*ImageSurface) Capabilities ¶
func (s *ImageSurface) Capabilities() Capabilities
Capabilities returns the surface capabilities.
func (*ImageSurface) Clear ¶
func (s *ImageSurface) Clear(c color.Color)
Clear fills the entire surface with the given color.
func (*ImageSurface) Close ¶
func (s *ImageSurface) Close() error
Close releases resources associated with the surface.
func (*ImageSurface) DrawImage ¶
func (s *ImageSurface) DrawImage(img image.Image, at Point, opts *DrawImageOptions)
DrawImage draws an image at the specified position.
func (*ImageSurface) Fill ¶
func (s *ImageSurface) Fill(path *Path, style FillStyle)
Fill fills the given path using the specified style.
func (*ImageSurface) Flush ¶
func (s *ImageSurface) Flush() error
Flush ensures all pending operations are complete. For ImageSurface, this is a no-op.
func (*ImageSurface) Height ¶
func (s *ImageSurface) Height() int
Height returns the surface height.
func (*ImageSurface) Image ¶
func (s *ImageSurface) Image() *image.RGBA
Image returns the underlying image.RGBA. This is a direct reference, not a copy.
func (*ImageSurface) Snapshot ¶
func (s *ImageSurface) Snapshot() *image.RGBA
Snapshot returns a copy of the current surface contents.
func (*ImageSurface) Stroke ¶
func (s *ImageSurface) Stroke(path *Path, style StrokeStyle)
Stroke strokes the given path using the specified style.
type Options ¶
type Options struct {
// Width is the surface width in pixels.
Width int
// Height is the surface height in pixels.
Height int
// Antialias enables anti-aliased rendering.
// Default: true
Antialias bool
// BackgroundColor is the initial background color.
// Default: transparent
BackgroundColor color.Color
// Custom options for specific backends.
Custom map[string]any
}
Options configures surface creation.
func DefaultOptions ¶
DefaultOptions returns Options with default values.
type Path ¶
type Path struct {
// contains filtered or unexported fields
}
Path represents a vector path for drawing operations.
Path is the surface-level path type that wraps raster.PathLike. It provides a convenient builder API for constructing paths.
Example:
p := surface.NewPath() p.MoveTo(100, 100) p.LineTo(200, 100) p.LineTo(150, 200) p.Close() surface.Fill(p, style)
func (*Path) Arc ¶
Arc adds a circular arc to the path. The arc goes from angle1 to angle2 (in radians) around (cx, cy).
func (*Path) Bounds ¶
Bounds returns the axis-aligned bounding box of the path. Returns an empty rectangle if the path is empty.
func (*Path) Close ¶
func (p *Path) Close()
Close closes the current subpath by connecting to the start point.
func (*Path) CubicTo ¶
CubicTo adds a cubic Bezier curve from the current point. (c1x, c1y) and (c2x, c2y) are control points, (x, y) is the endpoint.
func (*Path) CurrentPoint ¶
CurrentPoint returns the current point.
func (*Path) QuadTo ¶
QuadTo adds a quadratic Bezier curve from the current point. (cx, cy) is the control point, (x, y) is the endpoint.
func (*Path) RoundedRectangle ¶
RoundedRectangle adds a rectangle with rounded corners.
type Pattern ¶
type Pattern interface {
// ColorAt returns the color at the given coordinates.
ColorAt(x, y float64) color.Color
}
Pattern is a color source that can vary across the surface. Used for gradients and image patterns.
type Point ¶
type Point struct {
X, Y float64
}
Point represents a 2D point with float64 coordinates.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages registered surface backends.
The registry enables third-party backends to register themselves without requiring changes to the core library (RFC #46).
Example registration:
func init() {
surface.Register("vulkan", 100, vulkanFactory, vulkanAvailable)
}
Example usage:
s, err := surface.NewSurfaceByName("vulkan", 800, 600)
// or auto-select best available:
s, err := surface.NewSurface(800, 600)
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a new empty registry. Most code should use the global registry via Register and NewSurface.
func (*Registry) Get ¶
func (r *Registry) Get(name string) (*RegistryEntry, bool)
Get returns information about a specific backend.
func (*Registry) NewSurface ¶
NewSurface creates a surface using the best available backend.
func (*Registry) NewSurfaceByName ¶
NewSurfaceByName creates a surface using a specific backend.
func (*Registry) Register ¶
func (r *Registry) Register(name string, priority int, factory SurfaceFactory, available func() bool)
Register adds a backend to this registry.
func (*Registry) Unregister ¶
Unregister removes a backend from this registry.
type RegistryEntry ¶
type RegistryEntry struct {
// Name is the unique identifier for this backend.
Name string
// Priority determines selection order (higher = preferred).
// Standard priorities:
// - 100: GPU backends (Vulkan, Metal, D3D12)
// - 50: Hardware-accelerated software (WARP)
// - 10: Pure software backends
Priority int
// Factory creates surface instances.
Factory SurfaceFactory
// Available reports if the backend is available on this system.
// This is called during registration and cached.
Available func() bool
}
RegistryEntry represents a registered surface backend.
func Get ¶
func Get(name string) (*RegistryEntry, bool)
Get returns information about a specific backend.
type ResizableSurface ¶
type ResizableSurface interface {
Surface
// Resize changes the surface dimensions.
// Existing content may be discarded or preserved depending on implementation.
Resize(width, height int) error
}
ResizableSurface is an optional interface for surfaces that support resizing.
type SolidPattern ¶
SolidPattern is a pattern that returns a single color.
type StrokeStyle ¶
type StrokeStyle struct {
// Color is the stroke color.
Color color.Color
// Width is the line width in pixels.
Width float64
// Cap is the line cap style.
Cap LineCap
// Join is the line join style.
Join LineJoin
// MiterLimit is the limit for miter joins.
// When the miter length exceeds this, a bevel join is used instead.
MiterLimit float64
// DashPattern defines the dash/gap pattern.
// nil or empty means solid line.
DashPattern []float64
// DashOffset is the starting offset into the dash pattern.
DashOffset float64
// Pattern is an optional pattern brush for complex strokes.
// When set, takes precedence over Color.
Pattern Pattern
}
StrokeStyle defines how to stroke a path.
func DefaultStrokeStyle ¶
func DefaultStrokeStyle() StrokeStyle
DefaultStrokeStyle returns a StrokeStyle with default values. Uses black color, 1px width, butt caps, miter joins.
func (StrokeStyle) IsDashed ¶
func (s StrokeStyle) IsDashed() bool
IsDashed returns true if this style has a dash pattern.
func (StrokeStyle) WithCap ¶
func (s StrokeStyle) WithCap(lineCap LineCap) StrokeStyle
WithCap returns a copy with the specified cap style.
func (StrokeStyle) WithColor ¶
func (s StrokeStyle) WithColor(c color.Color) StrokeStyle
WithColor returns a copy with the specified color.
func (StrokeStyle) WithDash ¶
func (s StrokeStyle) WithDash(pattern []float64, offset float64) StrokeStyle
WithDash returns a copy with the specified dash pattern.
func (StrokeStyle) WithJoin ¶
func (s StrokeStyle) WithJoin(join LineJoin) StrokeStyle
WithJoin returns a copy with the specified join style.
func (StrokeStyle) WithMiterLimit ¶
func (s StrokeStyle) WithMiterLimit(limit float64) StrokeStyle
WithMiterLimit returns a copy with the specified miter limit.
func (StrokeStyle) WithPattern ¶
func (s StrokeStyle) WithPattern(p Pattern) StrokeStyle
WithPattern returns a copy with the specified pattern.
func (StrokeStyle) WithWidth ¶
func (s StrokeStyle) WithWidth(w float64) StrokeStyle
WithWidth returns a copy with the specified width.
type SubSurface ¶
type SubSurface interface {
Surface
// CreateSubSurface creates a new surface backed by a region of this surface.
// Drawing to the sub-surface affects the parent surface.
CreateSubSurface(bounds image.Rectangle) (Surface, error)
}
SubSurface is an optional interface for surfaces that support sub-regions.
type Surface ¶
type Surface interface {
// Width returns the surface width in pixels.
Width() int
// Height returns the surface height in pixels.
Height() int
// Clear fills the entire surface with the given color.
// This is typically the fastest way to reset the surface.
Clear(c color.Color)
// Fill fills the given path using the specified style.
// The path is not modified or consumed.
Fill(path *Path, style FillStyle)
// Stroke strokes the given path using the specified style.
// The path is not modified or consumed.
Stroke(path *Path, style StrokeStyle)
// DrawImage draws an image at the specified position.
// If opts is nil, default options are used.
DrawImage(img image.Image, at Point, opts *DrawImageOptions)
// Flush ensures all pending drawing operations are complete.
// For CPU surfaces, this is typically a no-op.
// For GPU surfaces, this may submit commands and wait for completion.
// Returns an error if flushing fails.
Flush() error
// Snapshot returns the current surface contents as an RGBA image.
// The returned image is a copy; modifications to it do not affect the surface.
// This may be slow for GPU surfaces as it requires readback.
Snapshot() *image.RGBA
// Close releases all resources associated with the surface.
// After Close, the surface must not be used.
// Close is idempotent; multiple calls are safe.
Close() error
}
Surface is the core rendering target abstraction.
A Surface represents a 2D canvas that can be drawn to. Implementations may use CPU-based software rendering, GPU acceleration, or any other backend.
Surfaces are NOT thread-safe. Each surface should be used from a single goroutine, or external synchronization must be used.
Example usage:
s := surface.NewImageSurface(800, 600)
defer s.Close()
s.Clear(color.White)
s.Fill(path, surface.FillStyle{Color: color.RGBA{255, 0, 0, 255}})
img := s.Snapshot()
func NewSurface ¶
NewSurface creates a surface using the best available backend. Returns an error if no backends are available.
func NewSurfaceByName ¶
NewSurfaceByName creates a surface using a specific named backend.
func NewSurfaceByNameWithOptions ¶
NewSurfaceByNameWithOptions creates a surface using a specific backend.
func NewSurfaceWithOptions ¶
NewSurfaceWithOptions creates a surface using the best available backend.
type SurfaceFactory ¶
SurfaceFactory creates a new Surface with the given options. Implementations should validate options and return descriptive errors.