Skip to content

xnano.image

xnano.image

xnano.image

xnano-image images that a terminal can show. Needs the optional image extra: pip install "xnano[image]".

Classes:

  • TerminalGraphics –

    Terminal graphics capabilities: the protocol to draw with and the cell size.

  • TerminalImage –

    A decoded, immutable image or animation.

TerminalGraphics

Terminal graphics capabilities: the protocol to draw with and the cell size.

Pass one as graphics= to TerminalImage.to_cell_grid or TerminalImage.to_cell_animation to draw with the sixel, kitty or iterm2 protocol instead of half-block characters. Instances are immutable and hashable; use with_protocol to get a copy with another protocol.

Maps to ratatui_image::picker::Picker.

Attributes:

Examples:

graphics = TerminalGraphics.detect()  # before the full-screen session starts
grid = TerminalImage.open('photo.png').to_cell_grid(40, 20, graphics=graphics)

Methods:

  • half_blocks –

    Capabilities that draw with half-block characters, which work in every terminal.

  • detect –

    Ask the terminal which graphics protocol and cell size it supports.

  • from_capabilities –

    Capabilities you already know, without querying the terminal.

  • with_protocol –

    A copy of these capabilities that draws with protocol.

protocol property

protocol: Literal['half_blocks', 'sixel', 'kitty', 'iterm2']

The graphics protocol images are drawn with.

cell_size_in_pixels property

cell_size_in_pixels: tuple[int, int]

The size of one terminal cell in pixels, (width, height).

capabilities property

capabilities: list[str]

The capabilities the terminal reported when it was detected.

Each is one of kitty, sixel, rectangular_operations, kitty_compression, cell_size, text_sizing_protocol or background_color. Empty unless built by detect.

half_blocks staticmethod

half_blocks() -> TerminalGraphics

Capabilities that draw with half-block characters, which work in every terminal.

The cell size is assumed to be (10, 20) pixels.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def half_blocks() -> TerminalGraphics:
    """
    Capabilities that draw with half-block characters, which work in every terminal.

    The cell size is assumed to be ``(10, 20)`` pixels.
    """

detect staticmethod

detect() -> TerminalGraphics

Ask the terminal which graphics protocol and cell size it supports.

The query writes escape sequences to the terminal and reads its answer from standard input, waiting up to two seconds. Call it once, before starting a full-screen session and before reading terminal events. A terminal that does not answer is treated as supporting half-blocks only.

Returns:

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def detect() -> TerminalGraphics:
    """
    Ask the terminal which graphics protocol and cell size it supports.

    The query writes escape sequences to the terminal and reads its answer
    from standard input, waiting up to two seconds. Call it once, before
    starting a full-screen session and before reading terminal events. A
    terminal that does not answer is treated as supporting half-blocks
    only.

    Returns:
        The detected capabilities.

    Raises:
        TerminalQueryError: If standard input or output is not a
            terminal, or the query cannot be written or read. Use
            [`from_capabilities`][xnano_image.TerminalGraphics.from_capabilities]
            or [`half_blocks`][xnano_image.TerminalGraphics.half_blocks]
            in that case.
    """

from_capabilities staticmethod

from_capabilities(*, protocol: Literal['half_blocks', 'sixel', 'kitty', 'iterm2'], cell_size_in_pixels: tuple[int, int]) -> TerminalGraphics

Capabilities you already know, without querying the terminal.

Use it when the terminal was queried elsewhere (for example by xnano.input.detect_terminal_capabilities()) or when a protocol must be forced.

Parameters:

  • protocol (Literal['half_blocks', 'sixel', 'kitty', 'iterm2']) –

    The graphics protocol to draw with.

  • cell_size_in_pixels (tuple[int, int]) –

    The size of one terminal cell, (width, height), each side between 1 and 1000 pixels.

