eger 1.2.0

A high-level, parallel ASCII-art media pipeline built on top of iascii: images, video, GIFs, dithering, animated text/banners, and procedural illusions, all rendered to ANSI.
Documentation
//! `eger` — a high-level, parallel ASCII-art media pipeline built on top
//! of [`iascii`].
//!
//! - [`config`] — fluent [`Config`]/[`ConfigBuilder`] describing input,
//!   output, sizing, and parallelism.
//! - [`style`] — one-knob color treatments: basic uncolored ASCII
//!   ([`Style::Plain`]), full color ([`Style::Full`]), black-and-white
//!   grayscale ([`Style::Gray`]), a single color of your choice
//!   ([`Style::Mono`]), or 1-bit bilevel ([`Style::BlackWhite`]) — set via
//!   [`ConfigBuilder::style`](crate::config::ConfigBuilder::style) and
//!   honored by the image, batch, GIF, and video pipelines.
//! - [`image`] — single-image and Rayon-parallel batch image → ASCII
//!   rendering. Always available.
//! - `video` *(requires the `video` feature)* — video → ASCII
//!   frame-sequence rendering, streamed through `ffmpeg` with chunked
//!   Rayon-parallel conversion, plus terminal playback.
//! - [`render`] — output targets: stdout, file, `String`, or `Vec<String>`,
//!   plus terminal-size detection ([`render::terminal_size`]) and
//!   cursor-addressed diffed playback ([`render::diff_frame`],
//!   `render::play_terminal_diffed`, the latter behind the `video`
//!   feature) as a lower-flicker alternative to clear-and-redraw.
//! - [`text`] — text → ANSI coloring/animation: built-in styles
//!   (`Typewriter`, `Rainbow`, `Wave`, `Blink`, `Marquee`, `Pulse`) plus
//!   custom animations driven by an external program or a Rust closure.
//!   Always available (no `video` feature needed, except for the async
//!   `play_text_async`/`stream_text_frames` variants).
//! - [`banner`] — multi-line "big text" banners: the same animation styles
//!   as [`text`], rendered as block letters instead of a single line.
//! - [`colorize`] — per-*cell* (as opposed to [`text`]/[`banner`]'s
//!   per-character) color animation for *any* block of plain ASCII text —
//!   a converted image, a decoded GIF/video frame, banner text, or
//!   anything else — including [`colorize::PixelAnimation::Custom`] for
//!   fully custom, mathematically-defined coloring functions.
//! - [`color`] — reusable [`color::ColorTransform`]s (grayscale, invert,
//!   sepia, brightness/contrast/saturation, hue rotation, tinting) shared
//!   by [`dither`] (transform-then-quantize), [`colorize`]
//!   ([`colorize::PixelAnimation::Transformed`] layers one on top of any
//!   other animation), and `wasm` (exposed directly for
//!   post-processing already-rendered art in the browser).
//! - [`segment`] — partitions a single converted image/frame into
//!   numbered/named **structures** ([`segment::SegmentMap`]), so a
//!   particular region can be bolded, colored, or animated
//!   ([`segment::SegmentStyle::Animation`]) on its own while the rest of
//!   the frame renders normally. [`segment::SequenceStyles`] extends this
//!   across a whole sequence of frames, keyed by `(frame, segment id)`.
//! - [`dither`] — Floyd–Steinberg/Atkinson error diffusion and Bayer
//!   ordered dithering, applied in character-grid space via
//!   [`Config::dither`] — mainly useful for reducing banding at
//!   [`iascii::render::ColorDepth::Ansi16`]/`Ansi256`.
//! - [`system`] — system-aware resource scaling: detected CPUs size
//!   thread pools, detected RAM sizes decode-chunk buffering (both
//!   manually overridable, both conservative when detection fails).
//! - [`canvas`] — efficient frame building/presentation, usable on its own
//!   (dashboards, monitors, custom renderers): a flat, clipping [`Canvas`]
//!   of colored [`canvas::Cell`]s with damage tracking (dirty rows, render
//!   cache, cell-snapshot dedupe), zero-allocation frame paths
//!   ([`Canvas::write_into`]/[`Canvas::write_dirty_into`]), and
//!   cursor-addressed patch-only presentation
//!   ([`Canvas::present_dirty_into`]). Plus multi-frame [`Sprite`]s from
//!   text, pictures, or any rendered frame, and [`render::diff_frame`]-based
//!   [`canvas::present_diff`]/[`canvas::screen`] helpers.
//! - [`game`] — realtime game *loop* layer on top of [`canvas`] (which it
//!   re-exports): backend-free [`Input`], [`Camera`], [`rects_overlap`],
//!   fixed-step [`TickClock`], the [`Game`] trait with a headless runner for
//!   tests/bots/replays, and the [`jump_arc`]/[`perspective_scale`]/
//!   [`shake_offset`] effect helpers.
//! - [`script`] — ready-made non-Latin character ramps ([`script::Script`]:
//!   Cyrillic, CJK, Devanagari, Braille, block/shade, ...) for
//!   [`ConfigBuilder::script`], alongside `iascii`'s own Latin ramps.
//! - [`illusions`] — procedurally generated optical-illusion source art
//!   (café wall, Hermann grid, twisted cord, and the animated "rotating
//!   rings" motion illusion), rendered through the same pipeline as any
//!   other image.
//! - `wasm` *(requires the `wasm` feature, `target_arch = "wasm32"` only)*
//!   — a thin `wasm-bindgen` wrapper around the terminal-independent parts
//!   of the pipeline, for rendering ASCII art in a browser.
//! - [`error`] — unified [`EgerError`] / [`Result`].
//!
//! `video` also gains GIF playback (`video::play_gif`), and [`image`]
//! gains synchronous GIF decoding ([`image::gif_to_lines`]) available
//! without the `video` feature.
//!
//! # Feature flags
//!
//! Modules are opt-out, not clubbed together. `default = ["standard"]`
//! matches the historical build (everything below except `video`/`wasm`);
//! `standard` pulls one feature per module (`image`, `text`, `banner`,
//! `color`, `colorize`, `dither`, `segment`, `script`, `illusions`,
//! `canvas`, `game`) so you can drop what you don't ship. With
//! `default-features = false` you get the lean core — grid rendering,
//! [`Config`], [`Style`], system detection, raw-buffer conversion — with
//! no image decoders and no extra modules:
//!
//! ```toml
//! [dependencies]
//! eger = { version = "1", default-features = false }            # core only
//! eger = { version = "1", default-features = false, features = ["image", "dither"] }
//! ```
//!
//! - **`standard`** *(on by default)* — all modules except `video`/`wasm`.
//! - **`full`** — `standard` + `video` + `wasm` (everything).
//! - **`video`** *(off by default)* — pulls in `tokio` (plus `image`,
//!   `colorize`, and `text`, which the pipeline is built on) and enables
//!   the `video` module (which additionally requires `ffmpeg`/`ffprobe`
//!   on `PATH` at runtime), plus the async (`_async`) variants of the
//!   image API and `render::dispatch_async`/`render::play_terminal`.
//! - **`wasm`** *(off by default)* — pulls in `wasm-bindgen` (plus
//!   `image`, `banner`, `color`, `colorize`, `dither`, `script`, `text`,
//!   and `illusions`) and enables the `wasm`
//!   module. Only meaningful when targeting `wasm32`; mutually exclusive
//!   with `video` in practice, since `tokio`/`ffmpeg`/real terminals don't
//!   exist in a browser.
//! - **`parallel`** *(off by default)* — row-level split *within* a
//!   single image/frame conversion (via `iascii/parallel`'s
//!   `convert_with_pool`); across-file/frame batching is always on.
//! - Granular module features: `image` (file/GIF decoding via the `image`
//!   crate — buffer APIs stay in the core), `text`, `banner` (implies
//!   `text`), `color`, `colorize`, `dither`, `segment` (implies
//!   `colorize`), `script`, `illusions` (implies `image` + `colorize`),
//!   `canvas` (efficient frame building/presentation, usable standalone),
//!   `game` (implies `canvas` — only `Canvas::apply_animation` needs
//!   `colorize`).
//!
//! Always-on substrate (small, pure-Rust, no system services): `iascii`,
//! `rayon` (shared thread pool), `regex` (directory input patterns),
//! `thiserror`, `terminal_size`.
//!
//! # Example
//!
//! ```rust,no_run
//! use eger::prelude::*;
//!
//! # fn run() -> eger::Result<()> {
//! // Core-only: no image decoders, no extra modules. A raw RGB8
//! // framebuffer (screencap, game frame, camera feed) straight to ASCII.
//! let config = Config::builder(MediaType::Image)
//!     .from_file("cat.png") // placeholder source; the buffer below is authoritative
//!     .max_width(120)
//!     .style(Style::Plain) // basic ASCII, zero escapes
//!     .build()?;
//! let rgb = vec![128u8; 64 * 32 * 3]; // your pixels here
//! let ascii = eger::image::rgb_buffer_to_string(64, 32, &rgb, &config)?;
//! println!("{ascii}");
//! # Ok(())
//! # }
//! ```

