diff --git a/examples/termbox2-image-demo/CMakeLists.txt b/examples/termbox2-image-demo/CMakeLists.txt
new file mode 100644
index 0000000..c20c299
--- /dev/null
+++ b/examples/termbox2-image-demo/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.25)
+project(clay_examples_termbox2_image_demo C)
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_BUILD_TYPE Release)
+set(CMAKE_C_FLAGS_RELEASE "-O3")
+
+include(FetchContent)
+set(FETCHCONTENT_QUIET FALSE)
+
+FetchContent_Declare(
+ termbox2
+ GIT_REPOSITORY "https://github.com/termbox/termbox2.git"
+ GIT_TAG "9c9281a9a4c971a2be57f8645e828ec99fd555e8"
+ GIT_PROGRESS TRUE
+ GIT_SHALLOW TRUE
+)
+FetchContent_MakeAvailable(termbox2)
+
+FetchContent_Declare(
+ stb
+ GIT_REPOSITORY "https://github.com/nothings/stb.git"
+ GIT_TAG "f58f558c120e9b32c217290b80bad1a0729fbb2c"
+ GIT_PROGRESS TRUE
+ GIT_SHALLOW TRUE
+)
+FetchContent_MakeAvailable(stb)
+
+add_executable(clay_examples_termbox2_image_demo main.c)
+
+target_compile_options(clay_examples_termbox2_image_demo PUBLIC)
+target_include_directories(clay_examples_termbox2_image_demo PUBLIC . PRIVATE ${termbox2_SOURCE_DIR} PRIVATE ${stb_SOURCE_DIR})
+target_link_libraries(clay_examples_termbox2_image_demo PRIVATE m) # Used by stb_image.h
+
+add_custom_command(
+ TARGET clay_examples_termbox2_image_demo POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+ ${CMAKE_CURRENT_SOURCE_DIR}/resources
+ ${CMAKE_CURRENT_BINARY_DIR}/resources)
diff --git a/examples/termbox2-image-demo/main.c b/examples/termbox2-image-demo/main.c
new file mode 100644
index 0000000..b666151
--- /dev/null
+++ b/examples/termbox2-image-demo/main.c
@@ -0,0 +1,706 @@
+/*
+ Unlicense
+
+ Copyright (c) 2025 Mivirl
+
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ For more information, please refer to
+*/
+
+#define CLAY_IMPLEMENTATION
+#include "../../clay.h"
+#include "../../renderers/termbox2/clay_renderer_termbox2.c"
+
+#define TB_IMPL
+#include "termbox2.h"
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+
+#define STB_IMAGE_RESIZE_IMPLEMENTATION
+#include "stb_image_resize2.h"
+
+
+// -------------------------------------------------------------------------------------------------
+// -- Data structures
+
+struct img_group {
+ clay_tb_image thumbnail;
+ clay_tb_image image;
+ clay_tb_image image_1;
+ clay_tb_image image_2;
+ int width;
+ int height;
+};
+typedef struct img_group img_group;
+
+
+// -------------------------------------------------------------------------------------------------
+// -- Internal state
+
+// If the program should exit the main render/interaction loop
+bool end_loop = false;
+
+// If the debug tools should be displayed
+bool debugMode = false;
+
+
+// -------------------------------------------------------------------------------------------------
+// -- Internal utility functions
+
+img_group img_group_load(const char *filename)
+{
+ img_group rv;
+ rv.thumbnail = Clay_Termbox_Image_Load_File(filename);
+ rv.image = Clay_Termbox_Image_Load_File(filename);
+ rv.image_1 = Clay_Termbox_Image_Load_File(filename);
+ rv.image_2 = Clay_Termbox_Image_Load_File(filename);
+ if (NULL == rv.thumbnail.pixel_data
+ || NULL == rv.image.pixel_data
+ || NULL == rv.image_1.pixel_data
+ || NULL == rv.image_2.pixel_data) {
+ exit(1);
+ }
+ rv.width = rv.image.pixel_width;
+ rv.height = rv.image.pixel_height;
+ return rv;
+}
+void img_group_free(img_group *img)
+{
+ Clay_Termbox_Image_Free(&img->thumbnail);
+ Clay_Termbox_Image_Free(&img->image);
+ Clay_Termbox_Image_Free(&img->image_1);
+ Clay_Termbox_Image_Free(&img->image_2);
+}
+
+
+// -------------------------------------------------------------------------------------------------
+// -- Clay components
+
+void component_text_pair(const char *key, const char *value)
+{
+ size_t keylen = strlen(key);
+ size_t vallen = strlen(value);
+ Clay_String keytext = (Clay_String) {
+ .length = keylen,
+ .chars = key,
+ };
+ Clay_String valtext = (Clay_String) {
+ .length = vallen,
+ .chars = value,
+ };
+ Clay_TextElementConfig *textconfig =
+ CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff } });
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = {
+ .size.minMax = {
+ .min = strlen("Border chars CLAY_TB_IMAGE_MODE_UNICODE_FAST") * Clay_Termbox_Cell_Width(),
+ }
+ },
+ }
+ },
+ }) {
+ CLAY_TEXT(keytext, textconfig);
+ CLAY({ .layout = { .sizing = CLAY_SIZING_GROW(1) } }) { }
+ CLAY_TEXT(valtext, textconfig);
+ }
+
+}
+
+void component_termbox_settings(void)
+{
+ CLAY({
+ .layout = {
+ .sizing = CLAY_SIZING_FIT(),
+ .padding = {
+ 6 * Clay_Termbox_Cell_Width(),
+ 6 * Clay_Termbox_Cell_Width(),
+ 2 * Clay_Termbox_Cell_Height(),
+ 2 * Clay_Termbox_Cell_Height(),
+ }
+ },
+ .border = {
+ .width = CLAY_BORDER_ALL(1),
+ .color = { 0x00, 0x00, 0x00, 0xff }
+ },
+ .backgroundColor = { 0x7f, 0x00, 0x00, 0x7f }
+ }) {
+ const char *color_mode = NULL;
+ switch (clay_tb_color_mode) {
+ case TB_OUTPUT_NORMAL: {
+ color_mode = "TB_OUTPUT_NORMAL";
+ break;
+ }
+ case TB_OUTPUT_256: {
+ color_mode = "TB_OUTPUT_256";
+ break;
+ }
+ case TB_OUTPUT_216: {
+ color_mode = "TB_OUTPUT_216";
+ break;
+ }
+ case TB_OUTPUT_GRAYSCALE: {
+ color_mode = "TB_OUTPUT_GRAYSCALE";
+ break;
+ }
+ case TB_OUTPUT_TRUECOLOR: {
+ color_mode = "TB_OUTPUT_TRUECOLOR";
+ break;
+ }
+ case CLAY_TB_OUTPUT_NOCOLOR: {
+ color_mode = "CLAY_TB_OUTPUT_NOCOLOR";
+ break;
+ }
+ default: {
+ color_mode = "INVALID";
+ break;
+ }
+ }
+ const char *border_mode = NULL;
+ switch (clay_tb_border_mode) {
+ case CLAY_TB_BORDER_MODE_ROUND: {
+ border_mode = "CLAY_TB_BORDER_MODE_ROUND";
+ break;
+ }
+ case CLAY_TB_BORDER_MODE_MINIMUM: {
+ border_mode = "CLAY_TB_BORDER_MODE_MINIMUM";
+ break;
+ }
+ default: {
+ border_mode = "INVALID";
+ break;
+ }
+ }
+ const char *border_chars = NULL;
+ switch (clay_tb_border_chars) {
+ case CLAY_TB_BORDER_CHARS_ASCII: {
+ border_chars = "CLAY_TB_BORDER_CHARS_ASCII";
+ break;
+ }
+ case CLAY_TB_BORDER_CHARS_UNICODE: {
+ border_chars = "CLAY_TB_BORDER_CHARS_UNICODE";
+ break;
+ }
+ case CLAY_TB_BORDER_CHARS_BLANK: {
+ border_chars = "CLAY_TB_BORDER_CHARS_BLANK";
+ break;
+ }
+ case CLAY_TB_BORDER_CHARS_NONE: {
+ border_chars = "CLAY_TB_BORDER_CHARS_NONE";
+ break;
+ }
+ default: {
+ border_chars = "INVALID";
+ break;
+ }
+ }
+ const char *image_mode = NULL;
+ switch (clay_tb_image_mode) {
+ case CLAY_TB_IMAGE_MODE_PLACEHOLDER: {
+ image_mode = "CLAY_TB_IMAGE_MODE_PLACEHOLDER";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_BG: {
+ image_mode = "CLAY_TB_IMAGE_MODE_BG";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_ASCII_FG: {
+ image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_ASCII_FG_FAST: {
+ image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FG_FAST";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_ASCII: {
+ image_mode = "CLAY_TB_IMAGE_MODE_ASCII";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_ASCII_FAST: {
+ image_mode = "CLAY_TB_IMAGE_MODE_ASCII_FAST";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_UNICODE: {
+ image_mode = "CLAY_TB_IMAGE_MODE_UNICODE";
+ break;
+ }
+ case CLAY_TB_IMAGE_MODE_UNICODE_FAST: {
+ image_mode = "CLAY_TB_IMAGE_MODE_UNICODE_FAST";
+ break;
+ }
+ default: {
+ image_mode = "INVALID";
+ break;
+ }
+ }
+ const char *transparency = NULL;
+ if (clay_tb_transparency) {
+ transparency = "true";
+ } else {
+ transparency = "false";
+ }
+
+ CLAY({
+ .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM },
+ }) {
+ component_text_pair("Color mode", color_mode);
+ component_text_pair("Border mode", border_mode);
+ component_text_pair("Border chars", border_chars);
+ component_text_pair("Image mode", image_mode);
+ component_text_pair("Transparency", transparency);
+ }
+ }
+}
+
+void component_keybinds(void)
+{
+ CLAY({
+ .layout = {
+ .sizing = CLAY_SIZING_FIT(),
+ .padding = {
+ 4 * Clay_Termbox_Cell_Width(),
+ 4 * Clay_Termbox_Cell_Width(),
+ 2 * Clay_Termbox_Cell_Height(),
+ 2 * Clay_Termbox_Cell_Height(),
+ }
+ },
+ .backgroundColor = { 0x00, 0x7f, 0x7f, 0xff }
+ }) {
+ CLAY_TEXT(
+ CLAY_STRING(
+ "Termbox2 renderer test\n"
+ "\n"
+ "Keybinds:\n"
+ " c/C - Cycle through color modes\n"
+ " b/B - Cycle through border modes\n"
+ " h/H - Cycle through border characters\n"
+ " i/I - Cycle through image modes\n"
+ " t/T - Toggle transparency\n"
+ " d/D - Toggle debug mode\n"
+ " q/Q - Quit\n"
+ ),
+ CLAY_TEXT_CONFIG({ .textColor = { 0xff, 0xff, 0xff, 0xff }})
+ );
+ }
+}
+
+void component_image(img_group *img_pair)
+{
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_GROW(),
+ .height = CLAY_SIZING_GROW()
+ },
+ .layoutDirection = CLAY_TOP_TO_BOTTOM,
+ .childAlignment = {
+ .x = CLAY_ALIGN_X_CENTER,
+ .y = CLAY_ALIGN_Y_CENTER
+ },
+ .childGap = 1 * Clay_Termbox_Cell_Height()
+ },
+ .backgroundColor = { 0x24, 0x24, 0x24, 0xff }
+ }) {
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_GROW(),
+ },
+ },
+ .image = {
+ .imageData = &img_pair->image,
+ },
+ .aspectRatio = { (float)img_pair->width / img_pair->height }
+ }) { }
+ component_keybinds();
+ }
+}
+
+void component_image_small(img_group **img_pairs, int count, int selected_index)
+{
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_PERCENT(0.25),
+ },
+ .layoutDirection = CLAY_TOP_TO_BOTTOM,
+ .childGap = 20,
+ .childAlignment = {
+ .x = CLAY_ALIGN_X_CENTER,
+ .y = CLAY_ALIGN_Y_CENTER
+ },
+ },
+ }) {
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_PERCENT(0.7),
+ },
+ },
+ .image = {
+ .imageData = &img_pairs[selected_index]->image_1,
+ },
+ .aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }
+ }) { }
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_GROW(),
+ },
+ },
+ .image = {
+ .imageData = &img_pairs[selected_index]->image_2,
+ },
+ .aspectRatio = { (float)img_pairs[selected_index]->width / img_pairs[selected_index]->height }
+ }) { }
+ component_termbox_settings();
+ }
+}
+
+void component_thumbnails(img_group **img_pairs, int count, int selected_index)
+{
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_PERCENT(0.1),
+ .height = CLAY_SIZING_GROW()
+ },
+ .layoutDirection = CLAY_TOP_TO_BOTTOM,
+ .childGap = 20
+ },
+ .backgroundColor = { 0x42, 0x42, 0x42, 0xff }
+ }) {
+ for (int i = 0; i < count; ++i) {
+ Clay_BorderElementConfig border;
+ if (i == selected_index) {
+ border = (Clay_BorderElementConfig) {
+ .width =CLAY_BORDER_OUTSIDE(10),
+ .color = { 0x00, 0x30, 0xc0, 0x8f }
+ };
+ } else {
+ border = (Clay_BorderElementConfig) {
+ .width = CLAY_BORDER_OUTSIDE(0),
+ };
+ }
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_GROW(),
+ },
+ },
+ .border = border,
+ .image = {
+ .imageData = &img_pairs[i]->thumbnail,
+ },
+ .aspectRatio = { (float)img_pairs[i]->width / img_pairs[i]->height }
+ }) { }
+ }
+ }
+}
+
+int selected_thumbnail = 0;
+const int thumbnail_count = 5;
+Clay_RenderCommandArray CreateLayout(struct img_group **imgs)
+{
+ Clay_BeginLayout();
+ CLAY({
+ .layout = {
+ .sizing = {
+ .width = CLAY_SIZING_GROW(),
+ .height = CLAY_SIZING_GROW()
+ },
+ .childAlignment = {
+ .x = CLAY_ALIGN_X_LEFT,
+ .y = CLAY_ALIGN_Y_CENTER
+ },
+ .childGap = 64
+ },
+ .backgroundColor = { 0x24, 0x24, 0x24, 0xff }
+ }) {
+ component_thumbnails(imgs, thumbnail_count, selected_thumbnail);
+ component_image_small(imgs, thumbnail_count, selected_thumbnail);
+ component_image(imgs[selected_thumbnail]);
+ }
+ return Clay_EndLayout();
+}
+
+
+// -------------------------------------------------------------------------------------------------
+// -- Interactive functions
+
+void handle_clay_errors(Clay_ErrorData errorData)
+{
+ Clay_Termbox_Close();
+ fprintf(stderr, "%s", errorData.errorText.chars);
+ exit(1);
+}
+
+/**
+ Process events received from termbox2 and handle interaction
+ */
+void handle_termbox_events(void)
+{
+ // Wait up to 100ms for an event (key/mouse press, terminal resize) before continuing
+ // If an event is already available, this doesn't wait. Will not wait due to the previous call
+ // to termbox_waitfor_event. Increasing the wait time reduces load without reducing
+ // responsiveness (but will of course prevent other code from running on this thread while it's
+ // waiting)
+ struct tb_event evt;
+ int ms_to_wait = 0;
+ int err = tb_peek_event(&evt, ms_to_wait);
+
+ switch (err) {
+ default:
+ case TB_ERR_NO_EVENT: {
+ break;
+ }
+ case TB_ERR_POLL: {
+ if (EINTR != tb_last_errno()) {
+ Clay_Termbox_Close();
+ fprintf(stderr, "Failed to read event from TTY\n");
+ exit(1);
+ }
+ break;
+ }
+ case TB_OK: {
+ switch (evt.type) {
+ case TB_EVENT_RESIZE: {
+ Clay_SetLayoutDimensions((Clay_Dimensions) {
+ Clay_Termbox_Width(),
+ Clay_Termbox_Height()
+ });
+ break;
+ }
+ case TB_EVENT_KEY: {
+ if (TB_KEY_CTRL_C == evt.key) {
+ end_loop = true;
+ break;
+ }
+ if (0 != evt.ch) {
+ switch (evt.ch) {
+ case 'q':
+ case 'Q': {
+ end_loop = true;
+ break;
+ }
+ case 'd':
+ case 'D': {
+ debugMode = !debugMode;
+ Clay_SetDebugModeEnabled(debugMode);
+ break;
+ }
+ case 'c': {
+ int new_mode = clay_tb_color_mode - 1;
+ new_mode = (0 <= new_mode) ? new_mode : TB_OUTPUT_TRUECOLOR;
+ Clay_Termbox_Set_Color_Mode(new_mode);
+ break;
+ }
+ case 'C': {
+ int new_mode = (clay_tb_color_mode + 1) % (TB_OUTPUT_TRUECOLOR + 1);
+ Clay_Termbox_Set_Color_Mode(new_mode);
+ break;
+ }
+ case 'b': {
+ enum border_mode new_mode = clay_tb_border_mode - 1;
+ new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
+ ? new_mode
+ : CLAY_TB_BORDER_MODE_MINIMUM;
+ Clay_Termbox_Set_Border_Mode(new_mode);
+ break;
+ }
+ case 'B': {
+ enum border_mode new_mode = (clay_tb_border_mode + 1)
+ % (CLAY_TB_BORDER_MODE_MINIMUM + 1);
+ new_mode = (CLAY_TB_BORDER_MODE_DEFAULT < new_mode)
+ ? new_mode
+ : CLAY_TB_BORDER_MODE_ROUND;
+ Clay_Termbox_Set_Border_Mode(new_mode);
+ break;
+ }
+ case 'h': {
+ enum border_chars new_chars = clay_tb_border_chars - 1;
+ new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
+ ? new_chars
+ : CLAY_TB_BORDER_CHARS_NONE;
+ Clay_Termbox_Set_Border_Chars(new_chars);
+ break;
+ }
+ case 'H': {
+ enum border_chars new_chars
+ = (clay_tb_border_chars + 1) % (CLAY_TB_BORDER_CHARS_NONE + 1);
+ new_chars = (CLAY_TB_BORDER_CHARS_DEFAULT < new_chars)
+ ? new_chars
+ : CLAY_TB_BORDER_CHARS_ASCII;
+ Clay_Termbox_Set_Border_Chars(new_chars);
+ break;
+ }
+ case 'i': {
+ enum image_mode new_mode = clay_tb_image_mode - 1;
+ new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
+ ? new_mode
+ : CLAY_TB_IMAGE_MODE_UNICODE_FAST;
+ Clay_Termbox_Set_Image_Mode(new_mode);
+ break;
+ }
+ case 'I': {
+ enum image_mode new_mode = (clay_tb_image_mode + 1)
+ % (CLAY_TB_IMAGE_MODE_UNICODE_FAST + 1);
+ new_mode = (CLAY_TB_IMAGE_MODE_DEFAULT < new_mode)
+ ? new_mode
+ : CLAY_TB_IMAGE_MODE_PLACEHOLDER;
+ Clay_Termbox_Set_Image_Mode(new_mode);
+ break;
+ }
+ case 't':
+ case 'T': {
+ Clay_Termbox_Set_Transparency(!clay_tb_transparency);
+ }
+ }
+ } else if (0 != evt.key) {
+ switch (evt.key) {
+ case TB_KEY_ARROW_UP: {
+ selected_thumbnail = (selected_thumbnail > 0) ? selected_thumbnail - 1 : 0;
+ break;
+ }
+ case TB_KEY_ARROW_DOWN: {
+ selected_thumbnail = (selected_thumbnail < thumbnail_count - 1) ? selected_thumbnail + 1 : thumbnail_count - 1;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case TB_EVENT_MOUSE: {
+ Clay_Vector2 mousePosition = {
+ (float)evt.x * Clay_Termbox_Cell_Width(),
+ (float)evt.y * Clay_Termbox_Cell_Height()
+ };
+
+ // Mouse release events may not be produced by all terminals, and will
+ // be sent during hover, so can't be used to detect when the mouse has
+ // been released
+
+ switch (evt.key) {
+ case TB_KEY_MOUSE_LEFT: {
+ Clay_SetPointerState(mousePosition, true);
+ break;
+ }
+ case TB_KEY_MOUSE_RIGHT: {
+ Clay_SetPointerState(mousePosition, false);
+ break;
+ }
+ case TB_KEY_MOUSE_MIDDLE: {
+ Clay_SetPointerState(mousePosition, false);
+ break;
+ }
+ case TB_KEY_MOUSE_RELEASE: {
+ Clay_SetPointerState(mousePosition, false);
+ break;
+ }
+ case TB_KEY_MOUSE_WHEEL_UP: {
+ Clay_Vector2 scrollDelta = { 0, 1 * Clay_Termbox_Cell_Height() };
+ Clay_UpdateScrollContainers(false, scrollDelta, 1);
+ break;
+ }
+ case TB_KEY_MOUSE_WHEEL_DOWN: {
+ Clay_Vector2 scrollDelta = { 0, -1 * Clay_Termbox_Cell_Height() };
+ Clay_UpdateScrollContainers(false, scrollDelta, 1);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+int main(void)
+{
+ img_group *imgs[thumbnail_count];
+ img_group img_shark = img_group_load("resources/512px-Shark_antwerp_zoo.jpeg");
+ img_group img_castle = img_group_load("resources/512px-Balmoral_Castle_30_July_2011.jpeg");
+ img_group img_dog = img_group_load("resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg");
+ img_group img_rosa = img_group_load("resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg");
+ img_group img_vorderer = img_group_load("resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg");
+ imgs[0] = &img_shark;
+ imgs[1] = &img_castle;
+ imgs[2] = &img_dog;
+ imgs[3] = &img_rosa;
+ imgs[4] = &img_vorderer;
+
+ int num_elements = 3 * 8192;
+ Clay_SetMaxElementCount(num_elements);
+
+ uint64_t size = Clay_MinMemorySize();
+ void *memory = malloc(size);
+ if (NULL == memory) { exit(1); }
+
+ Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(size, memory);
+
+ Clay_Termbox_Initialize(
+ TB_OUTPUT_256, CLAY_TB_BORDER_MODE_DEFAULT, CLAY_TB_BORDER_CHARS_DEFAULT, CLAY_TB_IMAGE_MODE_DEFAULT, false);
+
+ Clay_Initialize(clay_arena, (Clay_Dimensions) { Clay_Termbox_Width(), Clay_Termbox_Height() },
+ (Clay_ErrorHandler) { handle_clay_errors, NULL });
+
+ Clay_SetMeasureTextFunction(Clay_Termbox_MeasureText, NULL);
+
+ // Initial render before waiting for events
+ Clay_RenderCommandArray commands = CreateLayout(imgs);
+ Clay_Termbox_Render(commands);
+ tb_present();
+
+ while (!end_loop) {
+ // Block until event is available. Optional, but reduces load since this demo is purely
+ // synchronous to user input.
+ Clay_Termbox_Waitfor_Event();
+
+ handle_termbox_events();
+
+ commands = CreateLayout(imgs);
+
+ Clay_Termbox_Render(commands);
+ tb_present();
+ }
+
+ Clay_Termbox_Close();
+ img_group_free(&img_shark);
+ img_group_free(&img_castle);
+ img_group_free(&img_dog);
+ img_group_free(&img_rosa);
+ img_group_free(&img_vorderer);
+ free(memory);
+ return 0;
+}
diff --git a/examples/termbox2-image-demo/readme.md b/examples/termbox2-image-demo/readme.md
new file mode 100644
index 0000000..6241a40
--- /dev/null
+++ b/examples/termbox2-image-demo/readme.md
@@ -0,0 +1,88 @@
+# Termbox2 renderer demo
+
+Terminal-based renderer using [termbox2](https://github.com/termbox/termbox2)
+
+This demo shows a color palette and a few different components. It allows
+changing configuration settings for colors, border size rounding mode,
+characters used for borders, and transparency.
+
+```
+Keybinds:
+c/C - Cycle through color modes
+b/B - Cycle through border modes
+h/H - Cycle through border characters
+i/I - Cycle through image modes
+t/T - Toggle transparency
+d/D - Toggle debug mode
+q/Q - Quit
+```
+
+Configuration can be also be overriden by environment variables:
+- `CLAY_TB_COLOR_MODE`
+ - `NORMAL`
+ - `256`
+ - `216`
+ - `GRAYSCALE`
+ - `TRUECOLOR`
+ - `NOCOLOR`
+- `CLAY_TB_BORDER_CHARS`
+ - `DEFAULT`
+ - `ASCII`
+ - `UNICODE`
+ - `NONE`
+- `CLAY_TB_IMAGE_MODE`
+ - `DEFAULT`
+ - `PLACEHOLDER`
+ - `BG`
+ - `ASCII_FG`
+ - `ASCII`
+ - `UNICODE`
+ - `ASCII_FG_FAST`
+ - `ASCII_FAST`
+ - `UNICODE_FAST`
+- `CLAY_TB_TRANSPARENCY`
+ - `1`
+ - `0`
+- `CLAY_TB_CELL_PIXELS`
+ - `widthxheight`
+
+## Building
+
+Build the binary with cmake
+
+```sh
+mkdir build
+cd build
+cmake ..
+make
+```
+
+Then run the executable:
+
+```sh
+./clay_examples_termbox2_demo
+```
+
+## Attributions
+
+Resources used:
+- `512px-Shark_antwerp_zoo.jpeg`
+ - Retrieved from
+ - License: [Creative Commons Attribution 3.0 Unported](https://creativecommons.org/licenses/by/3.0/)
+ - No changes made
+- `512px-Balmoral_Castle_30_July_2011.jpg`
+ - Retrieved from
+ - License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)
+ - No changes made
+- `512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg`
+ - Retrieved from
+ - License: [Creative Commons Attribution-ShareAlike 3.0 Unported](https://creativecommons.org/licenses/by-sa/3.0/)
+ - No changes made
+- `512px-Rosa_Cubana_2018-09-21_1610.jpeg`
+ - Retrieved from
+ - License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)
+ - No changes made
+- `512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg`
+ - Retrieved from
+ - License: [Creative Commons Attribution-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/)
+ - No changes made
diff --git a/examples/termbox2-image-demo/resources/512px-Balmoral_Castle_30_July_2011.jpeg b/examples/termbox2-image-demo/resources/512px-Balmoral_Castle_30_July_2011.jpeg
new file mode 100644
index 0000000..051a604
Binary files /dev/null and b/examples/termbox2-image-demo/resources/512px-Balmoral_Castle_30_July_2011.jpeg differ
diff --git a/examples/termbox2-image-demo/resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg b/examples/termbox2-image-demo/resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg
new file mode 100644
index 0000000..2c79f5e
Binary files /dev/null and b/examples/termbox2-image-demo/resources/512px-German_Shepherd_(aka_Alsatian_and_Alsatian_Wolf_Dog),_Deutscher_Schäferhund_(Folder_(IV)_22.jpeg differ
diff --git a/examples/termbox2-image-demo/resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg b/examples/termbox2-image-demo/resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg
new file mode 100644
index 0000000..82c79fe
Binary files /dev/null and b/examples/termbox2-image-demo/resources/512px-Rosa_Cubana_2018-09-21_1610.jpeg differ
diff --git a/examples/termbox2-image-demo/resources/512px-Shark_antwerp_zoo.jpeg b/examples/termbox2-image-demo/resources/512px-Shark_antwerp_zoo.jpeg
new file mode 100644
index 0000000..a487d8e
Binary files /dev/null and b/examples/termbox2-image-demo/resources/512px-Shark_antwerp_zoo.jpeg differ
diff --git a/examples/termbox2-image-demo/resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg b/examples/termbox2-image-demo/resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg
new file mode 100644
index 0000000..8bd8a22
Binary files /dev/null and b/examples/termbox2-image-demo/resources/512px-Vorderer_Graben_10_Bamberg_20190830_001.jpeg differ