Raises:

  • TerminalImageError –

    If protocol is not one of the four protocols or a side of cell_size_in_pixels is out of range.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def from_capabilities(
    *,
    protocol: Literal['half_blocks', 'sixel', 'kitty', 'iterm2'],
    cell_size_in_pixels: tuple[int, int],
) -> TerminalGraphics:
    """
    Capabilities you already know, without querying the terminal.

    Use it when the terminal was queried elsewhere (for example by
    ``xnano.input.detect_terminal_capabilities()``) or when a
    protocol must be forced.

    Args:
        protocol: The graphics protocol to draw with.
        cell_size_in_pixels: The size of one terminal cell, ``(width, height)``,
            each side between 1 and 1000 pixels.

    Raises:
        TerminalImageError: If ``protocol`` is not one of the four protocols or
            a side of ``cell_size_in_pixels`` is out of range.
    """

with_protocol

with_protocol(protocol: Literal['half_blocks', 'sixel', 'kitty', 'iterm2']) -> TerminalGraphics

A copy of these capabilities that draws with protocol.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def with_protocol(
    self, protocol: Literal['half_blocks', 'sixel', 'kitty', 'iterm2']
) -> TerminalGraphics:
    """
    A copy of these capabilities that draws with ``protocol``.

    Raises:
        TerminalImageError: If ``protocol`` is not one of the four protocols.
    """

TerminalImage

A decoded, immutable image or animation.

Every operation returns a new image, so one instance can be shared and reused freely, including across threads. Frames are stored as straight RGBA. len(image) is the frame count.

Attributes:

Examples:

image = TerminalImage.open('photo.png').resize(80, 60)
print(image.to_ansi(40))

Methods:

  • open –

    Load an image from a file (PNG, JPEG, GIF, WebP or XNI1).

  • from_bytes –

    Decode PNG, JPEG, GIF, WebP or XNI1 bytes; the format is detected.

  • from_rgba –

    A single-frame image from width * height * 4 straight RGBA bytes.

  • from_frames –

    An animation from images that all share one size.

  • from_xni –

    Decode the compact XNI1 container written by

  • frame_durations –

    Each frame's display time in milliseconds, in playback order.

  • frame_index_at –

    Index of the frame showing elapsed_milliseconds into playback.

  • to_rgba –

    Raw straight RGBA bytes of one frame, row-major.

  • resize –

    Resize to exactly width x height pixels.

  • scale –

    Scale both sides by factor (2.0 doubles them).

  • crop –

    Cut out a rectangle of pixels.

  • fit_to_canvas –

    Place the image on a width x height canvas without resizing it.

  • focal_crop –

    Scale by scale, then keep an exact window, padding where needed.

  • flatten –

    Composite onto a solid RGB color so no pixel is transparent.

  • quantize –

    Reduce every frame to an adaptive palette of at most colors colors.

  • first_frames –

    Keep only the first count frames.

  • still_frame –

    Keep one frame as a still image.

  • with_frames_per_second –

    Give every frame an even cadence of frames_per_second.

  • with_speed –

    Play factor times faster (2.0 is twice as fast).

  • to_cell_grid –

    Convert one frame to a cell grid (XCG1 bytes).

  • to_cell_grids –

    Convert every frame to its own cell grid (XCG1 bytes).

  • to_cell_animation –

    Convert every frame, with its display time, to a cell animation (XCA1 bytes).

  • to_ansi –

    One frame as half-block ANSI text, ready to print.

  • encode –

    Encode as png (the first frame) or gif (every frame, looping).

  • save –

    Write to path; the extension (.png or .gif) picks the format.

  • to_xni –

    Encode as the compact XNI1 container, quantized to colors.

width property

width: int

Width in pixels.

height property

height: int

Height in pixels.

frame_count property

frame_count: int

Number of frames (1 for a still image).

duration_milliseconds property

duration_milliseconds: int

Total playback time of every frame, in milliseconds.

is_animated property

is_animated: bool

Whether there is more than one frame.

is_opaque property

is_opaque: bool

Whether no pixel is transparent.

open staticmethod

open(path: str | PathLike[str]) -> TerminalImage

Load an image from a file (PNG, JPEG, GIF, WebP or XNI1).

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def open(path: str | os.PathLike[str]) -> TerminalImage:
    """
    Load an image from a file (PNG, JPEG, GIF, WebP or XNI1).

    Raises:
        OSError: If the file cannot be read.
        TerminalImageError: If the data cannot be decoded.
    """

from_bytes staticmethod

from_bytes(data: bytes) -> TerminalImage

Decode PNG, JPEG, GIF, WebP or XNI1 bytes; the format is detected.

Animated GIF and WebP keep every frame, composited onto the full canvas, with their display times. A zero delay means 100 milliseconds and no frame is shorter than 10 milliseconds.

Raises:

  • TerminalImageError –

    If the format is unrecognized or the data is corrupt or too large (sides over 16384 pixels, over 1 GiB of pixels in total, an animated GIF or WebP canvas over 67 megapixels, or over 10000 frames).

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def from_bytes(data: bytes) -> TerminalImage:
    """
    Decode PNG, JPEG, GIF, WebP or XNI1 bytes; the format is detected.

    Animated GIF and WebP keep every frame, composited onto the full
    canvas, with their display times. A zero delay means 100 milliseconds
    and no frame is shorter than 10 milliseconds.

    Raises:
        TerminalImageError: If the format is unrecognized or the data is
            corrupt or too large (sides over 16384 pixels, over 1 GiB of
            pixels in total, an animated GIF or WebP canvas over 67
            megapixels, or over 10000 frames).
    """

from_rgba staticmethod

from_rgba(width: int, height: int, pixels: bytes, *, duration_milliseconds: int = 100) -> TerminalImage

A single-frame image from width * height * 4 straight RGBA bytes.

Parameters:

  • width (int) –

    Width in pixels, 1 to 16384.

  • height (int) –

    Height in pixels, 1 to 16384.

  • pixels (bytes) –

    Row-major RGBA bytes.

  • duration_milliseconds (int, default: 100 ) –

    How long the frame is shown when it becomes part of an animation.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def from_rgba(
    width: int, height: int, pixels: bytes, *, duration_milliseconds: int = 100
) -> TerminalImage:
    """
    A single-frame image from ``width * height * 4`` straight RGBA bytes.

    Args:
        width: Width in pixels, 1 to 16384.
        height: Height in pixels, 1 to 16384.
        pixels: Row-major RGBA bytes.
        duration_milliseconds: How long the frame is shown when it becomes
            part of an animation.

    Raises:
        TerminalImageError: If the size is out of range or ``pixels`` has the
            wrong length.
    """

from_frames staticmethod

from_frames(frames: list[TerminalImage]) -> TerminalImage

An animation from images that all share one size.

Each image contributes all of its frames, in order, keeping its own frame durations (use with_frames_per_second to even them out).

Raises:

  • TerminalImageError –

    If the list is empty, the sizes differ, or the result would hold more than 1 GiB of pixels.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def from_frames(frames: list[TerminalImage]) -> TerminalImage:
    """
    An animation from images that all share one size.

    Each image contributes all of its frames, in order, keeping its own
    frame durations (use
    [`with_frames_per_second`][xnano_image.TerminalImage.with_frames_per_second]
    to even them out).

    Raises:
        TerminalImageError: If the list is empty, the sizes differ, or the
            result would hold more than 1 GiB of pixels.
    """

from_xni staticmethod

from_xni(data: bytes) -> TerminalImage

Decode the compact XNI1 container written by to_xni.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
@staticmethod
def from_xni(data: bytes) -> TerminalImage:
    """
    Decode the compact ``XNI1`` container written by
    [`to_xni`][xnano_image.TerminalImage.to_xni].

    Raises:
        TerminalImageError: If the data is truncated or corrupt.
    """

frame_durations

frame_durations() -> list[int]

