Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

term_pic

A header/source C++23 library that renders images and graphics in a terminal using 24-bit truecolor ANSI escape sequences. Pairs of pixels are packed into the lower-half block character (U+2584) so each output cell shows two vertical pixels at once.

Features

  • Pure C++23, no external dependencies
  • 24-bit truecolor output (\033[38;2;r;g;bm / \033[48;2;...m)
  • Image — pixel buffer with bounds-checked access and terminal rendering
  • Canvas — drawing primitives: pixel, line, rect, circle, image blit, and 5×3 bitmap digits (draw_uint)
  • Alpha blending through overlay()
  • HSL color helper, grayscale helper
  • Terminal capability detection:
    • supports_truecolor() — actively probes the terminal with an OSC 11 query
    • enable_vt100() — probes with a Device Attributes query and enables VT100 application mode
  • Error handling with std::expected (C++23)

Requirements

  • C++23 compiler (GCC 13+, Clang 16+)
  • EazyMake for building (optional — you can compile the sources with any build system)

POSIX required for terminal support — the terminal.h functions (supports_truecolor(), enable_vt100(), terminal_size()) and Image::draw() rely on POSIX facilities (termios, poll, ioctl, isatty). These are available on Linux/macOS and in MSYS2 (MinGW-w64), but not in a native MSVC build. The rest of the library (color, image, canvas) is portable C++.

On Windows, run the binaries under Windows Terminal or mintty so the terminal answers ANSI queries (the library does not call SetConsoleMode(ENABLE_VIRTUAL_TERMINAL_PROCESSING)).

Build

ezmk build      # produces build/libterm_pic.a
ezmk test       # build and run tests

Or compile manually:

g++ -std=c++23 -O2 -Iinclude -c src/*.cpp
ar rcs libterm_pic.a *.o

Usage

#include "term_pic.h"
#include <cstdint>

int main() {
    if (!term_pic::supports_truecolor()) {
        return 1;
    }
    term_pic::enable_vt100();

    term_pic::Canvas canvas({ 64, 64 }, term_pic::gray(30));

    for (size_t y = 0; y < 64; y += 8) {
        term_pic::RgbColor c = term_pic::hsl(360.0f * y / 64.0f, 1.0f, 0.5f);
        canvas.draw_rect({8, y}, {55, y + 6}, c, 1.0f, true);
    }

    canvas.draw_circle({32, 32}, 12, term_pic::gray(255), 0.5f, false);
    canvas.draw_uint({26, 27}, 42, term_pic::gray(255), 1.0f);
    canvas.draw();
}

API

All types live in namespace term_pic. The umbrella header term_pic.h (compiled with -Iinclude) includes everything; individual headers such as term_pic/canvas.h are also available.

pos.h

struct Pos { size_t x, y; };
struct Size { size_t w, h; };

color.h

Declaration Description
struct RgbColor { uint8_t r, g, b; } RGB color value
RgbColor gray(uint8_t g) Gray color
RgbColor overlay(const RgbColor &raw, const RgbColor &mask, float alpha = 0.5) Alpha blend raw over mask
RgbColor hsl(float h, float s, float l) HSL → RGB (h in degrees)
std::string gen_seq(const std::optional<RgbColor> &fg, const std::optional<RgbColor> &bg) Build a truecolor ANSI SGR sequence

error.h

enum class Error : size_t { OutOfRange = 0, NotTerminal = 1 };
template<typename T> using Res = std::expected<T, Error>;
using Err = std::unexpected<Error>;
std::string to_string(Error e) noexcept;

image.h

Member Description
Image(const Size &size, const RgbColor &bg = {255,255,255}) Create buffer
Res<RgbColor> get(const Pos &) const Bounds-checked read
Res<void> set(const Pos &, const RgbColor &) Bounds-checked write
void resize(size_t w, size_t h, const std::optional<RgbColor> &bg = std::nullopt) Resize
void draw(std::ostream &os = std::cout) const Render to stream
void clear() Reset to background
Size size() const Width, height

canvas.h

Canvas wraps an Image and adds clipping to the drawing primitives below. Every draw call takes float alpha = 1.0f for blending and is noexcept.

Member Description
Canvas(const Size &size, const RgbColor &bg) Create canvas
Image &image_ref() Access the underlying image
void clear() Reset to background
void draw(std::ostream &os = std::cout) const Render
RgbColor get_bg() const / void set_bg(const RgbColor &) Background access
void draw_pixel(Pos, const RgbColor &, float alpha = 1) Single pixel
void draw_line(Pos start, Pos end, const RgbColor &, float alpha = 1) Bresenham line
void draw_rect(Pos start, Pos end, const RgbColor &, float alpha = 1, bool fill = true) Rectangle
void draw_circle(Pos center, float radius, const RgbColor &, float alpha = 1, bool fill = true) Circle
void draw_image(Pos offset, const Image &, float alpha = 1) Blit an image
void draw_uint(Pos pos, unsigned int num, const RgbColor &, float alpha = 1) Render a decimal number with the 5×3 bitmap font
void fill(const RgbColor &, float alpha = 1) Fill entire canvas

terminal.h

Member Description
bool supports_truecolor() noexcept Sends an OSC 11 query (\033]11;?\033\\) and returns true if the terminal answers with an rgb: value. Requires a TTY on stdin/stdout.
bool enable_vt100() noexcept Sends a Device Attributes query (\033[0c), and if the terminal confirms (\033[?…c), emits the VT100 application-mode sequence (\033[?1h) and returns true.
Res<Size> terminal_size() noexcept Returns the terminal size in character cells (Size{w, h}) via ioctl(TIOCGWINSZ). Returns Err(Error::NotTerminal) when stdin/stdout is not a TTY or the query fails.

Both probes temporarily switch stdin to raw mode (termios) and read the response with a poll() timeout, restoring the original settings afterwards.

POSIX — available on Linux/macOS and MSYS2 (MinGW-w64); not available in a native MSVC build.

Tests

ezmk test
  • test/a.cpp — renders a 64×64 gradient
  • test/terminal_test.cpp — terminal detection smoke tests

About

A simple terminal graphics rendering library

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages