Skip to content

Commit

Permalink
[Renderers/Allegro4] Add Allegro 4.x renderer and demo
Browse files Browse the repository at this point in the history
  • Loading branch information
eigenl committed Feb 16, 2025
1 parent c73dffb commit 56c0397
Show file tree
Hide file tree
Showing 10 changed files with 873 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ option(CLAY_INCLUDE_CPP_EXAMPLE "Build C++ example" OFF)
option(CLAY_INCLUDE_RAYLIB_EXAMPLES "Build raylib examples" OFF)
option(CLAY_INCLUDE_SDL2_EXAMPLES "Build SDL 2 examples" OFF)
option(CLAY_INCLUDE_SDL3_EXAMPLES "Build SDL 3 examples" OFF)
option(CLAY_INCLUDE_ALLEGRO4_EXAMPLE "Build Allegro 4.x example" OFF)

message(STATUS "CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}")

Expand All @@ -33,6 +34,9 @@ endif ()
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL2_EXAMPLES)
add_subdirectory("examples/SDL2-video-demo")
endif ()
if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_ALLEGRO4_EXAMPLE)
add_subdirectory("examples/allegro4-demo")
endif ()
if(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES))
add_subdirectory("examples/SDL3-simple-demo")
endif()
Expand Down
40 changes: 40 additions & 0 deletions examples/allegro4-demo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 3.27)
project(allegro4_demo C)
set(CMAKE_C_STANDARD 99)

include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)

# Fetch pre-built MinGW binaries
FetchContent_Declare(
allegro4
URL https://bitbucket.org/bugsquasher/unofficial-allegro-5-binaries/downloads/A443Deluxe.7z
)

FetchContent_MakeAvailable(allegro4)

link_directories(${allegro4_SOURCE_DIR}/lib/)

add_executable(allegro4_demo main.c alleg4.c)

target_compile_options(allegro4_demo PUBLIC)
target_include_directories(allegro4_demo PUBLIC . ${CMAKE_SOURCE_DIR} ${allegro4_SOURCE_DIR}/include/)

# All global variables Allegro makes available are lost when linking statically,
# at least not when extern-ing all over the place.
target_link_libraries(allegro4_demo PUBLIC alleg44.dll.a)

set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")

add_custom_command(TARGET allegro4_demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${allegro4_SOURCE_DIR}/bin/dlls/alleg44.dll"
$<TARGET_FILE_DIR:allegro4_demo>
)

add_custom_command(
TARGET allegro4_demo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/res
${CMAKE_CURRENT_BINARY_DIR}/res)
306 changes: 306 additions & 0 deletions examples/allegro4-demo/alleg4.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
#define CLAY_IMPLEMENTATION
#include "alleg4.h"

#define CLIPPED_OPERATION(BUF, X, Y, W, H, CODE) { \
int _X0, _Y0, _X1, _Y1; \
get_clip_rect(BUF, &_X0, &_Y0, &_X1, &_Y1); \
set_clip_rect(BUF, X, Y, X+W-1, Y+H-1); \
CODE; \
set_clip_rect(BUF, _X0, _Y0, _X1, _Y1); \
}

typedef struct alleg4_font_store {
size_t size;
FONT *fonts[];
} alleg4_font_store;

static alleg4_font_store *font_store = NULL;

void alleg4_init_fonts(size_t size) {
font_store = malloc(sizeof(alleg4_font_store) + sizeof(FONT*[size]));
font_store->size = size;
for (size_t i = 0; i < size; ++i) {
font_store->fonts[i] = NULL;
}
}

void alleg4_set_font(unsigned int font_id, FONT *font_object) {
font_store->fonts[font_id] = font_object;
}

static inline FONT* get_font(unsigned int font_id) {
if (font_id >= 0 && font_id < font_store->size) {
return font_store->fonts[font_id];
} else {
return font; /* Default built-in font */
}
}

static inline void arc_thickness(
BITMAP *dst,
int x,
int y,
fixed from,
fixed to,
int r,
int color,
int thickness
) {
do {
arc(dst, x, y, from, to, r--, color);
} while (--thickness);
}

static inline void rectfill_wh(
BITMAP *dst,
int x,
int y,
int w,
int h,
int color
) {
// rectfill uses stard and end coordinates instead of size, so we'd have to -1 all over the place
if (w == 1) { vline(dst, x, y, y+h-1, color); }
else if (h == 1) { hline(dst, x, y, x+w-1, color); }
else { rectfill(dst, x, y, x+w-1, y+h-1, color); }
}

/* Radiuses array contains corner radiuses in clockwise order starting from top-left */
static inline void roundrectfill(
BITMAP *dst,
int x,
int y,
int w,
int h,
int color,
int r[]
) {
int top = CLAY__MAX(r[0],r[1]);
int bottom = CLAY__MAX(r[2],r[3]);
int left = CLAY__MAX(r[0],r[3]);
int right = CLAY__MAX(r[1],r[2]);

if (r[0]) {
CLIPPED_OPERATION(dst, x, y, r[0], r[0], {
circlefill(dst, x+r[0], y+r[0], r[0], color);
});
}

if (r[1]) {
CLIPPED_OPERATION(dst, x+w-r[1], y, r[1], r[1], {
circlefill(dst, x+w-1-r[1], y+r[1], r[1], color);
});
}

if (r[2]) {
CLIPPED_OPERATION(dst, x+w-r[2], y+h-r[2], r[2], r[2], {
circlefill(dst, x+w-1-r[2], y+h-1-r[2], r[2], color);
});
}

if (r[3]) {
CLIPPED_OPERATION(dst, x, y+h-r[3], r[3], r[3], {
circlefill(dst, x+r[3], y+h-1-r[3], r[3], color);
});
}

if (top) {
rectfill_wh(dst, x+r[0], y, w-r[0]-r[1], top, color);
}

if (bottom) {
rectfill_wh(dst, x+r[3], y+h-bottom, w-r[2]-r[3], bottom, color);
}

if (left) {
rectfill_wh(dst, x, y+top, left, h-top-bottom, color);
}

if (right) {
rectfill_wh(dst, x+w-right, y+top, right, h-top-bottom, color);
}

rectfill_wh(dst, x+left, y+top, w-left-right, h-top-bottom, color);
}

void alleg4_render(
BITMAP *buffer,
Clay_RenderCommandArray renderCommands
) {
static int crx0, cry0, crx1, cry1;

for (uint32_t i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);
Clay_BoundingBox box = renderCommand->boundingBox;

switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
Clay_Color color = config->backgroundColor;

int radiuses[] = {
config->cornerRadius.topLeft,
config->cornerRadius.topRight,
config->cornerRadius.bottomRight,
config->cornerRadius.bottomLeft
};

if (radiuses[0] + radiuses[1] + radiuses[2] + radiuses[3] > 0) {
roundrectfill(buffer, box.x, box.y, box.width, box.height, ALLEGCOLOR(config->backgroundColor), radiuses);
} else {
rectfill_wh(buffer, box.x, box.y, box.width, box.height, ALLEGCOLOR(config->backgroundColor));
}

break;
}

case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *config = &renderCommand->renderData.text;
char *slice = (char *)calloc(config->stringContents.length + 1, 1);
memcpy(slice, config->stringContents.chars, config->stringContents.length);
FONT *font_object = get_font(config->fontId);
if (is_color_font(font_object)) {
textout_ex(buffer, get_font(config->fontId), slice, box.x, box.y, -1, -1);
} else {
textout_ex(buffer, get_font(config->fontId), slice, box.x, box.y, ALLEGCOLOR(config->textColor), -1);
}
free(slice);
break;
}

case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
Clay_ImageRenderData *config = &renderCommand->renderData.image;
Clay_Color tintColor = config->backgroundColor;
BITMAP *image = (BITMAP *)config->imageData;
if (tintColor.r + tintColor.g + tintColor.b == 0) {
draw_sprite(buffer, image, box.x, box.y);
} else {
set_trans_blender(tintColor.r, tintColor.g, tintColor.b, 0);
draw_lit_sprite(buffer, image, box.x, box.y, tintColor.a);
}
break;
}

case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
get_clip_rect(buffer, &crx0, &cry0, &crx1, &cry1); /* Save current clip rect coordinates */
set_clip_rect(buffer, box.x, box.y, box.x + box.width, box.y + box.height);
break;
}

case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
set_clip_rect(buffer, crx0, cry0, crx1, cry1);
break;
}

case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *config = &renderCommand->renderData.border;

const float tl = config->cornerRadius.topLeft;
const float tr = config->cornerRadius.topRight;
const float bl = config->cornerRadius.bottomLeft;
const float br = config->cornerRadius.bottomRight;

if (config->width.left > 0) {
rectfill_wh(buffer, box.x, box.y + tl, config->width.left, box.height-tl-bl, ALLEGCOLOR(config->color));

/* Top-left half-arc */
if (tl > 0) {
CLIPPED_OPERATION(buffer, box.x, box.y+tl/2, tl, tl/2, {
arc_thickness(buffer, box.x+tl, box.y+tl, itofix(64), itofix(128), tl, ALLEGCOLOR(config->color), config->width.left);
});
}

/* Bottom-left half-arc */
if (bl > 0) {
const int y = box.y+box.height-bl;
CLIPPED_OPERATION(buffer, box.x, y, bl, bl/2, {
arc_thickness(buffer, box.x+bl, y-1, itofix(128), itofix(192), bl, ALLEGCOLOR(config->color), config->width.left);
});
}
}

