gif

package
v0.0.24 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 7, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

gif

The gif package provides utilities for creating animated GIF images. It offers a builder-style API for constructing GIFs frame by frame with drawing primitives for shapes, lines, and fills. The package includes terminal emulation for converting terminal recordings to animated GIFs.

Usage Examples

Basic Animation
package main

import (
    "log"

    "github.com/deepnoodle-ai/wonton/gif"
)

func main() {
    // Create a 200x200 GIF
    g := gif.New(200, 200)

    // Add 20 frames
    for i := 0; i < 20; i++ {
        g.AddFrame(func(f *gif.Frame) {
            // Fill background
            f.Fill(gif.White)

            // Draw a moving circle
            x := 50 + i*5
            y := 100
            f.FillCircle(x, y, 20, gif.Red)
        })
    }

    // Save to file
    if err := g.Save("animation.gif"); err != nil {
        log.Fatal(err)
    }
}
Custom Frame Delays
g := gif.New(100, 100)

// Add frame with custom delay (in hundredths of a second)
g.AddFrameWithDelay(func(f *gif.Frame) {
    f.Fill(gif.Blue)
}, 50) // 500ms delay

g.AddFrameWithDelay(func(f *gif.Frame) {
    f.Fill(gif.Red)
}, 100) // 1 second delay

g.Save("slow-animation.gif")
Custom Palette
// Create a custom color palette
palette := gif.Palette{
    gif.White,
    gif.Black,
    gif.RGB(255, 100, 0),  // Orange
    gif.RGB(0, 150, 255),  // Light blue
    gif.RGB(255, 0, 255),  // Magenta
}

g := gif.NewWithPalette(150, 150, palette)

g.AddFrame(func(f *gif.Frame) {
    f.Fill(gif.White)
    f.FillCircle(75, 75, 40, gif.RGB(255, 100, 0))
})

g.Save("custom-colors.gif")
Drawing Shapes
g := gif.New(300, 300)

g.AddFrame(func(f *gif.Frame) {
    f.Fill(gif.White)

    // Draw rectangle outline
    f.DrawRect(50, 50, 100, 80, gif.Black)

    // Fill rectangle
    f.FillRect(180, 50, 100, 80, gif.Blue)

    // Draw circle outline
    f.DrawCircle(100, 200, 30, gif.Red)

    // Fill circle
    f.FillCircle(230, 200, 30, gif.Green)

    // Draw line
    f.DrawLine(20, 20, 280, 280, gif.Black)
})

g.Save("shapes.gif")
Loop Control
g := gif.New(100, 100)

// Play once (no loop)
g.SetLoopCount(-1)

// Loop 5 times
g.SetLoopCount(5)

// Loop forever (default)
g.SetLoopCount(0)

// Add frames...
g.Save("looping.gif")
Working with Pixels
g := gif.New(100, 100)

g.AddFrame(func(f *gif.Frame) {
    f.Fill(gif.White)

    // Set individual pixels
    for x := 0; x < f.Width(); x++ {
        for y := 0; y < f.Height(); y++ {
            // Create a gradient
            intensity := uint8(x * 255 / f.Width())
            color := gif.RGB(intensity, intensity, intensity)
            f.SetPixel(x, y, color)
        }
    }
})

g.Save("gradient.gif")
Using Palette Indices
palette := gif.Palette{gif.White, gif.Black, gif.Red, gif.Green, gif.Blue}
g := gif.NewWithPalette(100, 100, palette)

g.AddFrame(func(f *gif.Frame) {
    // Fast pixel setting using palette indices
    for y := 0; y < f.Height(); y++ {
        for x := 0; x < f.Width(); x++ {
            idx := uint8((x + y) % len(palette))
            f.SetPixelIndex(x, y, idx)
        }
    }
})

g.Save("indexed.gif")
Bouncing Ball Animation
g := gif.New(200, 200)

ballY := 20
velocityY := 3
gravity := 1

for i := 0; i < 100; i++ {
    g.AddFrame(func(f *gif.Frame) {
        f.Fill(gif.White)
        f.FillCircle(100, ballY, 15, gif.Red)
    })

    ballY += velocityY
    velocityY += gravity

    // Bounce when hitting bottom
    if ballY >= 180 {
        velocityY = -velocityY * 9 / 10 // Damping
        ballY = 180
    }
}

g.Save("bouncing-ball.gif")
Grayscale Palette
// Create grayscale palette with 256 shades
palette := gif.Grayscale(256)
g := gif.NewWithPalette(200, 200, palette)

g.AddFrame(func(f *gif.Frame) {
    for y := 0; y < f.Height(); y++ {
        for x := 0; x < f.Width(); x++ {
            // Create radial gradient
            dx := x - 100
            dy := y - 100
            dist := int(math.Sqrt(float64(dx*dx + dy*dy)))
            shade := uint8(255 - min(dist*2, 255))
            f.SetPixel(x, y, gif.RGB(shade, shade, shade))
        }
    }
})

