Compare commits

...

5 Commits

Author SHA1 Message Date
Eigen Lenk
673ca1629f
Merge e5cf195d8a into 08e4c5b198 2025-03-26 11:03:53 +00:00
Nic Barker
08e4c5b198 [Core] Fix a bug where ID aliases werent copied on hash collision
Some checks are pending
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Waiting to run
2025-03-26 09:35:15 +13:00
ellie-but-backwards
b1c72a0647
[Bindings/Odin] Remove field hashStringContents in odin bindings (#350) 2025-03-26 09:21:35 +13:00
Igor Karatayev
aee4baee1c
[Core] Guard against hashmap item null dereference (#338) 2025-03-26 09:19:50 +13:00
Eigen Lenk
e5cf195d8a [Renderers/Allegro4] Add Allegro 4.x renderer and demo 2025-02-20 11:47:25 +02:00
11 changed files with 827 additions and 4 deletions

View File

@ -111,7 +111,6 @@ TextElementConfig :: struct {
lineHeight: u16,
wrapMode: TextWrapMode,
textAlignment: TextAlignment,
hashStringContents: bool,
}
ImageElementConfig :: struct {

3
clay.h
View File

@ -1670,6 +1670,7 @@ Clay_LayoutElementHashMapItem* Clay__AddHashMapItem(Clay_ElementId elementId, Cl
item.nextIndex = hashItem->nextIndex;
if (hashItem->generation <= context->generation) { // First collision - assume this is the "same" element
hashItem->elementId = elementId; // Make sure to copy this across. If the stringId reference has changed, we should update the hash item to use the new one.
hashItem->idAlias = idAlias;
hashItem->generation = context->generation + 1;
hashItem->layoutElement = layoutElement;
hashItem->debugData->collision = false;
@ -3810,10 +3811,10 @@ void Clay_SetPointerState(Clay_Vector2 position, bool isPointerDown) {
Clay_LayoutElementHashMapItem *mapItem = Clay__GetHashMapItem(currentElement->id); // TODO think of a way around this, maybe the fact that it's essentially a binary tree limits the cost, but the worst case is not great
int32_t clipElementId = Clay__int32_tArray_GetValue(&context->layoutElementClipElementIds, (int32_t)(currentElement - context->layoutElements.internalArray));
Clay_LayoutElementHashMapItem *clipItem = Clay__GetHashMapItem(clipElementId);
if (mapItem) {
Clay_BoundingBox elementBox = mapItem->boundingBox;
elementBox.x -= root->pointerOffset.x;
elementBox.y -= root->pointerOffset.y;
if (mapItem) {
if ((Clay__PointIsInsideRect(position, elementBox)) && (clipElementId == 0 || (Clay__PointIsInsideRect(position, clipItem->boundingBox)))) {
if (mapItem->onHoverFunction) {
mapItem->onHoverFunction(mapItem->elementId, context->pointerInfo, mapItem->hoverFunctionUserData);

View File

@ -0,0 +1,3 @@
# Allegro 4 demo
To build this demo on your system of choice, include all source files in this directory, as well as in "renderers/alleg4" and link with a 4.x version of Allegro. It's probably easiest to go with a pre-built binary.

View File

@ -0,0 +1,486 @@
#include "renderers/alleg4/alleg4.h"
#include <stdio.h>
#include <math.h>
// Locked 50 FPS
#define TICKRATE_MS 20
#define DEG_TO_RAD(x) ((x) / 180.0 * M_PI)
static volatile int pending_main_loop_update = 0;
void main_loop_ticker() {
pending_main_loop_update = 1;
}
END_OF_FUNCTION(main_loop_ticker);
static const int FONT_ID_BODY_16 = 0;
static const int FONT_ID_LARGE_TITLE = 1;
static Clay_Color COLOR_BLACK = { 0, 0, 0, 255 };
static Clay_Color COLOR_WHITE = { 255, 255, 255, 255 };
static Clay_Color COLOR_RED = { 255, 0, 0, 255 };
#define EXPANDING_LAYOUT { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }
#define BORDER_DEPTH { .width = { 1, 2, 1, 2, 0 }, .color = COLOR_BLACK }
#define BEVELED(PADDING, BG, WIDTH, ...) \
CLAY({ \
.backgroundColor = BG, \
.border = { .width = { 1, 0, 1, 0, 0 }, .color = COLOR_WHITE }, \
.layout.sizing.height = CLAY_SIZING_GROW(0), \
.layout.sizing.width = WIDTH > 0 ? CLAY_SIZING_FIXED(WIDTH) : WIDTH == 0 ? CLAY_SIZING_GROW() : CLAY_SIZING_FIT() \
}) { \
CLAY({ \
.layout.padding.left = PADDING, \
.layout.padding.right = PADDING, \
.layout.sizing.width = CLAY_SIZING_GROW(), \
.layout.sizing.height = CLAY_SIZING_GROW(), \
.layout.childAlignment.x = CLAY_ALIGN_X_CENTER, \
.layout.childAlignment.y = CLAY_ALIGN_Y_CENTER, \
.border = { .width = { 0, 1, 0, 1, 0 }, .color = { 128, 128, 128, 255 }, } \
}) { \
__VA_ARGS__ \
} \
}
static struct {
BITMAP *lib_logo;
BITMAP *environment_category;
BITMAP *coral_reef;
} R;
static void draw_bouncy_ball(BITMAP *buffer, Clay_BoundingBox box, void *user_data) {
#define BALL_R 4
#define BORDER 1
static int x = BALL_R+BORDER, y = BALL_R+BORDER;
static Clay_Vector2 dir = { 1, 1 };
circlefill(buffer, box.x + x, box.y + y, BALL_R, ALLEGCOLOR(COLOR_RED));
x += dir.x;
y += dir.y;
if (x <= BALL_R+BORDER || x >= box.width - (BALL_R+BORDER)) {
dir.x *= -1;
}
if (y <= BALL_R+BORDER || y >= box.height - (BALL_R+BORDER)) {
dir.y *= -1;
}
}
static Clay_String test_article;
typedef struct {
intptr_t offset;
intptr_t memory;
} EncartaDemo_Arena;
typedef struct {
EncartaDemo_Arena frameArena;
unsigned int counter;
} EncartaDemo_Data;
EncartaDemo_Data EncartaDemo_Initialize() {
test_article = CLAY_STRING("A coral reef is an underwater ecosystem characterized by reef-building corals. Reefs are formed of colonies of coral polyps held together by calcium carbonate. Most coral reefs are built from stony corals, whose polyps cluster in groups.\n\nCoral belongs to the class Anthozoa in the animal phylum Cnidaria, which includes sea anemones and jellyfish. Unlike sea anemones, corals secrete hard carbonate exoskeletons that support and protect the coral. Most reefs grow best in warm, shallow, clear, sunny and agitated water. Coral reefs first appeared 485 million years ago, at the dawn of the Early Ordovician, displacing the microbial and sponge reefs of the Cambrian.\n\nSometimes called rainforests of the sea, shallow coral reefs form some of Earth's most diverse ecosystems. They occupy less than 0.1% of the world's ocean area, about half the area of France, yet they provide a home for at least 25% of all marine species, including fish, mollusks, worms, crustaceans, echinoderms, sponges, tunicates and other cnidarians. Coral reefs flourish in ocean waters that provide few nutrients. They are most commonly found at shallow depths in tropical waters, but deep water and cold water coral reefs exist on smaller scales in other areas.\n\nShallow tropical coral reefs have declined by 50% since 1950, partly because they are sensitive to water conditions. They are under threat from excess nutrients (nitrogen and phosphorus), rising ocean heat content and acidification, overfishing (e.g., from blast fishing, cyanide fishing, spearfishing on scuba), sunscreen use, and harmful land-use practices, including runoff and seeps (e.g., from injection wells and cesspools).\n\nCoral reefs deliver ecosystem services for tourism, fisheries and shoreline protection. The annual global economic value of coral reefs has been estimated at anywhere from US$30-375 billion (1997 and 2003 estimates) to US$2.7 trillion (a 2020 estimate) to US$9.9 trillion (a 2014 estimate).\n\nThough the shallow water tropical coral reefs are best known, there are also deeper water reef-forming corals, which live in colder water and in temperate seas.\n\n\n(( Material ))\n\nAs the name implies, coral reefs are made up of coral skeletons from mostly intact coral colonies. As other chemical elements present in corals become incorporated into the calcium carbonate deposits, aragonite is formed. However, shell fragments and the remains of coralline algae such as the green-segmented genus Halimeda can add to the reef's ability to withstand damage from storms and other threats. Such mixtures are visible in structures such as Eniwetok Atoll.");
EncartaDemo_Data data = {
.frameArena = { .memory = (intptr_t)malloc(1024) },
.counter = 0
};
return data;
}
void BeveledButton(Clay_String text, int padding, int fixedWidth) {
BEVELED(padding, Clay_Hovered() ? ((Clay_Color) { 224, 224, 224, 255 }) : ((Clay_Color) { 192, 192, 192, 255 }), fixedWidth, {
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.fontSize = 16,
.textColor = COLOR_BLACK,
.textAlignment = CLAY_TEXT_ALIGN_CENTER,
.lineHeight = 12
}));
})
}
static void menubar(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("menubar"),
.backgroundColor = {64, 64, 64, 255 },
.border = {
.width = { 0, 0, 0, 1, 0 },
.color = COLOR_BLACK
},
.layout = {
.sizing = {
.height = CLAY_SIZING_FIXED(24),
.width = CLAY_SIZING_GROW(0)
},
}
}) {
BeveledButton(CLAY_STRING("Menu"), 12, -1);
BeveledButton(CLAY_STRING("Contents"), 12, -1);
BeveledButton(CLAY_STRING("Find"), 12, -1);
BeveledButton(CLAY_STRING("Go Back"), 12, -1);
BeveledButton(CLAY_STRING("Gallery"), 12, -1);
BeveledButton(CLAY_STRING("Atlas"), 12, -1);
BeveledButton(CLAY_STRING("Timeline"), 12, -1);
BeveledButton(CLAY_STRING("Help"), 12, -1);
BEVELED(0, ((Clay_Color) { 192, 192, 192, 255 }), 0, { });
}
}
static void largeArticleHeading(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("titlebar"),
.layout = {
.sizing.width = CLAY_SIZING_GROW(),
.padding = { 8, 8, 8, 0 },
}
}) {
CLAY({
.backgroundColor = {255, 255, 128, 255 },
.cornerRadius = { 12, 0, 0, 12 },
.layout = {
.sizing = {
.height = CLAY_SIZING_GROW(),
.width = CLAY_SIZING_GROW()
},
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }
},
.border = { .width = CLAY_BORDER_ALL(1), .color = COLOR_BLACK }
}) {
CLAY_TEXT(CLAY_STRING("Coral Reef"), CLAY_TEXT_CONFIG({
.fontId = FONT_ID_LARGE_TITLE,
.textColor = COLOR_BLACK
}));
}
}
}
static void demoTitleBox(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("DemoHeader"),
.backgroundColor = COLOR_WHITE,
.layout.sizing.width = CLAY_SIZING_FIXED(250),
.layout.padding = CLAY_PADDING_ALL(5),
.layout.childGap = 5,
.border = BORDER_DEPTH,
.cornerRadius = { 8, 8, 0, 0 }
}) {
CLAY({
.id = CLAY_ID("LibraryLogo"),
.layout = { .sizing = CLAY_SIZING_FIXED(28) },
.image = { .imageData = R.lib_logo, .sourceDimensions = {28, 28} },
.backgroundColor = { 128+sin(DEG_TO_RAD(data->counter))*127, 0, 128+sin(DEG_TO_RAD(data->counter/2))*127, 128 }
}) {}
CLAY_TEXT(CLAY_STRING("Clay Encarta 95:\nAllegro4 Demo"), CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.textColor = COLOR_BLACK
}));
}
}
static void categoryBox(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("CategoryImage"),
.backgroundColor = { 90, 90, 90, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = {
.width = CLAY_SIZING_FIXED(250),
.height = CLAY_SIZING_FIT()
}
},
.border = BORDER_DEPTH
}) {
CLAY({
.image = {
.imageData = R.environment_category,
.sourceDimensions = {250, 120}
},
.layout.sizing.height = CLAY_SIZING_FIXED(120),
.layout.padding = CLAY_PADDING_ALL(6),
.layout.childAlignment.y = CLAY_ALIGN_Y_BOTTOM
}) {
CLAY_TEXT(CLAY_STRING("Environment"), CLAY_TEXT_CONFIG({
.fontId = FONT_ID_LARGE_TITLE,
.textColor = COLOR_BLACK
}));
}
CLAY({
.backgroundColor = {64, 64, 64, 255 },
.layout = {
.padding = { 1, 2, 1, 2 },
.sizing = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_FIT(32),
}
}
}) {
BeveledButton(CLAY_STRING("Show List"), 8, 0);
BeveledButton(CLAY_STRING("Change\nCategory"), 8, 0);
BeveledButton(CLAY_STRING("<"), 8, 28);
BeveledButton(CLAY_STRING(">"), 8, 28);
}
}
}
static void articleBox(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("ArticleImage"),
.backgroundColor = { 90, 90, 90, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing.width = CLAY_SIZING_FIXED(250)
},
.border = BORDER_DEPTH
}) {
CLAY({
.image = {
.imageData = R.coral_reef,
.sourceDimensions = {250, 230}
},
.layout.sizing.height = CLAY_SIZING_FIXED(230)
}) {}
CLAY({
.layout.sizing.height = CLAY_SIZING_FIXED(20),
.layout.sizing.width = CLAY_SIZING_GROW(),
.layout.padding = { 1, 1, 1, 0 },
}) {
BEVELED(0, ((Clay_Color) { 192, 192, 192, 255 }), 0, {
CLAY_TEXT(CLAY_STRING("Biodiversity of a coral reef"), CLAY_TEXT_CONFIG({
.fontId = -1,
.textColor = COLOR_BLACK
}));
});
}
CLAY({
.backgroundColor = {64, 64, 64, 255 },
.layout = {
.padding = { 1, 2, 1, 2 },
.sizing = {
.height = CLAY_SIZING_FIT(32),
.width = CLAY_SIZING_GROW()
}
}
}) {
BeveledButton(CLAY_STRING("Copy"), 8, 0);
BeveledButton(CLAY_STRING("Print"), 8, 0);
BeveledButton(CLAY_STRING("<"), 8, 28);
BeveledButton(CLAY_STRING(">"), 8, 28);
}
}
}
static void sidebar(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("sidebar"),
.layout = {
.childGap = 8,
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing.width = CLAY_SIZING_FIT()
}
}) {
demoTitleBox(data);
categoryBox(data);
articleBox(data);
}
}
static void article(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("article"),
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = EXPANDING_LAYOUT
},
.backgroundColor = COLOR_WHITE,
.border = BORDER_DEPTH,
.cornerRadius = { 8, 8, 0, 0 },
}) {
CLAY({
.scroll.vertical = true,
.layout.padding = CLAY_PADDING_ALL(8),
}) {
CLAY_TEXT(test_article, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.textColor = COLOR_BLACK
}));
}
CLAY({
.backgroundColor = {64, 64, 64, 255 },
.layout = {
.padding = { 1, 2, 1, 2 },
.sizing = {
.height = CLAY_SIZING_FIT(32),
.width = CLAY_SIZING_GROW()
}
}
}) {
BeveledButton(CLAY_STRING("Outline"), 8, 0);
BeveledButton(CLAY_STRING("See Also"), 8, 0);
BeveledButton(CLAY_STRING("Copy"), 8, 0);
BeveledButton(CLAY_STRING("Print"), 8, 0);
BEVELED(0, ((Clay_Color) { 192, 192, 192, 255 }), 64, {
alleg4_custom_element *ball_renderer = (alleg4_custom_element *)(data->frameArena.memory + data->frameArena.offset);
data->frameArena.offset += sizeof(alleg4_custom_element);
*ball_renderer = (alleg4_custom_element) {
&draw_bouncy_ball, NULL
};
CLAY({
.layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } },
.custom = { .customData = ball_renderer }
}) { }
})
}
}
}
Clay_RenderCommandArray EncartaDemo_CreateLayout(EncartaDemo_Data *data) {
data->frameArena.offset = 0;
Clay_BeginLayout();
// Build UI here
CLAY({
.id = CLAY_ID("outer-container"),
.backgroundColor = { 64, 64, 64, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = EXPANDING_LAYOUT
}
}) {
menubar(data);
largeArticleHeading(data);
CLAY({
.id = CLAY_ID("lower-content"),
.backgroundColor = { 64, 64, 64, 255 },
.layout = {
.sizing = EXPANDING_LAYOUT,
.childGap = 8,
.padding = CLAY_PADDING_ALL(8)
}
}) {
sidebar(data);
article(data);
}
}
return Clay_EndLayout();
}
void HandleClayErrors(Clay_ErrorData errorData) {
printf("%s", errorData.errorText.chars);
}
int main(int argc, char *argv[]) {
EncartaDemo_Data demoData = EncartaDemo_Initialize();
BITMAP *buffer;
if (allegro_init() != 0) {
return 1;
}
install_keyboard();
install_mouse();
install_timer();
set_color_depth(16);
int driver = GFX_AUTODETECT;
#ifdef _WIN32
driver = GFX_AUTODETECT_WINDOWED;
#endif
set_window_title("Clay Encarta 95");
if (set_gfx_mode(driver, 800, 600, 0, 0) != 0) {
if (set_gfx_mode(GFX_SAFE, 800, 600, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
return 2;
}
}
if (install_int(main_loop_ticker, TICKRATE_MS) < 0) {
allegro_message("Error installing interrupt\n%s\n", allegro_error);
return 4;
}
LOCK_VARIABLE(pending_main_loop_update);
LOCK_FUNCTION(main_loop_ticker);
alleg4_init_fonts(2); /* Allocate space for 1 font */
alleg4_set_font(FONT_ID_BODY_16, load_font("res/8x16.pcx", NULL, NULL));
alleg4_set_font(FONT_ID_LARGE_TITLE, load_font("res/ex06.pcx", NULL, NULL));
buffer = create_bitmap(SCREEN_W, SCREEN_H);
R.lib_logo = load_bitmap("res/liblogo.pcx", NULL);
R.environment_category = load_bitmap("res/env.pcx", NULL);
R.coral_reef = load_bitmap("res/coral.pcx", NULL);
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
Clay_Initialize(clayMemory, (Clay_Dimensions) {
(float)SCREEN_W, (float)SCREEN_H
}, (Clay_ErrorHandler) {
HandleClayErrors
});
Clay_SetMeasureTextFunction(alleg4_measure_text, NULL);
show_os_cursor(MOUSE_CURSOR_ARROW);
enable_hardware_cursor();
int _mouse_w = mouse_w, _mouse_z = mouse_z;
while (!keypressed()) {
if (!pending_main_loop_update) {
continue;
}
pending_main_loop_update = false;
clear_to_color(buffer, makecol(0, 0, 0));
if (mouse_needs_poll()) {
poll_mouse();
}
Clay_SetLayoutDimensions((Clay_Dimensions) {
(float)SCREEN_W, (float)SCREEN_H
});
Clay_SetPointerState((Clay_Vector2) {
mouse_x, mouse_y
}, mouse_b & 1);
Clay_UpdateScrollContainers(true, (Clay_Vector2) {
mouse_w - _mouse_w, mouse_z - _mouse_z
}, (float)TICKRATE_MS / 1000.f);
_mouse_w = mouse_w;
_mouse_z = mouse_z;
Clay_RenderCommandArray renderCommands = EncartaDemo_CreateLayout(&demoData);
alleg4_render(buffer, renderCommands);
demoData.counter ++;
if (gfx_capabilities & GFX_HW_CURSOR) {
blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
} else {
show_mouse(NULL);
blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
show_mouse(screen);
}
}
return 0;
}
END_OF_MAIN()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

306
renderers/alleg4/alleg4.c Normal file
View File

@ -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
renderers/alleg4/alleg4.h Normal file
View File

@ -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