#[cfg(feature = "banner")]
pub mod banner;
#[cfg(feature = "canvas")]
pub mod canvas;
#[cfg(feature = "color")]
pub mod color;
#[cfg(feature = "colorize")]
pub mod colorize;
pub mod config;
#[cfg(feature = "dither")]
pub mod dither;
pub mod error;
pub(crate) mod fast_ansi;
#[cfg(feature = "game")]
pub mod game;
#[cfg(feature = "illusions")]
pub mod illusions;
pub mod image;
mod palette;
pub mod render;
pub mod rgb;
#[cfg(feature = "script")]
pub mod script;
#[cfg(feature = "segment")]
pub mod segment;
pub mod style;
pub mod system;
pub mod text;
#[cfg(feature = "video")]
pub mod video;
#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
pub mod wasm;

#[cfg(feature = "banner")]
pub use crate::banner::{banner_frames, banner_lines};
#[cfg(feature = "canvas")]
pub use crate::canvas::{present_diff, screen, Canvas, Cell as GameCell, Sprite};
#[cfg(feature = "color")]
pub use crate::color::ColorTransform;
#[cfg(feature = "colorize")]
pub use crate::colorize::{
    animation_color, colorize_frames, colorize_grid, colorize_grid_frames,
    colorize_joined_sequence, colorize_lines, colorize_sequence, ColorFn, PixelAnimation,
};
pub use crate::config::{Config, ConfigBuilder, MediaType};
#[cfg(feature = "dither")]
pub use crate::dither::{DitherMethod, DitherOptions};
pub use crate::error::{EgerError, Result};
#[cfg(feature = "game")]
pub use crate::game::{
    jump_arc, perspective_scale, rects_overlap, run_headless, shake_offset, Camera, Control, Game,
    Input, Key, TickClock,
};
#[cfg(feature = "illusions")]
pub use crate::illusions::{generate as generate_illusion, render_illusion, Illusion};
#[cfg(feature = "image")]
pub use crate::image::{
    gif_to_lines, image_to_file, image_to_lines, image_to_string, render_batch_parallel,
    render_dynamic_image, render_image_file,
};
#[cfg(all(feature = "video", feature = "image"))]
pub use crate::image::{render_batch_async, render_image_file_async};
pub use crate::image::{render_rgb_buffer, rgb_buffer_to_string};
#[cfg(feature = "video")]
pub use crate::render::play_terminal_diffed;
pub use crate::render::{
    detect_render_depth, terminal_size, DiffGranularity, RenderOutput, RenderTarget,
};
pub use crate::rgb::Rgb;
#[cfg(feature = "script")]
pub use crate::script::Script;
#[cfg(feature = "segment")]
pub use crate::segment::{
    Rect, Segment, SegmentMap, SegmentOptions, SegmentStyle, SegmentStyles, SequenceStyles,
};
pub use crate::style::Style;
pub use crate::system::SystemSpecs;
#[cfg(feature = "text")]
pub use crate::text::{play_text, text_frames, CustomAnimation, TextAnimOptions, TextAnimation};
#[cfg(feature = "video")]
pub use crate::text::{play_text_async, stream_text_frames};
#[cfg(feature = "video")]
pub use crate::video::{
    play_gif, play_video, probe, process_video_frames, render_colorized_video_frames,
    render_video_frames, stream_colorized_video_frames, stream_video_frames, video_to_file,
    video_to_file_styled, video_to_lines, video_to_lines_plain, video_to_writer, ColorMode,
    VideoInfo,
};

