SkillAgentSearch skills...

Constixel

constixel is a single header minimalistic constexpr C++20 2D graphics rendering library

Install / Use

/learn @tinic/Constixel

README

constixel.hpp

CMake on multiple platforms CodeQL Advanced

constixel is a single header minimalistic constexpr C++20 palette based 2D graphics rendering library with the ability to output to a sixel image stream and png images which can be viewed in a modern terminal like Windows Terminal.

Table of Contents

Primary features and goals
Applications
Requirements
Minimal example
Text drawing example
Consteval sixel example
Consteval image example
Saving a PNG to a file example
API

Primary features and goals

  • Completely constexpr. All graphics rendering, including generating the sixel output stream can happen during compilation.
  • No dynamic allocations. The backbuffer and the very few internal data structures can live as global static variables.
  • Minimalistic interface and single header implementation.
  • 1, 2, 4 and 8bit palette based back buffers for minimal memory usage. Reasonable standard palettes are provided. 24bit and 32bit back buffers are also provided if the target is something else than sixel.
  • Simple fill_rect(), fill_round_rect(), draw_line() and fill_circle() drawing functions among others.
  • Render proportional text, optionally with kerning, using pre-rendered font textures generated by a custom version of fontbm. Repository includes a set of pre-made (open source) fonts which are trivial to use. UTF-8 is supported.
  • An uncompressed png encoder is included to reduce dependencies.
  • Blit raw 32-bit RGBA image buffers into the palette based back buffer (with or without dithering). Also convert back into a RGBA buffer when needed.
  • Code is cpplint compliant, has a .clang-tidy profile, passes cppcheck and is of course consteval runnable.
  • Code compiles with "-Wall -Wextra -Wpedantic -Weffc++ -Werror" so it can be easily used in any existing C++ project without creating noise.
  • Resistance to unbound behavior (i.e. potential for DoS) when passing unreasonable values.
  • Various other simple image manipulation operations.

[!NOTE] This library is not designed for high fidelity graphics generation and should be more thought of a utility library for software development purposes and embedded devices. Despite that, on HiDPI screens like on Macs the results generally look fairly good. If you want a high fidelity header only 2D C++ rendering library I recommend canvas_ity

Applications

  • Interface rendering on embedded devices. Perfect to target monochrome or grayscale OLED screens or just to save memory when rendering to full RGB OLED screens.
  • Send graphical data to your terminal through a serial or ssh connections from embedded or remote devices.
  • Add graphical output to unit tests.
  • Programmatically render static graphical assets.
  • Help debug dynamic memory handling issues in complex C++ projects.
  • Add text overlays to RGBA8 buffers.
  • ...

Requirements

  • C++20, i.e. gcc 13.3 or newer, clang 16 or newer, MSVC 17 or newer
  • For viewing the sixel image you will need a sixel capable terminal. Windows Terminal, iTerm2 on MacOS and some Linux terminals will work. In Visual Studio Code sixel rendering can be enabled for all terminals using the "terminal.integrated.enableImages": true setting (Seems to be broken on Windows as of 4/2025, but works on MacOS and Linux). There is also an option to output to a kitty graphics enabled terminal.

[!NOTE] The Terminal app on MacOS does not support sixel, please use iTerm2.

[!NOTE] If you want to consteval either the sixel or image data you likely need to increase the constexpr ops limit. With g++ use '-fconstexpr-ops-limit=268435456' with clang use '-fconstexpr-steps=33554432'. The default limit in MSVC usually seems adequate.

Usage

Option 1: Simply copy the header file to your project and include it. Optionally copy the fonts you want from the fonts directory.

Option 2: If you prefer you can add this git repo as a cmake library:

...
add_subdirectory(constixel)
...
target_link_libraries([your target name] PRIVATE constixel::constixel)
...

After that you can simply do this:

#include "constixel/constixel.hpp"
#include "constixel/fonts/[whatever font you want].hpp"

Don't forget to enable C++20/23:

set(CMAKE_CXX_STANDARD 20)

Minimal example

#define CONSTIXEL_ENABLE_COUT
#include "constixel.hpp"

int main() {
    static constixel::image<constixel::format_8bit, 256, 256> image;

    for (int32_t y = 0; y < 16; y++) {
        for (int32_t x = 0; x < 16; x++) {
            image.fill_rect({.x = x * 16, .y = y * 16, .w = 16, .h = 16, 
                             .col = static_cast<uint8_t>(y * 16 + x)});
        }
    }

    image.sixel_to_cout();
}

constixel

Text drawing example