g.Save("grayscale.gif")
Web-Safe Colors
// Use 216-color web-safe palette
palette := gif.WebSafe()
g := gif.NewWithPalette(300, 300, palette)

// Draw with web-safe colors
g.AddFrame(func(f *gif.Frame) {
    f.Fill(gif.White)
    // Colors will be mapped to nearest web-safe color
    f.FillCircle(150, 150, 100, gif.RGB(123, 45, 67))
})

g.Save("websafe.gif")
Exporting to Bytes
g := gif.New(100, 100)

// Add frames...

// Get as byte slice instead of saving to file
data, err := g.Bytes()
if err != nil {
    log.Fatal(err)
}

// Use data (e.g., send over HTTP, embed in response)
Encoding to Writer
import (
    "bytes"
    "io"
)

g := gif.New(100, 100)
// Add frames...

var buf bytes.Buffer
if err := g.Encode(&buf); err != nil {
    log.Fatal(err)
}

// Use buf.Bytes() or write to any io.Writer

API Reference

Constructor Functions
Function Description Parameters Returns
New(width, height) Creates GIF with default palette int, int *GIF
NewWithPalette(w, h, pal) Creates GIF with custom palette int, int, Palette *GIF
GIF Methods
Method Description Parameters Returns
AddFrame(fn) Adds frame with 100ms delay func(*Frame) *GIF
AddFrameWithDelay(fn, delay) Adds frame with custom delay func(*Frame), int *GIF
AddImage(img, delay) Adds existing paletted image *image.Paletted, int *GIF
SetLoopCount(count) Sets loop count (0=forever, -1=once) int *GIF
Width() Returns GIF width - int
Height() Returns GIF height - int
FrameCount() Returns number of frames - int
Save(filename) Saves to file string error
Encode(w) Encodes to writer io.Writer error
Bytes() Returns as byte slice - ([]byte, error)
Frame Drawing Methods
Method Description Parameters Returns
SetPixel(x, y, color) Sets a single pixel int, int, color.Color -
SetPixelIndex(x, y, idx) Sets pixel by palette index int, int, uint8 -
Fill(color) Fills entire frame color.Color -
FillRect(x, y, w, h, c) Fills rectangle int, int, int, int, color.Color -
DrawRect(x, y, w, h, c) Draws rectangle outline int, int, int, int, color.Color -
DrawLine(x0, y0, x1, y1, c) Draws line (Bresenham) int, int, int, int, color.Color -
DrawCircle(cx, cy, r, c) Draws circle outline int, int, int, color.Color -
FillCircle(cx, cy, r, c) Fills circle int, int, int, color.Color -
Width() Returns frame width - int
Height() Returns frame height - int
Image() Returns underlying image - *image.Paletted
Color Functions
Function Description Parameters Returns
RGB(r, g, b) Creates RGB color uint8, uint8, uint8 color.RGBA
RGBA(r, g, b, a) Creates RGBA color uint8, uint8, uint8, uint8 color.RGBA
Grayscale(n) Creates n-shade grayscale palette int Palette
WebSafe() Creates 216-color web-safe palette - Palette
Pre-defined Colors
Constant Value
Black color.RGBA{0, 0, 0, 255}
White color.RGBA{255, 255, 255, 255}
Red color.RGBA{255, 0, 0, 255}
Green color.RGBA{0, 255, 0, 255}
Blue color.RGBA{0, 0, 255, 255}
Yellow color.RGBA{255, 255, 0, 255}
Cyan color.RGBA{0, 255, 255, 255}
Magenta color.RGBA{255, 0, 255, 255}
Transparent color.RGBA{0, 0, 0, 0}
Pre-defined Palettes
Constant Description Size
DefaultPalette Basic 8-color palette 8 colors
Types
Type Description
Palette Alias for []color.Color (max 256 colors)
GIF Animated GIF builder
Frame Single frame with drawing methods

Frame Timing

Frame delays are specified in hundredths of a second:

  • 10 = 100ms (10 FPS)
  • 5 = 50ms (20 FPS)
  • 3 = 30ms (33 FPS)
  • 2 = 20ms (50 FPS)

Default delay when using AddFrame() is 10 (100ms).

Palette Limitations

GIF format supports up to 256 colors per frame. Colors not in the palette are automatically matched to the nearest palette color.

Drawing Algorithms

  • Lines use Bresenham's line algorithm for efficiency
  • Circles use midpoint circle algorithm
  • Filled circles use simple pixel scanning

Performance Tips

  1. Use SetPixelIndex() instead of SetPixel() when possible (faster)
  2. Keep palettes small (fewer colors = smaller files)
  3. Use grayscale palettes for monochrome animations
  4. Consider frame delay vs. file size tradeoffs
  • color - Advanced color manipulation and gradients
  • terminal - Terminal graphics and animations
  • termsession - Terminal session recording