Each frame's display time in milliseconds, in playback order.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def frame_durations(self) -> list[int]:
    """Each frame's display time in milliseconds, in playback order."""

frame_index_at

frame_index_at(elapsed_milliseconds: float, *, should_loop: bool = True) -> int

Index of the frame showing elapsed_milliseconds into playback.

Negative times show the first frame.

Parameters:

  • elapsed_milliseconds (float) –

    Time since playback started.

  • should_loop (bool, default: True ) –

    Start over after the last frame; otherwise hold the last frame.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def frame_index_at(
    self, elapsed_milliseconds: float, *, should_loop: bool = True
) -> int:
    """
    Index of the frame showing ``elapsed_milliseconds`` into playback.

    Negative times show the first frame.

    Args:
        elapsed_milliseconds: Time since playback started.
        should_loop: Start over after the last frame; otherwise hold the
            last frame.

    Raises:
        TerminalImageError: If ``elapsed_milliseconds`` is NaN or infinite.
    """

to_rgba

to_rgba(frame_index: int = 0) -> bytes

Raw straight RGBA bytes of one frame, row-major.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def to_rgba(self, frame_index: int = 0) -> bytes:
    """
    Raw straight RGBA bytes of one frame, row-major.

    Raises:
        TerminalImageError: If ``frame_index`` is out of range.
    """

resize