/// Everything needed to configure and drive `eger` in one `use`.
pub mod prelude {
    #[cfg(feature = "banner")]
    pub use crate::banner::{banner_frames, banner_lines};
    #[cfg(feature = "canvas")]
    pub use crate::canvas::Cell as GameCell;
    #[cfg(feature = "canvas")]
    pub use crate::canvas::{present_diff, Canvas, Sprite};
    #[cfg(feature = "color")]
    pub use crate::color::ColorTransform;
    #[cfg(feature = "colorize")]
    pub use crate::colorize::{
        animation_color, colorize_frames, colorize_grid, colorize_grid_frames,
        colorize_joined_sequence, colorize_lines, colorize_sequence, ColorFn, PixelAnimation,
    };
    pub use crate::config::{Config, ConfigBuilder, InputSource, MediaType};
    #[cfg(feature = "dither")]
    pub use crate::dither::{DitherMethod, DitherOptions};
    pub use crate::error::{ConfigError, EgerError, Result};
    #[cfg(feature = "game")]
    pub use crate::game::{
        jump_arc, perspective_scale, rects_overlap, run_headless, shake_offset, Camera, Control,
        Game, Input, Key, TickClock,
    };
    #[cfg(feature = "illusions")]
    pub use crate::illusions::{generate as generate_illusion, render_illusion, Illusion};
    #[cfg(feature = "video")]
    pub use crate::render::play_terminal_diffed;
    pub use crate::render::{
        detect_render_depth, terminal_size, DiffGranularity, RenderOutput, RenderTarget,
    };
    pub use crate::rgb::Rgb;
    #[cfg(feature = "script")]
    pub use crate::script::Script;
    #[cfg(feature = "segment")]
    pub use crate::segment::{
        Rect, Segment, SegmentMap, SegmentOptions, SegmentStyle, SegmentStyles, SequenceStyles,
    };
    pub use crate::style::Style;
    pub use crate::system::SystemSpecs;
    #[cfg(feature = "text")]
    pub use crate::text::{
        play_text, text_frames, CustomAnimation, TextAnimOptions, TextAnimation,
    };
    #[cfg(feature = "video")]
    pub use crate::text::{play_text_async, stream_text_frames};
    #[cfg(feature = "video")]
    pub use crate::video::{
        play_gif, process_video_frames, render_colorized_video_frames,
        stream_colorized_video_frames, video_to_file, video_to_file_styled, video_to_lines_plain,
        video_to_writer, ColorMode, VideoInfo,
    };

    pub use iascii::config::{LuminanceMethod, OutputSizing};
    pub use iascii::ramp::{Ramp, RampType};
    pub use iascii::render::ColorDepth;
}