Implementation Notes

  • Core GIF encoding uses the standard library image/gif package
  • Text rendering uses golang.org/x/image for TTF fonts, with bitmap fallback
  • All coordinates are in pixels, origin at top-left (0, 0)
  • Drawing operations clip to frame boundaries automatically
  • GIF disposal method is set to DisposalBackground (clear to background)
  • Not thread-safe: do not modify frames concurrently

Documentation

Overview

Package gif provides utilities for creating animated GIF images with a clean, builder-style API. It includes comprehensive support for:

  • Frame-by-frame GIF creation with drawing primitives
  • Terminal emulation and ANSI escape sequence processing
  • Converting terminal recordings (.cast files) to animated GIFs
  • TTF and bitmap fonts for text rendering

The package is designed to work seamlessly with the termsession package for converting terminal recordings into animated GIFs, making it ideal for creating documentation and tutorials.

Basic GIF Creation

Create an animated GIF by building frames with drawing operations:

g := gif.New(100, 100)
for i := 0; i < 10; i++ {
    g.AddFrame(func(f *gif.Frame) {
        f.Fill(gif.White)
        f.FillCircle(50, 50+i*3, 10, gif.Red)
    })
}
g.Save("animation.gif")

Custom Palettes

GIFs support up to 256 colors per frame. Create custom palettes:

palette := gif.Palette{gif.White, gif.Black, gif.RGB(255, 0, 0)}
g := gif.NewWithPalette(100, 100, palette)

Terminal Recording to GIF

Convert asciinema recordings to animated GIFs:

opts := gif.DefaultCastOptions()
opts.FontSize = 14
opts.FPS = 10
g, err := gif.RenderCast("recording.cast", opts)
if err != nil {
    log.Fatal(err)
}
g.Save("demo.gif")

Terminal Emulation

Process raw terminal output with ANSI escape sequences:

emulator := gif.NewEmulator(80, 24)
emulator.ProcessOutput("\x1b[31mHello\x1b[0m World")

renderer := gif.NewTerminalRenderer(emulator.Screen(), 8)
renderer.RenderFrame(10)
renderer.Save("terminal.gif")

Drawing Primitives

The Frame type provides various drawing operations:

  • Fill, FillRect, FillCircle: Fill areas with color
  • DrawLine, DrawRect, DrawCircle: Draw outlines
  • SetPixel, SetPixelIndex: Set individual pixels

All drawing operations handle bounds checking automatically.

Example

Example demonstrates creating a simple animated GIF.

// Create a 100x100 pixel GIF
g := New(100, 100)

// Add 10 frames showing a moving circle
for i := 0; i < 10; i++ {
	g.AddFrame(func(f *Frame) {
		f.Fill(White)
		f.FillCircle(20+i*8, 50, 10, Red)
	})
}

// Save to file
if err := g.Save("animation.gif"); err != nil {
	fmt.Printf("Error: %v\n", err)
}

Index

Examples

Constants

View Source
const (
	FontWidth  = 8 // Default font width
	FontHeight = 8 // Default font height
)

Bitmap font dimension constants for backward compatibility.

Variables

View Source
var (
	// BitmapFont8x8 is the classic 8x8 pixel bitmap font, suitable for
	// compact displays and retro aesthetics.
	BitmapFont8x8 = BitmapFont{Width: 8, Height: 8}

	// BitmapFont8x16 is a taller 8x16 pixel font that provides better
	// readability for terminal rendering. This is the recommended default
	// for bitmap font usage.
	BitmapFont8x16 = BitmapFont{Width: 8, Height: 16}
)

Predefined bitmap fonts with different sizes.

View Source
var (
	Black       = color.RGBA{0, 0, 0, 255}
	White       = color.RGBA{255, 255, 255, 255}
	Red         = color.RGBA{255, 0, 0, 255}
	Green       = color.RGBA{0, 255, 0, 255}
	Blue        = color.RGBA{0, 0, 255, 255}
	Yellow      = color.RGBA{255, 255, 0, 255}
	Cyan        = color.RGBA{0, 255, 255, 255}
	Magenta     = color.RGBA{255, 0, 255, 255}
	Transparent = color.RGBA{0, 0, 0, 0}
)

Common colors for convenience.

View Source
var DefaultPalette = Palette{
	White, Black, Red, Green, Blue, Yellow, Cyan, Magenta,
}

DefaultPalette provides a basic palette with common colors.

Functions

func DrawBitmapChar

func DrawBitmapChar(f *Frame, px, py int, char rune, fg color.Color, font BitmapFont)

DrawBitmapChar draws a single character using a bitmap font at the specified pixel position (px, py) on a GIF frame. The character is drawn in the foreground color, with the frame's existing background showing through empty pixels.

Supported fonts:

  • BitmapFont8x8: Classic 8x8 pixel font
  • BitmapFont8x16: Taller 8x16 font with better readability

Characters outside the ASCII printable range (32-126) are rendered as block characters.

Example:

g := gif.New(100, 20)
g.AddFrame(func(f *gif.Frame) {
    f.Fill(gif.Black)
    x := 5
    for _, ch := range "Hello" {
        gif.DrawBitmapChar(f, x, 5, ch, gif.White, gif.BitmapFont8x8)
        x += 8
    }
})

func RGB

func RGB(r, g, b uint8) color.RGBA

RGB creates an RGBA color from RGB values.

func RGBA

func RGBA(r, g, b, a uint8) color.RGBA

RGBA creates an RGBA color.

Types

type BitmapFont

type BitmapFont struct {
	Width  int // Character cell width in pixels
	Height int // Character cell height in pixels
}

BitmapFont represents a fixed-width bitmap font with specific dimensions. Bitmap fonts are faster to render than TTF fonts and require no external dependencies, but offer lower visual quality.

type CastInfo

type CastInfo struct {
	Width      int     // Terminal width in columns
	Height     int     // Terminal height in rows
	Duration   float64 // Total recording duration in seconds
	EventCount int     // Number of events in the recording
	Title      string  // Recording title from metadata
	Timestamp  int64   // Unix timestamp when recorded
}

CastInfo contains metadata about a terminal recording without rendering it. This is useful for inspecting recording properties before conversion.

func GetCastInfo

func GetCastInfo(castFile string) (*CastInfo, error)

GetCastInfo extracts metadata from a .cast file without rendering it to a GIF. This is useful for displaying recording information or making decisions about rendering options.

Example:

info, err := gif.GetCastInfo("demo.cast")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Recording: %dx%d, %.1fs duration, %d events\n",
    info.Width, info.Height, info.Duration, info.EventCount)

type CastOptions

type CastOptions struct {
	Cols      int       // Override terminal columns (0 = use recording dimensions)
	Rows      int       // Override terminal rows (0 = use recording dimensions)
	Speed     float64   // Playback speed multiplier (1.0 = normal, 2.0 = double speed)
	MaxIdle   float64   // Maximum idle time between events in seconds (default: 2.0, prevents long pauses)
	FPS       int       // Target frames per second for output GIF (default: 10)
	Padding   int       // Padding around terminal content in pixels (default: 8)
	Font      *FontFace // Custom TTF/OTF font (nil = use default Inconsolata)
	FontSize  float64   // Font size in points when using default font (default: 14)
	UseBitmap bool      // Force bitmap font instead of TTF (faster but lower quality)
}

CastOptions configures the conversion of terminal recordings (.cast files) to animated GIFs. It provides control over dimensions, timing, rendering quality, and font selection.

Use DefaultCastOptions to get sensible defaults, then customize as needed:

opts := gif.DefaultCastOptions()
opts.FontSize = 16
opts.FPS = 15
opts.Speed = 2.0  // Double speed playback

func DefaultCastOptions

func DefaultCastOptions() CastOptions

DefaultCastOptions returns sensible defaults for cast conversion.

type Emulator

type Emulator struct {
	// contains filtered or unexported fields
}

Emulator interprets ANSI escape sequences and maintains terminal screen state. It processes raw terminal output (including control characters and escape sequences) and updates a TerminalScreen buffer that can be rendered to GIF frames.