if (config->width.right > 0) {
rectfill_wh(buffer, box.x+box.width-config->width.right, box.y+tr, config->width.right, box.height-br-tr, ALLEGCOLOR(config->color));

/* Top-right half-arc */
if (tr > 0) {
const int x = box.x+box.width-tr;
CLIPPED_OPERATION(buffer, x, box.y+tr/2, tr, tr/2, {
arc_thickness(buffer, x-1, box.y+tr, itofix(0), itofix(64), tr, ALLEGCOLOR(config->color), config->width.right);
});
}

/* Bottom-right half-arc */
if (br > 0) {
const int y = box.y+box.height-br;
const int x = box.x+box.width-br;
CLIPPED_OPERATION(buffer, x, y, br, br/2, {
arc_thickness(buffer, x-1, y-1, itofix(192), itofix(256), br, ALLEGCOLOR(config->color), config->width.right);
});
}
}

if (config->width.top > 0) {
rectfill_wh(buffer, box.x + tl, box.y, box.width - tr - tl, config->width.top, ALLEGCOLOR(config->color));

/* Top-left half-arc */
if (tl > 0) {
CLIPPED_OPERATION(buffer, box.x, box.y, tl, tl/2, {
arc_thickness(buffer, box.x+tl, box.y+tl, itofix(64), itofix(128), tl, ALLEGCOLOR(config->color), config->width.top);
});
}

/* Top-right half-arc */
if (tr > 0) {
const int x = box.x+box.width-tr;
CLIPPED_OPERATION(buffer, x, box.y, tr, tr/2, {
arc_thickness(buffer, x-1, box.y+tr, itofix(0), itofix(64), tr, ALLEGCOLOR(config->color), config->width.top);
});
}
}

if (config->width.bottom > 0) {
rectfill_wh(buffer, box.x+bl, box.y+box.height-config->width.bottom, box.width-br-bl, config->width.bottom, ALLEGCOLOR(config->color));

/* Bottom-left half-arc */
if (bl > 0) {
const int y = box.y+box.height-bl;
CLIPPED_OPERATION(buffer, box.x, y+bl/2, bl, bl/2, {
arc_thickness(buffer, box.x+bl, y-1, itofix(128), itofix(192), bl, ALLEGCOLOR(config->color), config->width.bottom);
});
}

/* Bottom-right half-arc */
if (br > 0) {
const int y = box.y+box.height-br;
const int x = box.x+box.width-br;
CLIPPED_OPERATION(buffer, x, y+br/2, br, br/2, {
arc_thickness(buffer, x-1, y-1, itofix(192), itofix(256), br, ALLEGCOLOR(config->color), config->width.bottom);
});
}
}

break;
}

case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
Clay_CustomRenderData *config = &renderCommand->renderData.custom;
alleg4_custom_element *callback_info = (alleg4_custom_element *)config->customData;
callback_info->render(buffer, box, callback_info->user_data);
break;
}
}
}
}

Clay_Dimensions alleg4_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
FONT *font_object = get_font(config->fontId);
char *slice = (char *)calloc(text.length + 1, 1);
memcpy(slice, text.chars, text.length);
const Clay_Dimensions d = (Clay_Dimensions) {
.width = text_length(font_object, slice),
.height = text_height(font_object)
};
free(slice);
return d;
}
28 changes: 28 additions & 0 deletions examples/allegro4-demo/alleg4.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef CLAY_ALLEG4_HEADER
#define CLAY_ALLEG4_HEADER

#include "clay.h"
#include <allegro.h>

#define ALLEGCOLOR(COLOR) makecol(COLOR.r, COLOR.g, COLOR.b)

typedef struct {
void (*render)(BITMAP *buffer, Clay_BoundingBox box, void *user_data);
void *user_data;
} alleg4_custom_element;

void alleg4_init_fonts(size_t);
void alleg4_set_font(unsigned int, FONT *);

void alleg4_render(
BITMAP *buffer,
Clay_RenderCommandArray renderCommands
);

Clay_Dimensions alleg4_measure_text(
Clay_StringSlice text,
Clay_TextElementConfig *config,
void *userData
);

#endif
Loading

0 comments on commit 56c0397

Please sign in to comment.