resize(width: int, height: int, *, resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3') -> TerminalImage

Resize to exactly width x height pixels.

Uses SIMD resampling that keeps transparent edges free of dark halos.

Parameters:

  • width (int) –

    Width in pixels, 1 to 16384.

  • height (int) –

    Height in pixels, 1 to 16384.

  • resampling_filter (Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'], default: 'lanczos3' ) –

    nearest keeps hard pixel edges; bilinear, catmull_rom and lanczos3 are progressively sharper and slower.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def resize(
    self,
    width: int,
    height: int,
    *,
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
) -> TerminalImage:
    """
    Resize to exactly ``width`` x ``height`` pixels.

    Uses SIMD resampling that keeps transparent edges free of dark halos.

    Args:
        width: Width in pixels, 1 to 16384.
        height: Height in pixels, 1 to 16384.
        resampling_filter: ``nearest`` keeps hard pixel edges; ``bilinear``,
            ``catmull_rom`` and ``lanczos3`` are progressively sharper and
            slower.

    Raises:
        TerminalImageError: If a side is out of range or the filter is unknown.
    """

scale

scale(factor: float, *, resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3') -> TerminalImage

Scale both sides by factor (2.0 doubles them).

Raises:

  • TerminalImageError –

    If factor is not a finite number greater than zero, or the result would exceed 16384 pixels per side.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def scale(
    self,
    factor: float,
    *,
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
) -> TerminalImage:
    """
    Scale both sides by ``factor`` (``2.0`` doubles them).

    Raises:
        TerminalImageError: If ``factor`` is not a finite number greater than
            zero, or the result would exceed 16384 pixels per side.
    """

crop

crop(left: int, top: int, width: int, height: int) -> TerminalImage

Cut out a rectangle of pixels.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def crop(self, left: int, top: int, width: int, height: int) -> TerminalImage:
    """
    Cut out a rectangle of pixels.

    Raises:
        TerminalImageError: If the rectangle is empty or does not fit inside
            the image.
    """

fit_to_canvas

fit_to_canvas(width: int, height: int, *, background: tuple[int, int, int] = (0, 0, 0), left: int | None = None, top: int | None = None) -> TerminalImage

Place the image on a width x height canvas without resizing it.

Smaller images are centered and padded with background; larger ones are cropped around the center.

Parameters:

  • width (int) –

    Canvas width in pixels, 1 to 16384.

  • height (int) –

    Canvas height in pixels, 1 to 16384.

  • background (tuple[int, int, int], default: (0, 0, 0) ) –

    RGB color of the padding.

  • left (int | None, default: None ) –

    Left edge of the kept window within a wider image. Centered when None.

  • top (int | None, default: None ) –

    Top edge of the kept window within a taller image. Centered when None.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def fit_to_canvas(
    self,
    width: int,
    height: int,
    *,
    background: tuple[int, int, int] = (0, 0, 0),
    left: int | None = None,
    top: int | None = None,
) -> TerminalImage:
    """
    Place the image on a ``width`` x ``height`` canvas without resizing it.

    Smaller images are centered and padded with ``background``; larger
    ones are cropped around the center.

    Args:
        width: Canvas width in pixels, 1 to 16384.
        height: Canvas height in pixels, 1 to 16384.
        background: RGB color of the padding.
        left: Left edge of the kept window within a wider image. Centered
            when ``None``.
        top: Top edge of the kept window within a taller image. Centered
            when ``None``.
    """

focal_crop

focal_crop(width: int, height: int, *, scale: float = 1.0, left: int | None = None, top: int | None = None, background: tuple[int, int, int] = (0, 0, 0), resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3') -> TerminalImage

Scale by scale, then keep an exact window, padding where needed.

Zooms in on the subject, then keeps a fixed-size rectangle at the scaled resolution.

Parameters:

  • width (int) –

    Width of the window in pixels.

  • height (int) –

    Height of the window in pixels.

  • scale (float, default: 1.0 ) –

    Scale factor applied before cropping.

  • left (int | None, default: None ) –

    Left edge of the window in the scaled image. Centered when None.

  • top (int | None, default: None ) –

    Top edge of the window in the scaled image. Centered when None.

  • background (tuple[int, int, int], default: (0, 0, 0) ) –

    RGB color used to flatten transparency and to pad when the window extends past the image.

  • resampling_filter (Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'], default: 'lanczos3' ) –

    Resampling filter, as in resize.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def focal_crop(
    self,
    width: int,
    height: int,
    *,
    scale: float = 1.0,
    left: int | None = None,
    top: int | None = None,
    background: tuple[int, int, int] = (0, 0, 0),
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
) -> TerminalImage:
    """
    Scale by ``scale``, then keep an exact window, padding where needed.

    Zooms in on the subject, then keeps a fixed-size rectangle at the
    scaled resolution.

    Args:
        width: Width of the window in pixels.
        height: Height of the window in pixels.
        scale: Scale factor applied before cropping.
        left: Left edge of the window in the scaled image. Centered when
            ``None``.
        top: Top edge of the window in the scaled image. Centered when
            ``None``.
        background: RGB color used to flatten transparency and to pad when
            the window extends past the image.
        resampling_filter: Resampling filter, as in
            [`resize`][xnano_image.TerminalImage.resize].
    """

flatten

flatten(background: tuple[int, int, int] = (0, 0, 0)) -> TerminalImage

Composite onto a solid RGB color so no pixel is transparent.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def flatten(self, background: tuple[int, int, int] = (0, 0, 0)) -> TerminalImage:
    """Composite onto a solid RGB color so no pixel is transparent."""

quantize

quantize(colors: int = 64, *, should_dither: bool = False) -> TerminalImage

Reduce every frame to an adaptive palette of at most colors colors.

Transparency is flattened onto black first.

Parameters:

  • colors (int, default: 64 ) –

    2 to 256. Fewer colors compress better (see to_xni) at the cost of fidelity.

  • should_dither (bool, default: False ) –

    Apply Floyd-Steinberg dithering.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def quantize(self, colors: int = 64, *, should_dither: bool = False) -> TerminalImage:
    """
    Reduce every frame to an adaptive palette of at most ``colors`` colors.

    Transparency is flattened onto black first.

    Args:
        colors: 2 to 256. Fewer colors compress better (see
            [`to_xni`][xnano_image.TerminalImage.to_xni]) at the cost of
            fidelity.
        should_dither: Apply Floyd-Steinberg dithering.

    Raises:
        TerminalImageError: If ``colors`` is out of range.
    """

first_frames

first_frames(count: int) -> TerminalImage

Keep only the first count frames.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def first_frames(self, count: int) -> TerminalImage:
    """
    Keep only the first ``count`` frames.

    Raises:
        TerminalImageError: If ``count`` is 0.
    """

still_frame

still_frame(frame_index: int) -> TerminalImage

Keep one frame as a still image.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def still_frame(self, frame_index: int) -> TerminalImage:
    """
    Keep one frame as a still image.

    Raises:
        TerminalImageError: If ``frame_index`` is out of range.
    """

with_frames_per_second

with_frames_per_second(frames_per_second: float) -> TerminalImage

Give every frame an even cadence of frames_per_second.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def with_frames_per_second(self, frames_per_second: float) -> TerminalImage:
    """
    Give every frame an even cadence of ``frames_per_second``.

    Raises:
        TerminalImageError: If ``frames_per_second`` is not a finite number
            greater than zero.
    """

with_speed

with_speed(factor: float) -> TerminalImage

Play factor times faster (2.0 is twice as fast).

No frame becomes shorter than 10 milliseconds.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def with_speed(self, factor: float) -> TerminalImage:
    """
    Play ``factor`` times faster (``2.0`` is twice as fast).

    No frame becomes shorter than 10 milliseconds.

    Raises:
        TerminalImageError: If ``factor`` is not a finite number greater than
            zero.
    """

to_cell_grid

to_cell_grid(columns: int | None = None, rows: int | None = None, *, fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain', horizontal_pixels_per_cell: int = 1, should_correct_aspect_ratio: bool = False, background: tuple[int, int, int] = (0, 0, 0), resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3', frame_index: int = 0, graphics: TerminalGraphics | None = None) -> bytes

Convert one frame to a cell grid (XCG1 bytes).

With neither columns nor rows the image keeps its native pixel size (two pixel rows per cell). Give one to derive the other from the aspect ratio, or both to fit inside that box.

Parameters:

  • columns (int | None, default: None ) –

    Width in terminal cells, 1 to 1000.

  • rows (int | None, default: None ) –

    Height in terminal cells, 1 to 1000.

  • fit (Literal['crop', 'contain', 'cover', 'stretch', 'smart'], default: 'contain' ) –

    How the image is placed in a columns x rows box. crop never resizes (center, crop or pad), contain fits entirely inside, cover fills and crops the overflow, stretch fills exactly, and smart is cover when the aspect ratios nearly match and contain otherwise.

  • horizontal_pixels_per_cell (int, default: 1 ) –

    1 (lossless width) or 2 (sample two pixels across each cell).

  • should_correct_aspect_ratio (bool, default: False ) –

    With 2 pixels per cell the pixels are half as wide as tall; this widens the picture so proportions stay true.

  • background (tuple[int, int, int], default: (0, 0, 0) ) –

    RGB color behind transparency and padding.

  • resampling_filter (Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'], default: 'lanczos3' ) –

    Resampling filter, as in resize.

  • frame_index (int, default: 0 ) –

    Which frame to convert.

  • graphics (TerminalGraphics | None, default: None ) –

    Capabilities whose protocol is sixel, kitty or iterm2 draw with that protocol instead of half-blocks.

Raises:

  • TerminalImageError –

    If an option is invalid, frame_index is out of range, or the grid would hold more than 4000000 cells (an unsized conversion of a very large image; pass columns or rows).

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def to_cell_grid(
    self,
    columns: int | None = None,
    rows: int | None = None,
    *,
    fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain',
    horizontal_pixels_per_cell: int = 1,
    should_correct_aspect_ratio: bool = False,
    background: tuple[int, int, int] = (0, 0, 0),
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
    frame_index: int = 0,
    graphics: TerminalGraphics | None = None,
) -> bytes:
    """
    Convert one frame to a cell grid (``XCG1`` bytes).

    With neither ``columns`` nor ``rows`` the image keeps its native pixel
    size (two pixel rows per cell). Give one to derive the other from the
    aspect ratio, or both to fit inside that box.

    Args:
        columns: Width in terminal cells, 1 to 1000.
        rows: Height in terminal cells, 1 to 1000.
        fit: How the image is placed in a ``columns`` x ``rows`` box.
            ``crop`` never resizes (center, crop or pad), ``contain``
            fits entirely inside, ``cover`` fills and crops the overflow,
            ``stretch`` fills exactly, and ``smart`` is ``cover`` when
            the aspect ratios nearly match and ``contain`` otherwise.
        horizontal_pixels_per_cell: 1 (lossless width) or 2 (sample two
            pixels across each cell).
        should_correct_aspect_ratio: With 2 pixels per cell the pixels are
            half as wide as tall; this widens the picture so proportions
            stay true.
        background: RGB color behind transparency and padding.
        resampling_filter: Resampling filter, as in
            [`resize`][xnano_image.TerminalImage.resize].
        frame_index: Which frame to convert.
        graphics: Capabilities whose protocol is ``sixel``, ``kitty`` or
            ``iterm2`` draw with that protocol instead of half-blocks.

    Raises:
        TerminalImageError: If an option is invalid, ``frame_index`` is out of
            range, or the grid would hold more than 4000000 cells (an
            unsized conversion of a very large image; pass ``columns`` or
            ``rows``).
    """

to_cell_grids

to_cell_grids(columns: int | None = None, rows: int | None = None, *, fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain', horizontal_pixels_per_cell: int = 1, should_correct_aspect_ratio: bool = False, background: tuple[int, int, int] = (0, 0, 0), resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3', graphics: TerminalGraphics | None = None) -> list[bytes]

Convert every frame to its own cell grid (XCG1 bytes).

Takes the same arguments as to_cell_grid, except frame_index. Pair the result with frame_durations, or use to_cell_animation to get frames and timing in one value.

Raises:

  • TerminalImageError –

    If an option is invalid or a grid would hold more than 4000000 cells.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def to_cell_grids(
    self,
    columns: int | None = None,
    rows: int | None = None,
    *,
    fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain',
    horizontal_pixels_per_cell: int = 1,
    should_correct_aspect_ratio: bool = False,
    background: tuple[int, int, int] = (0, 0, 0),
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
    graphics: TerminalGraphics | None = None,
) -> list[bytes]:
    """
    Convert every frame to its own cell grid (``XCG1`` bytes).

    Takes the same arguments as
    [`to_cell_grid`][xnano_image.TerminalImage.to_cell_grid], except
    ``frame_index``. Pair the result with
    [`frame_durations`][xnano_image.TerminalImage.frame_durations], or use
    [`to_cell_animation`][xnano_image.TerminalImage.to_cell_animation] to
    get frames and timing in one value.

    Raises:
        TerminalImageError: If an option is invalid or a grid would hold more
            than 4000000 cells.
    """

to_cell_animation

to_cell_animation(columns: int | None = None, rows: int | None = None, *, should_loop: bool = True, fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain', horizontal_pixels_per_cell: int = 1, should_correct_aspect_ratio: bool = False, background: tuple[int, int, int] = (0, 0, 0), resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3', graphics: TerminalGraphics | None = None) -> bytes

Convert every frame, with its display time, to a cell animation (XCA1 bytes).

Takes the same arguments as to_cell_grid, except frame_index. A still image becomes a one-frame animation.

With the kitty protocol every frame reuses one image id, so the terminal replaces the image in place instead of storing a new one per frame; each frame still carries its full pixel data. sixel and iterm2 frames carry their full image data every frame.

Parameters:

  • should_loop (bool, default: True ) –

    Start over after the last frame; otherwise hold the last frame.

Raises:

  • TerminalImageError –

    If an option is invalid, or the animation would hold more than 100000 frames or 64000000 cells in total. Pass smaller columns or rows, or keep fewer frames with first_frames.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def to_cell_animation(
    self,
    columns: int | None = None,
    rows: int | None = None,
    *,
    should_loop: bool = True,
    fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain',
    horizontal_pixels_per_cell: int = 1,
    should_correct_aspect_ratio: bool = False,
    background: tuple[int, int, int] = (0, 0, 0),
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
    graphics: TerminalGraphics | None = None,
) -> bytes:
    """
    Convert every frame, with its display time, to a cell animation (``XCA1`` bytes).

    Takes the same arguments as
    [`to_cell_grid`][xnano_image.TerminalImage.to_cell_grid], except
    ``frame_index``. A still image becomes a one-frame animation.

    With the ``kitty`` protocol every frame reuses one image id, so the
    terminal replaces the image in place instead of storing a new one per
    frame; each frame still carries its full pixel data. ``sixel`` and
    ``iterm2`` frames carry their full image data every frame.

    Args:
        should_loop: Start over after the last frame; otherwise hold the
            last frame.

    Raises:
        TerminalImageError: If an option is invalid, or the animation would
            hold more than 100000 frames or 64000000 cells in total. Pass
            smaller ``columns`` or ``rows``, or keep fewer frames with
            [`first_frames`][xnano_image.TerminalImage.first_frames].
    """

to_ansi

to_ansi(columns: int | None = None, rows: int | None = None, *, fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain', horizontal_pixels_per_cell: int = 1, should_correct_aspect_ratio: bool = False, background: tuple[int, int, int] = (0, 0, 0), resampling_filter: Literal['nearest', 'bilinear', 'catmull_rom', 'lanczos3'] = 'lanczos3', frame_index: int = 0) -> str

One frame as half-block ANSI text, ready to print.

Takes the same arguments as to_cell_grid, except graphics. Every line ends with a style reset.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def to_ansi(
    self,
    columns: int | None = None,
    rows: int | None = None,
    *,
    fit: Literal['crop', 'contain', 'cover', 'stretch', 'smart'] = 'contain',
    horizontal_pixels_per_cell: int = 1,
    should_correct_aspect_ratio: bool = False,
    background: tuple[int, int, int] = (0, 0, 0),
    resampling_filter: Literal[
        'nearest', 'bilinear', 'catmull_rom', 'lanczos3'
    ] = 'lanczos3',
    frame_index: int = 0,
) -> str:
    """
    One frame as half-block ANSI text, ready to ``print``.

    Takes the same arguments as
    [`to_cell_grid`][xnano_image.TerminalImage.to_cell_grid], except
    ``graphics``. Every line ends with a style reset.
    """

encode

encode(image_format: Literal['png', 'gif'] = 'png') -> bytes

Encode as png (the first frame) or gif (every frame, looping).

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def encode(self, image_format: Literal['png', 'gif'] = 'png') -> bytes:
    """
    Encode as ``png`` (the first frame) or ``gif`` (every frame, looping).

    Raises:
        TerminalImageError: If ``image_format`` is not ``png`` or ``gif``.
    """

save

save(path: str | PathLike[str]) -> None

Write to path; the extension (.png or .gif) picks the format.

Raises:

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def save(self, path: str | os.PathLike[str]) -> None:
    """
    Write to ``path``; the extension (``.png`` or ``.gif``) picks the format.

    Raises:
        TerminalImageError: If the extension is not ``.png`` or ``.gif``.
        OSError: If the file cannot be written.
    """

to_xni

to_xni(colors: int = 64) -> bytes

Encode as the compact XNI1 container, quantized to colors.

Raises:

  • TerminalImageError –

    If colors is not between 2 and 256, or there are more than 65535 frames.

Source code in packages/xnano-image/src/xnano_image/_xnano_image/__init__.pyi
def to_xni(self, colors: int = 64) -> bytes:
    """
    Encode as the compact ``XNI1`` container, quantized to ``colors``.

    Raises:
        TerminalImageError: If ``colors`` is not between 2 and 256, or there
            are more than 65535 frames.
    """