scene/layer.go

Functions Structs

Functions

func AcquireLayer

AcquireLayer gets a layer from the pool.

func (s *LayerStack) AcquireLayer() *LayerState {
	return s.pool.get()
}

func All

All returns all layers in the stack (bottom to top).

func (s *LayerStack) All() []*LayerState {
	return s.layers
}

func CombinedBounds

CombinedBounds returns the intersection of all clip bounds.

Returns an empty rect if any clip is empty.

func (s *ClipStack) CombinedBounds() Rect {
	if len(s.clips) == 0 {
		return Rect{
			MinX:	-1e30,
			MinY:	-1e30,
			MaxX:	1e30,
			MaxY:	1e30,
		}	// No clip = infinite bounds
	}

	result := s.clips[0].Bounds
	for i := 1; i < len(s.clips); i++ {
		// Intersect bounds
		result = Rect{
			MinX:	max32(result.MinX, s.clips[i].Bounds.MinX),
			MinY:	max32(result.MinY, s.clips[i].Bounds.MinY),
			MaxX:	min32(result.MaxX, s.clips[i].Bounds.MaxX),
			MaxY:	min32(result.MaxY, s.clips[i].Bounds.MaxY),
		}
		if result.IsEmpty() {
			return EmptyRect()
		}
	}

	return result
}

func Contains

Contains returns true if the point is inside the clip region.

This is a conservative test using the bounding box.

func (cs *ClipState) Contains(x, y float32) bool {
	if cs.Bounds.IsEmpty() {
		return false
	}
	return x >= cs.Bounds.MinX && x <= cs.Bounds.MaxX &&
		y >= cs.Bounds.MinY && y <= cs.Bounds.MaxY
}

func Contains

Contains returns true if the point is inside all clip regions.

func (s *ClipStack) Contains(x, y float32) bool {
	for _, clip := range s.clips {
		if !clip.Contains(x, y) {
			return false
		}
	}
	return true
}

func Depth

Depth returns the current stack depth.

func (s *ClipStack) Depth() int {
	return len(s.clips)
}

func Depth

Depth returns the current stack depth (1 = only root).

func (s *LayerStack) Depth() int {
	return len(s.layers)
}

func HasClip

HasClip returns true if the layer has a clip shape.

func (ls *LayerState) HasClip() bool {
	return ls.Clip != nil
}

func Intersects

Intersects returns true if the rectangle intersects the clip region.

This is a conservative test using bounding boxes.

func (cs *ClipState) Intersects(r Rect) bool {
	if cs.Bounds.IsEmpty() || r.IsEmpty() {
		return false
	}
	return !(r.MaxX < cs.Bounds.MinX || r.MinX > cs.Bounds.MaxX ||
		r.MaxY < cs.Bounds.MinY || r.MinY > cs.Bounds.MaxY)
}

func Intersects

Intersects returns true if the rectangle intersects all clip regions.

func (s *ClipStack) Intersects(r Rect) bool {
	for _, clip := range s.clips {
		if !clip.Intersects(r) {
			return false
		}
	}
	return true
}

func IsClipOnly

IsClipOnly returns true if this layer is only for clipping (no rendering).

func (k LayerKind) IsClipOnly() bool {
	return k == LayerClip
}

func IsEmpty

IsEmpty returns true if the layer has no content.

func (ls *LayerState) IsEmpty() bool {
	if ls.Kind == LayerClip {
		return ls.Clip == nil
	}
	return ls.Encoding == nil || ls.Encoding.IsEmpty()
}

func IsEmpty

IsEmpty returns true if there are no clips on the stack.

func (s *ClipStack) IsEmpty() bool {
	return len(s.clips) == 0
}

func IsRoot

IsRoot returns true if only the root layer is on the stack.

func (s *LayerStack) IsRoot() bool {
	return len(s.layers) == 1
}

func NeedsOffscreen

NeedsOffscreen returns true if this layer kind requires an offscreen buffer.

func (k LayerKind) NeedsOffscreen() bool {
	return k == LayerFiltered
}

func NewClipLayer

NewClipLayer creates a new clip-only layer.

func NewClipLayer(clip Shape) *LayerState {
	return &LayerState{
		Kind:		LayerClip,
		BlendMode:	BlendNormal,
		Alpha:		1.0,
		Clip:		clip,
		Encoding:	nil,	// Clip layers don't need their own encoding
		Bounds:		EmptyRect(),
		Transform:	IdentityAffine(),
	}
}

func NewClipStack

NewClipStack creates a new empty clip stack.

func NewClipStack() *ClipStack {
	return &ClipStack{
		clips: make([]*ClipState, 0, 4),
	}
}

func NewClipState

NewClipState creates a new clip state.

func NewClipState(shape Shape, transform Affine) *ClipState {
	var bounds Rect
	if shape != nil {
		bounds = shape.Bounds()
		// Transform bounds if needed
		if !transform.IsIdentity() {
			// Transform all four corners and compute new bounds
			corners := [][2]float32{
				{bounds.MinX, bounds.MinY},
				{bounds.MaxX, bounds.MinY},
				{bounds.MaxX, bounds.MaxY},
				{bounds.MinX, bounds.MaxY},
			}
			bounds = EmptyRect()
			for _, c := range corners {
				x, y := transform.TransformPoint(c[0], c[1])
				bounds = bounds.UnionPoint(x, y)
			}
		}
	} else {
		bounds = EmptyRect()
	}

	return &ClipState{
		Shape:		shape,
		Bounds:		bounds,
		Transform:	transform,
	}
}

func NewFilteredLayer

NewFilteredLayer creates a new layer for filter effects.

func NewFilteredLayer(blend BlendMode, alpha float32) *LayerState {
	return &LayerState{
		Kind:		LayerFiltered,
		BlendMode:	blend,
		Alpha:		clampAlpha(alpha),
		Encoding:	NewEncoding(),
		Bounds:		EmptyRect(),
		Transform:	IdentityAffine(),
	}
}

func NewLayerStack

NewLayerStack creates a new layer stack with a root layer.

func NewLayerStack() *LayerStack {
	stack := &LayerStack{
		layers:	make([]*LayerState, 0, 8),
		pool:	newLayerPool(),
	}
	// Create root layer
	root := stack.pool.get()
	root.Kind = LayerRegular
	root.BlendMode = BlendNormal
	root.Alpha = 1.0
	stack.layers = append(stack.layers, root)
	return stack
}

func NewLayerState

NewLayerState creates a new layer state with default values.

func NewLayerState(kind LayerKind, blend BlendMode, alpha float32) *LayerState {
	return &LayerState{
		Kind:		kind,
		BlendMode:	blend,
		Alpha:		clampAlpha(alpha),
		Encoding:	NewEncoding(),
		Bounds:		EmptyRect(),
		Transform:	IdentityAffine(),
	}
}

func Pop

Pop removes and returns the top clip region.

func (s *ClipStack) Pop() *ClipState {
	if len(s.clips) == 0 {
		return nil
	}
	clip := s.clips[len(s.clips)-1]
	s.clips = s.clips[:len(s.clips)-1]
	return clip
}

func Pop

Pop removes and returns the top layer.

Returns nil if only the root layer remains.

func (s *LayerStack) Pop() *LayerState {
	if len(s.layers) <= 1 {
		return nil	// Cannot pop root layer
	}
	layer := s.layers[len(s.layers)-1]
	s.layers = s.layers[:len(s.layers)-1]
	return layer
}

func Push

Push adds a clip region to the stack.

func (s *ClipStack) Push(clip *ClipState) {
	s.clips = append(s.clips, clip)
}

func Push

Push adds a new layer to the stack.

func (s *LayerStack) Push(layer *LayerState) {
	s.layers = append(s.layers, layer)
}

func ReleaseLayer

ReleaseLayer returns a layer to the pool.

func (s *LayerStack) ReleaseLayer(layer *LayerState) {
	s.pool.put(layer)
}

func Reset

Reset clears the stack, returning to just the root layer.

func (s *LayerStack) Reset() {
	// Return all non-root layers to pool
	for i := len(s.layers) - 1; i > 0; i-- {
		s.pool.put(s.layers[i])
	}
	s.layers = s.layers[:1]

	// Reset root layer
	if len(s.layers) > 0 {
		s.layers[0].Reset()
	}
}

func Reset

Reset clears the layer state for reuse.

func (ls *LayerState) Reset() {
	ls.Kind = LayerRegular
	ls.BlendMode = BlendNormal
	ls.Alpha = 1.0
	ls.Clip = nil
	if ls.Encoding != nil {
		ls.Encoding.Reset()
	}
	ls.Bounds = EmptyRect()
	ls.Transform = IdentityAffine()
	ls.ClipStackDepth = 0
}

func Reset

Reset clears the clip stack.

func (s *ClipStack) Reset() {
	s.clips = s.clips[:0]
}

func Root

Root returns the root (bottom) layer.

func (s *LayerStack) Root() *LayerState {
	if len(s.layers) == 0 {
		return nil
	}
	return s.layers[0]
}

func String

String returns a human-readable name for the layer kind.

func (k LayerKind) String() string {
	switch k {
	case LayerRegular:
		return "Regular"
	case LayerFiltered:
		return "Filtered"
	case LayerClip:
		return "Clip"
	default:
		return unknownStr
	}
}

func Top

Top returns the current clip region without removing it.

func (s *ClipStack) Top() *ClipState {
	if len(s.clips) == 0 {
		return nil
	}
	return s.clips[len(s.clips)-1]
}

func Top

Top returns the current (topmost) layer without removing it.

func (s *LayerStack) Top() *LayerState {
	if len(s.layers) == 0 {
		return nil
	}
	return s.layers[len(s.layers)-1]
}

func UpdateBounds

UpdateBounds expands the layer bounds to include the given rectangle.

func (ls *LayerState) UpdateBounds(r Rect) {
	ls.Bounds = ls.Bounds.Union(r)
}

Structs

type LayerState struct

LayerState represents the state of an active compositing layer.

Each layer has its own encoding for isolated rendering.

type LayerState struct {
	// Kind identifies the type of layer
	Kind	LayerKind

	// BlendMode specifies how this layer composites with layers below
	BlendMode	BlendMode

	// Alpha is the layer opacity (0.0 to 1.0)
	Alpha	float32

	// Clip is the optional clip shape for this layer.
	// If nil, the layer has no clip (infinite bounds).
	Clip	Shape

	// Encoding holds the layer's drawing commands.
	// This is populated as drawing commands are added to the scene
	// while this layer is active.
	Encoding	*Encoding

	// Bounds tracks the cumulative bounding box of layer content.
	// This is updated as content is added.
	Bounds	Rect

	// Transform is the transform active when the layer was pushed.
	// Used to restore transform state on pop.
	Transform	Affine

	// ClipStackDepth records the clip stack depth when layer was pushed.
	// Used to restore clip state on pop.
	ClipStackDepth	int
}

type LayerStack struct

LayerStack manages a stack of active layers.

The stack always has at least one layer (the root layer).

type LayerStack struct {
	layers	[]*LayerState
	pool	*layerPool
}

type ClipState struct

ClipState represents a clip region on the clip stack.

type ClipState struct {
	// Shape is the clip shape
	Shape	Shape

	// Bounds is the clip bounds (for quick rejection)
	Bounds	Rect

	// Transform is the transform that was active when clip was pushed
	Transform	Affine
}

type ClipStack struct

ClipStack manages a stack of clip regions.

type ClipStack struct {
	clips []*ClipState
}