scene/filter.go
Functions
func Add
func (fc *FilterChain) Add(f Filter) {
if f != nil {
fc.filters = append(fc.filters, f)
}
}
func Apply
Apply processes src through all filters in sequence.
For chains with more than one filter, temporary buffers are used.
func (fc *FilterChain) Apply(src, dst *gg.Pixmap, bounds Rect) {
if len(fc.filters) == 0 {
// No filters - copy src to dst if different
if src != dst {
copyPixmap(src, dst, bounds)
}
return
}
if len(fc.filters) == 1 {
// Single filter - direct application
fc.filters[0].Apply(src, dst, bounds)
return
}
// Multiple filters - need temporary buffer
// Calculate maximum expanded bounds
maxBounds := bounds
for _, f := range fc.filters {
maxBounds = f.ExpandBounds(maxBounds)
}
// Create temporary buffer
w := int(maxBounds.Width()) + 1
h := int(maxBounds.Height()) + 1
if w < 1 {
w = 1
}
if h < 1 {
h = 1
}
temp1 := gg.NewPixmap(w, h)
temp2 := gg.NewPixmap(w, h)
// Apply first filter: src -> temp1
fc.filters[0].Apply(src, temp1, bounds)
currentBounds := fc.filters[0].ExpandBounds(bounds)
// Apply middle filters: alternate between temp1 and temp2
current := temp1
next := temp2
for i := 1; i < len(fc.filters)-1; i++ {
fc.filters[i].Apply(current, next, currentBounds)
currentBounds = fc.filters[i].ExpandBounds(currentBounds)
current, next = next, current
}
// Apply last filter: current -> dst
fc.filters[len(fc.filters)-1].Apply(current, dst, currentBounds)
}
func ExpandBounds
ExpandBounds returns the combined expansion of all filters.
func (fc *FilterChain) ExpandBounds(input Rect) Rect {
result := input
for _, f := range fc.filters {
result = f.ExpandBounds(result)
}
return result
}
func ExpandsOutput
ExpandsOutput returns true if this filter type typically expands output bounds.
func (ft FilterType) ExpandsOutput() bool {
return ft == FilterBlur || ft == FilterDropShadow
}
func IsEmpty
IsEmpty returns true if the chain has no filters.
func (fc *FilterChain) IsEmpty() bool {
return len(fc.filters) == 0
}
func Len
Len returns the number of filters in the chain.
func (fc *FilterChain) Len() int {
return len(fc.filters)
}
func NewFilterChain
NewFilterChain creates a new filter chain from the given filters.
func NewFilterChain(filters ...Filter) *FilterChain {
chain := &FilterChain{
filters: make([]Filter, 0, len(filters)),
}
for _, f := range filters {
if f != nil {
chain.filters = append(chain.filters, f)
}
}
return chain
}
func String
String returns a human-readable name for the filter type.
func (ft FilterType) String() string {
switch ft {
case FilterNone:
return "None"
case FilterBlur:
return "Blur"
case FilterDropShadow:
return "DropShadow"
case FilterColorMatrix:
return "ColorMatrix"
default:
return unknownStr
}
}
Structs
type FilterChain struct
FilterChain represents multiple filters applied in sequence.
Filters are applied in order from first to last.
type FilterChain struct {
filters []Filter
}
Interfaces
type Filter interface
Filter applies visual effects to rendered layers.
Filters are applied during layer pop when LayerFiltered is used.
The filter interface supports both in-place and copy operations:
- If src == dst, the filter operates in-place when possible
- Some filters (like blur) may require temporary buffers internally
Bounds handling:
- Input bounds specify the region to process
- ExpandBounds returns how much the output grows (e.g., blur radius)
type Filter interface {
// Apply processes src pixmap and writes result to dst.
// bounds specifies the affected region in pixel coordinates.
// The filter may read pixels outside bounds but only writes within bounds.
Apply(src, dst *gg.Pixmap, bounds Rect)
// ExpandBounds returns the expanded bounds after filter application.
// This is used for buffer allocation:
// - Blur expands by radius in all directions
// - Shadow expands by offset + blur radius
// - Color matrix does not expand
ExpandBounds(input Rect) Rect
}
Add appends a filter to the chain.