Include a text font and pass the struct name as a template parameter to the draw_string function. The pre-made available fonts in this repo are viewable here. More can be easily added/modified, for instance if you need more than just ASCII character ranges or want symbols/icons.

#define CONSTIXEL_ENABLE_COUT
#include "constixel.hpp"
#include "fonts/ibmplexsans_medium_48_mono.hpp"
using large_font = constixel::ibmplexsans_medium_48_mono;

int main() {
    using namespace constixel;

    static constexpr std::array<const char *, 5> strings {
        "ABCDEFGHIJKLM", "NOPQRTSUVWXYZ", "abcdefghijklm", "nopqrstuvwxyz","1234567890&@.,?!'"""
    };

    image<format_8bit, 512, 312> image;
    for (size_t i = 0; i < strings.size(); i++) { 
        uint8_t col = color::GRAY_RAMP_STOP - static_cast<uint8_t>(i * 3);

        image.draw_string_mono<large_font>({.x = 16, .y = 48 * static_cast<int32_t>(i) + 16, 
                                            .str = strings.at(i), .col = col});
    }

    image.sixel_to_cout();
}

constixel

Consteval sixel example

As std::vector can not escape consteval (yet) so we use std::array. Output of this example should be "Actual byte size: 18537" and the sixel image. The binary will contain the evaluated sixel string.

Compile as such (clang needs -fconstexpr-steps=33554432):

> g++ -fconstexpr-ops-limit=268435456 -std=c++23 constixel.cpp -o constixel -Os
#include "constixel.hpp"

#include <iostream>
#include <cstring>

consteval auto gen_sixel() {
    constixel::image<constixel::format_8bit, 256, 256> image;

    for (int32_t y = 0; y < 16; y++) {
        for (int32_t x = 0; x < 16; x++) {
            image.fill_rect(x * 16, y * 16, 16, 16, static_cast<uint8_t>(y * 16 + x));
        }
    }

    std::array<char, 32767> sixel{};
    char *ptr = sixel.data();
    image.sixel([&ptr](char ch) mutable {
        *ptr++ = ch;
    });
    *ptr++ = '\n';
    *ptr++ = 0;

    return sixel;
}

int main() {
    static const auto sixel = gen_sixel();

    std::cout << "Actual byte size: " << strlen(sixel.data()) << "\n";
    std::cout << sixel.data() << std::endl;
}

Consteval embedded image data example

This example will consteval gen_image_1bit() into a std::array, while dynamically generating the sixel string.

#include "constixel.hpp"

#include <cstring>

consteval auto gen_image_1bit() {
    constixel::image<constixel::format_1bit, 256, 256> image;
    for (int32_t y = 0; y < 16; y++) {
        for (int32_t x = 0; x < 16; x++) {
            image.fill_rect(x * 16, y * 16, 16, 16, static_cast<uint8_t>(y + x) & 1);
        }
    }
    return image;
}

int main() {
    static const auto image = gen_image_1bit();

    printf("image width x height: %d %d x 1bit depth\n", int(image.width()), int(image.height()));
    printf("image instance byte size: %d\n", int(image.size()));

    size_t count = 0;
    image.sixel([&count](char ch) mutable {
        putc(ch, stdout);
        count++;
    });
    putc('\n', stdout);

    printf("sixel byte size: %d\n", int(count));
}

constixel

Saving a PNG to a file example

#include "constixel.hpp"
#include <iostream>
#include <fstream>

int main() {
    static constixel::image<constixel::format_8bit, 256, 256> image;

    for (int32_t y = 0; y < 16; y++) {
        for (int32_t x = 0; x < 16; x++) {
            image.fill_rect(x * 16, y * 16, 16, 16, static_cast<uint8_t>(y * 16 + x));
        }
    }

    std::ofstream file("constixel.png", std::ios::binary);
    image.png([&](char ch){ file.put(ch); });
}

API

Full doxygen generated API docs are at constixel.dev

The following image formats are available. [Width] is the width in pixels, [Height] is the height in pixels. [Grayscale] will set the palette to a grayscale palette with black being the first entry in the palette and white being the last entry in the palette. [UseSpan] allows to pass in an existing std::span/std::array to be used as a backbuffer if so desired.

constixel::image<constixel::format_1bit,  [Width], [Height], [Unused=false],    [UseSpan=false]>
constixel::image<constixel::format_2bit,  [Width], [Height], [Grayscale=false], [UseSpan=false]>
constixel::image<constixel::format_4bi

Related Skills

View on GitHub
GitHub Stars44
CategoryDevelopment
Updated1mo ago
Forks0

Languages

C++

Security Score

95/100

Audited on Feb 12, 2026

No findings