The emulator supports:

  • Standard ANSI color codes (30-37, 40-47, 90-97, 100-107)
  • 256-color mode (ESC[38;5;Nm and ESC[48;5;Nm)
  • True color / 24-bit RGB (ESC[38;2;R;G;Bm and ESC[48;2;R;G;Bm)
  • Cursor movement (CSI sequences: H, A, B, C, D, E, F, G, d)
  • Screen clearing (CSI J and K sequences)
  • Common control characters (newline, carriage return, backspace, tab)

Example:

emulator := gif.NewEmulator(80, 24)
emulator.ProcessOutput("Hello \x1b[31mRed\x1b[0m World\n")
emulator.ProcessOutput("Line 2")

renderer := gif.NewTerminalRenderer(emulator.Screen(), 8)
renderer.RenderFrame(10)
renderer.Save("output.gif")

func NewEmulator

func NewEmulator(cols, rows int) *Emulator

NewEmulator creates a new terminal emulator with the specified dimensions. The cols and rows parameters define the terminal size in character cells. Standard terminal sizes include 80x24 (classic), 80x25 (DOS), and 132x24.

func (*Emulator) ProcessOutput

func (e *Emulator) ProcessOutput(data string)

ProcessOutput processes raw terminal output containing printable characters, control characters, and ANSI escape sequences. It updates the screen state, cursor position, and text attributes accordingly.

Supported sequences include:

  • CSI sequences (ESC[...): cursor movement, colors, clearing
  • OSC sequences (ESC]...): ignored (window title, etc.)
  • Control characters: \n, \r, \b, \t, and printable ASCII

func (*Emulator) Reset

func (e *Emulator) Reset()

Reset clears the screen and resets all state to initial values. This includes clearing all text, resetting colors to defaults (white on black), and moving the cursor to the home position (0, 0).

func (*Emulator) Resize

func (e *Emulator) Resize(cols, rows int)

Resize changes the terminal dimensions, preserving as much existing content as possible. If the new size is smaller, content outside the bounds is lost. If larger, new areas are filled with spaces using default colors.

func (*Emulator) Screen

func (e *Emulator) Screen() *TerminalScreen

Screen returns the current terminal screen state, which contains the character cells, colors, and cursor position. The returned screen can be rendered to GIF frames using a TerminalRenderer.

func (*Emulator) Write

func (e *Emulator) Write(p []byte) (n int, err error)

Write implements io.Writer, allowing the emulator to be used as a writer for terminal output. It processes the data through ProcessOutput and always returns the full length written with no error.

This makes Emulator compatible with any code that writes to an io.Writer, such as terminal applications or command execution output.

type FontFace

type FontFace struct {
	// contains filtered or unexported fields
}

FontFace wraps a TrueType or OpenType font for high-quality text rendering to images. It maintains metrics for monospace character cells, making it suitable for terminal and grid-based text rendering.

FontFace calculates cell dimensions automatically based on the font metrics, ensuring consistent spacing for terminal emulation and text layouts.

func LoadDefaultFont

func LoadDefaultFont(size float64) (*FontFace, error)

LoadDefaultFont loads the embedded Inconsolata monospace font at the specified size in points. Inconsolata is a clean, highly readable monospace font ideal for terminal rendering and code display.

This is the recommended font for terminal GIF rendering, as it provides excellent readability and proper monospace character metrics.

Example:

font, err := gif.LoadDefaultFont(14)
if err != nil {
    log.Fatal(err)
}
defer font.Close()

// Use with terminal renderer
opts := gif.DefaultRendererOptions()
opts.Font = font
renderer := gif.NewTerminalRendererWithOptions(screen, opts)

func LoadFontFromBytes

func LoadFontFromBytes(data []byte, size float64) (*FontFace, error)

LoadFontFromBytes loads a TrueType (TTF) or OpenType (OTF) font from raw bytes at the specified size in points. The font is configured with standard 72 DPI and full hinting for best rendering quality.

This function is useful for embedding custom fonts or loading fonts from non-standard locations.

Example:

fontData, err := os.ReadFile("custom-font.ttf")
if err != nil {
    log.Fatal(err)
}
font, err := gif.LoadFontFromBytes(fontData, 14)
if err != nil {
    log.Fatal(err)
}
defer font.Close()

func (*FontFace) Ascent

func (ff *FontFace) Ascent() int

Ascent returns the font ascent (distance from baseline to top of tallest glyph) in pixels. This is used internally for positioning characters correctly on the baseline.

func (*FontFace) CellHeight

func (ff *FontFace) CellHeight() int

CellHeight returns the height of a single character cell in pixels. This includes the space needed for ascenders, descenders, and line spacing. This value is used to calculate total image height for terminal rendering.

func (*FontFace) CellWidth

func (ff *FontFace) CellWidth() int

CellWidth returns the width of a single character cell in pixels. For monospace fonts, all characters have the same width. This value is used to calculate total image width for terminal rendering.

func (*FontFace) Close

func (ff *FontFace) Close() error

Close releases resources associated with the font face. While most font faces don't require explicit cleanup, this method is provided for completeness and should be called when the font is no longer needed, typically using defer.

Example:

font, err := gif.LoadDefaultFont(14)
if err != nil {
    log.Fatal(err)
}
defer font.Close()

func (*FontFace) DrawChar

func (ff *FontFace) DrawChar(img draw.Image, px, py int, char rune, fg color.Color)

DrawChar draws a single character at the specified pixel position on the image. The position (px, py) is the top-left corner of the character cell. The character is drawn in the foreground color with proper baseline alignment.

Space characters and null runes are not drawn (optimization).

func (*FontFace) DrawString

func (ff *FontFace) DrawString(img draw.Image, px, py int, s string, fg color.Color)

DrawString draws a complete string at the specified pixel position. The position (px, py) is the top-left corner of the first character cell. Characters advance horizontally according to the font's advance width.

This method is primarily used internally by TerminalRenderer but can be used directly for custom text rendering needs.

type Frame

type Frame struct {
	// contains filtered or unexported fields
}

Frame represents a single frame being drawn.

func (*Frame) DrawCircle

func (f *Frame) DrawCircle(cx, cy, r int, c color.Color)

DrawCircle draws a circle outline using the midpoint algorithm.

Example

ExampleFrame_DrawCircle demonstrates drawing circles.

g := New(200, 200)

g.AddFrame(func(f *Frame) {
	f.Fill(White)

	// Draw concentric circles
	for r := 20; r <= 100; r += 20 {
		f.DrawCircle(100, 100, r, Black)
	}
})

g.Save("circles.gif")

func (*Frame) DrawLine

func (f *Frame) DrawLine(x0, y0, x1, y1 int, c color.Color)

DrawLine draws a line from (x0, y0) to (x1, y1) using Bresenham's algorithm.

Example

ExampleFrame_DrawLine demonstrates drawing lines on a frame.

g := New(200, 200)

g.AddFrame(func(f *Frame) {
	f.Fill(White)

	// Draw a grid
	for i := 0; i <= 200; i += 20 {
		f.DrawLine(i, 0, i, 200, RGB(200, 200, 200))
		f.DrawLine(0, i, 200, i, RGB(200, 200, 200))
	}

	// Draw diagonal lines
	f.DrawLine(0, 0, 200, 200, Red)
	f.DrawLine(200, 0, 0, 200, Blue)
})

g.Save("lines.gif")

func (*Frame) DrawRect

func (f *Frame) DrawRect(x, y, w, h int, c color.Color)

DrawRect draws a rectangle outline.

func (*Frame) Fill

func (f *Frame) Fill(c color.Color)

Fill fills the entire frame with a color.

func (*Frame) FillCircle

func (f *Frame) FillCircle(cx, cy, r int, c color.Color)

FillCircle fills a circle.

func (*Frame) FillRect

func (f *Frame) FillRect(x, y, w, h int, c color.Color)

FillRect fills a rectangle with a color.

func (*Frame) Height

func (f *Frame) Height() int

Height returns the frame height.

func (*Frame) Image

func (f *Frame) Image() *image.Paletted

Image returns the underlying paletted image for advanced manipulation.

func (*Frame) SetPixel

func (f *Frame) SetPixel(x, y int, c color.Color)

SetPixel sets a pixel to the given color. The color must be in the palette or it will be matched to the nearest color.

func (*Frame) SetPixelIndex

func (f *Frame) SetPixelIndex(x, y int, index uint8)

SetPixelIndex sets a pixel using a palette index directly. This is faster than SetPixel when you know the index.

func (*Frame) Width

func (f *Frame) Width() int

Width returns the frame width.

type GIF

type GIF struct {
	// contains filtered or unexported fields
}

GIF represents an animated GIF being constructed.

func New

func New(width, height int) *GIF

New creates a new GIF with the specified dimensions and the default palette. Dimensions must be positive; values less than 1 are clamped to 1.

Example

ExampleNew demonstrates creating a basic GIF with the default palette.

g := New(200, 100)

g.AddFrame(func(f *Frame) {
	f.Fill(White)
	f.FillRect(50, 25, 100, 50, Blue)
	f.DrawRect(49, 24, 102, 52, Black)
})

g.Save("simple.gif")

func NewWithPalette

func NewWithPalette(width, height int, palette Palette) *GIF

NewWithPalette creates a new GIF with a custom palette. Dimensions must be positive; values less than 1 are clamped to 1. Palette must have 1-256 colors; empty palettes use DefaultPalette, and palettes exceeding 256 colors are truncated.

Example

ExampleNewWithPalette demonstrates creating a GIF with a custom palette.

// Create a custom palette with grayscale colors
palette := Grayscale(16)

g := NewWithPalette(100, 100, palette)

// Create frames with different shades
for i := 0; i < 16; i++ {
	g.AddFrame(func(f *Frame) {
		f.Fill(palette[i])
	})
}

g.Save("grayscale.gif")

func RenderCast

func RenderCast(castFile string, opts CastOptions) (*GIF, error)

RenderCast converts an asciinema .cast file (terminal recording) to an animated GIF. It processes the terminal output events, applies ANSI escape sequences, and renders each frame with proper timing.

The function handles:

  • ANSI color codes (16-color, 256-color, and true color)
  • Cursor movement and text positioning
  • Screen clearing and line editing
  • Playback speed adjustment and idle time limiting

Example:

opts := gif.DefaultCastOptions()
opts.FontSize = 16
opts.Speed = 1.5  // Play at 1.5x speed
g, err := gif.RenderCast("demo.cast", opts)
if err != nil {
    log.Fatal(err)
}
g.Save("demo.gif")

The returned GIF can be saved with Save() or encoded with Encode().

func RenderCastEvents

func RenderCastEvents(header *termsession.RecordingHeader, events []termsession.RecordingEvent, opts CastOptions) (*GIF, error)

RenderCastEvents converts pre-loaded terminal recording events to an animated GIF. This function is useful when you already have the header and events in memory, or when you want to filter or modify events before rendering.

See RenderCast for higher-level usage that loads from a file directly.

func (*GIF) AddFrame

func (g *GIF) AddFrame(draw func(*Frame)) *GIF

AddFrame adds a new frame with the default delay (100ms). The draw function is called to render the frame content.

Example

ExampleGIF_AddFrame demonstrates adding frames with drawing operations.

g := New(150, 150)

// Add frames showing geometric shapes
g.AddFrame(func(f *Frame) {
	f.Fill(White)
	f.FillCircle(75, 75, 50, Red)
})

g.AddFrame(func(f *Frame) {
	f.Fill(White)
	f.FillRect(25, 25, 100, 100, Blue)
})

g.AddFrame(func(f *Frame) {
	f.Fill(White)
	f.DrawLine(0, 0, 150, 150, Green)
	f.DrawLine(150, 0, 0, 150, Green)
})

g.Save("shapes.gif")

func (*GIF) AddFrameWithDelay

func (g *GIF) AddFrameWithDelay(draw func(*Frame), delay int) *GIF

AddFrameWithDelay adds a new frame with a custom delay. Delay is in 100ths of a second (e.g., 10 = 100ms). Negative delays are clamped to 0.

func (*GIF) AddImage

func (g *GIF) AddImage(img *image.Paletted, delay int) *GIF

AddImage adds an existing paletted image as a frame. The image is added directly without palette conversion; ensure the image uses a compatible palette or colors may not display correctly. Nil images are ignored. Negative delays are clamped to 0.

func (*GIF) Bytes

func (g *GIF) Bytes() ([]byte, error)

Bytes returns the GIF as a byte slice.

func (*GIF) Encode

func (g *GIF) Encode(w io.Writer) error

Encode writes the GIF to an io.Writer.

func (*GIF) FrameCount

func (g *GIF) FrameCount() int

FrameCount returns the number of frames added so far.

func (*GIF) Height

func (g *GIF) Height() int

Height returns the GIF height.

func (*GIF) Save

func (g *GIF) Save(filename string) error

Save writes the GIF to a file.

func (*GIF) SetLoopCount

func (g *GIF) SetLoopCount(count int) *GIF

SetLoopCount sets the number of times the animation should loop. 0 means loop forever, -1 means no loop (play once).

Example

ExampleGIF_SetLoopCount demonstrates controlling animation looping.

g := New(100, 50)

// Set to loop 3 times (not infinite)
g.SetLoopCount(3)

for i := 0; i < 5; i++ {
	g.AddFrameWithDelay(func(f *Frame) {
		f.Fill(White)
		f.FillCircle(10+i*20, 25, 10, Red)
	}, 20) // 200ms delay
}

g.Save("limited-loop.gif")

func (*GIF) Width

func (g *GIF) Width() int

Width returns the GIF width.

type Palette

type Palette []color.Color

Palette is a slice of colors used for GIF frames. GIFs support up to 256 colors per frame.

func Grayscale

func Grayscale(n int) Palette

Grayscale creates a grayscale palette with n shades from black to white.

Example

ExampleGrayscale demonstrates creating a grayscale palette.

// Create a 32-shade grayscale palette
palette := Grayscale(32)

g := NewWithPalette(320, 100, palette)

g.AddFrame(func(f *Frame) {
	// Draw a gradient bar showing all shades
	for i := 0; i < 32; i++ {
		f.FillRect(i*10, 0, 10, 100, palette[i])
	}
})

g.Save("gradient.gif")

func Terminal256 added in v0.0.7

func Terminal256() Palette

Terminal256 creates the standard xterm 256-color palette used by modern terminal emulators. This palette provides excellent coverage for terminal rendering with anti-aliased text, as it includes:

  • Colors 0-15: Standard 16 ANSI colors (8 normal + 8 bright)
  • Colors 16-231: 6×6×6 RGB color cube (216 colors)
  • Colors 232-255: 24-shade grayscale ramp

This is the recommended palette for terminal GIF rendering, as the color cube provides smooth intermediate shades for anti-aliased font edges.

Example:

palette := gif.Terminal256()
g := gif.NewWithPalette(800, 600, palette)
Example

ExampleTerminal256 demonstrates creating a terminal-style palette.

// Use the standard xterm 256-color palette for terminal rendering
palette := Terminal256()

g := NewWithPalette(400, 300, palette)

g.AddFrame(func(f *Frame) {
	// Dark background like a terminal
	f.Fill(palette[0]) // Black

	// The palette includes all colors needed for anti-aliased text
})

g.Save("terminal.gif")

func WebSafe

func WebSafe() Palette

WebSafe creates the 216-color web-safe palette.

type RendererOptions

type RendererOptions struct {
	Font       *FontFace  // Custom TTF/OTF font (nil = use default Inconsolata or bitmap)
	FontSize   float64    // Font size in points for default TTF (default: 14)
	UseBitmap  bool       // Force bitmap font instead of TTF (faster, lower quality)
	BitmapFont BitmapFont // Which bitmap font to use when UseBitmap=true (default: 8x16)
	Padding    int        // Padding in pixels around terminal content (default: 8)
}

RendererOptions configures how a TerminalRenderer converts terminal screens to GIF frames. It provides control over font selection, sizing, and layout.

Use DefaultRendererOptions() to get sensible defaults, then customize:

opts := gif.DefaultRendererOptions()
opts.FontSize = 16
opts.Padding = 10
renderer := gif.NewTerminalRendererWithOptions(screen, opts)

func DefaultRendererOptions

func DefaultRendererOptions() RendererOptions

DefaultRendererOptions returns sensible defaults for terminal rendering.

type TerminalCell

type TerminalCell struct {
	Char rune        // The Unicode character displayed in this cell
	FG   color.Color // Foreground (text) color
	BG   color.Color // Background color
}

TerminalCell represents a single character cell in a terminal screen, including the character itself and its foreground and background colors.

type TerminalRenderer

type TerminalRenderer struct {
	// contains filtered or unexported fields
}

TerminalRenderer converts a TerminalScreen buffer into animated GIF frames. It handles text rendering using either TTF fonts (for high quality) or bitmap fonts (for speed and simplicity), and manages the GIF creation process.

The renderer calculates pixel dimensions based on the terminal size and selected font, then renders each frame by drawing character cells with their foreground and background colors.

Example:

screen := gif.NewTerminalScreen(80, 24)
screen.WriteString("Hello World", gif.White, gif.Black)

renderer := gif.NewTerminalRenderer(screen, 8)
renderer.RenderFrame(10) // Add frame with 100ms delay
renderer.Save("output.gif")

func NewTerminalRenderer

func NewTerminalRenderer(screen *TerminalScreen, padding int) *TerminalRenderer

NewTerminalRenderer creates a renderer for the given terminal screen using the default TTF font (Inconsolata) for best rendering quality. The padding parameter specifies extra pixels around the terminal content.

This is a convenience constructor that uses DefaultRendererOptions with the specified padding. For more control over font selection and rendering options, use NewTerminalRendererWithOptions.

func NewTerminalRendererWithOptions

func NewTerminalRendererWithOptions(screen *TerminalScreen, opts RendererOptions) *TerminalRenderer

NewTerminalRendererWithOptions creates a renderer with full control over rendering options. This allows customizing font selection (TTF vs bitmap), font size, and padding.

The renderer automatically calculates pixel dimensions based on the terminal size and selected font metrics.

func (*TerminalRenderer) Bytes

func (tr *TerminalRenderer) Bytes() ([]byte, error)

Bytes returns the GIF animation as a byte slice, suitable for writing to HTTP responses or other byte-oriented outputs. This is a convenience method that calls Bytes on the underlying GIF object.

func (*TerminalRenderer) GIF

func (tr *TerminalRenderer) GIF() *GIF

GIF returns the underlying GIF object being constructed. This allows access to the GIF for additional manipulation or encoding options.

func (*TerminalRenderer) RenderFrame

func (tr *TerminalRenderer) RenderFrame(delay int)

RenderFrame captures the current terminal screen state and adds it as a frame to the GIF animation. The delay parameter specifies how long to display this frame, measured in hundredths of a second (e.g., 10 = 100ms, 50 = 500ms).

Call this method multiple times as the terminal screen changes to create an animation showing the terminal session progression.

func (*TerminalRenderer) Save

func (tr *TerminalRenderer) Save(filename string) error

Save writes the rendered GIF animation to a file. This is a convenience method that calls Save on the underlying GIF object.

func (*TerminalRenderer) SetLoopCount

func (tr *TerminalRenderer) SetLoopCount(count int) *TerminalRenderer

SetLoopCount sets how many times the GIF animation should loop. Use 0 for infinite looping (default), -1 to play once, or a positive number for a specific repeat count. Returns the renderer for method chaining.

type TerminalScreen

type TerminalScreen struct {
	Width     int              // Screen width in columns (character cells)
	Height    int              // Screen height in rows (character cells)
	Cells     [][]TerminalCell // 2D array of cells [row][column]
	CursorX   int              // Current cursor column (0-indexed)
	CursorY   int              // Current cursor row (0-indexed)
	DefaultFG color.Color      // Default foreground color (typically white)
	DefaultBG color.Color      // Default background color (typically black)
}

TerminalScreen represents a virtual terminal screen buffer with character cells arranged in rows and columns. It maintains the complete state needed to render terminal output, including all characters, their colors, and the cursor position.

The screen uses (0,0) as the top-left position, with X increasing to the right and Y increasing downward.

func NewTerminalScreen

func NewTerminalScreen(cols, rows int) *TerminalScreen

NewTerminalScreen creates a new terminal screen buffer with the specified dimensions. All cells are initialized with space characters and default colors.

func (*TerminalScreen) Clear

func (ts *TerminalScreen) Clear()

Clear clears the entire screen, filling all cells with spaces using the default foreground and background colors. The cursor is reset to position (0,0).

func (*TerminalScreen) MoveCursor

func (ts *TerminalScreen) MoveCursor(x, y int)

MoveCursor moves the cursor to the specified position, clamping coordinates to valid screen bounds. Negative values are clamped to 0, and values beyond the screen dimensions are clamped to the maximum valid position.

func (*TerminalScreen) SetCell

func (ts *TerminalScreen) SetCell(x, y int, char rune, fg, bg color.Color)

SetCell sets the character and colors for a cell at the specified position. If the position is out of bounds, the call is silently ignored. Nil colors are replaced with the default foreground or background colors.

func (*TerminalScreen) WriteString

func (ts *TerminalScreen) WriteString(s string, fg, bg color.Color)

WriteString writes a string to the screen starting at the current cursor position. The cursor advances as characters are written. Newlines (\n) move the cursor to the start of the next line. Carriage returns (\r) move to the start of the current line. The screen scrolls up if text extends beyond the bottom row.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL