Compare commits

...

11 Commits

Author SHA1 Message Date
Julio Ernesto Rodríguez Cabañas
a12c8a9e35
Merge 0242c58ff5 into 6d23a35d15 2025-02-11 20:46:08 -06:00
Nic Barker
6d23a35d15 [Examples/clay-official-website] Update compiled wasm for official website example
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-02-12 13:09:29 +13:00
Nic Barker
b4933a6e4c [Examples/clay-official-website] Switch default renderer back to HTML for official website example 2025-02-12 13:08:46 +13:00
Nic Barker
9f91450431 [Bindings/Odin] Update odin bindings to include debug tools changes 2025-02-12 13:07:05 +13:00
Nic Barker
e35bba079e [Core] Update debug tools to include text alignment 2025-02-12 13:05:48 +13:00
Nic Barker
d637e2a122 [Documentation] Fix documentation mistake for border configuration 2025-02-12 12:02:57 +13:00
Nic Barker
e6e0cd5a46 [Documentation] Update README with better documentation of Clay_ElementDeclaration 2025-02-12 11:59:50 +13:00
Julio Ernesto Rodríguez Cabañas
0242c58ff5
Consistency 2025-02-11 21:43:12 +00:00
Julio Ernesto Rodríguez Cabañas
1307834f3b Ensure fonts are closed during cleanup 2025-02-10 22:20:44 +00:00
Julio Ernesto Rodríguez Cabañas
4c27b0cf6a
Merge branch 'nicbarker:main' into 254-sdl3-renderer-text-improvements 2025-02-10 22:06:12 +00:00
Julio Ernesto Rodríguez Cabañas
3d4a63263c Use text engine to render text on the SDL3 renderer 2025-02-10 21:47:14 +00:00
12 changed files with 181 additions and 58 deletions

104
README.md
View File

@ -808,7 +808,7 @@ Clay_TextElementConfig {
`CLAY_TEXT_CONFIG(.textColor = {120, 120, 120, 255})` `CLAY_TEXT_CONFIG(.textColor = {120, 120, 120, 255})`
Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout. Uses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
--- ---
@ -990,28 +990,95 @@ typedef struct {
Clay_CustomElementConfig custom; Clay_CustomElementConfig custom;
Clay_ScrollElementConfig scroll; Clay_ScrollElementConfig scroll;
Clay_BorderElementConfig border; Clay_BorderElementConfig border;
uintptr_t userData; void *userData;
} Clay_ElementDeclaration; } Clay_ElementDeclaration;
``` ```
**Fields** **Fields**
**`.color`** - `Clay_Color` **`.id`** - `Clay_ElementID`
`.backgroundColor = {120, 120, 120, 255} })` `CLAY({ .id = CLAY_ID("FileButton") })`
Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout. Uses [Clay_ElementId](#clay_elementid). Tags the element with an ID that can be later used to query data about the element, and gives it a human readable name in the debug tools.
IDs are typically generated using the [CLAY_ID](#clay_id), [CLAY_IDI](#clay_idi), [CLAY_ID_LOCAL](#clay_id_local) and [CLAY_IDI_LOCAL](#clay_idi_local) macros.
---
**`.layout`** - `Clay_LayoutConfig`
`CLAY({ .layout = { .padding = { 16, 16, 12, 12 }, .layoutDirection = CLAY_TOP_TO_BOTTOM } })`
Uses [Clay_LayoutConfig](#clay_layoutconfig). Controls various settings related to _layout_, which can be thought of as "the size and position of this element and its children".
---
**`.backgroundColor`** - `Clay_Color`
`CLAY({ .backgroundColor = {120, 120, 120, 255} } })`
Uses [Clay_Color](#clay_color). Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
--- ---
**`.cornerRadius`** - `float` **`.cornerRadius`** - `float`
`CLAY_RECTANGLE({ .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 })` `CLAY({ .cornerRadius = { .topLeft = 16, .topRight = 16, .bottomLeft = 16, .bottomRight = 16 } })`
Defines the radius in pixels for the arc of rectangle corners (`0` is square, `rectangle.width / 2` is circular). Defines the radius in pixels for the arc of rectangle corners (`0` is square, `rectangle.width / 2` is circular).
Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER({ .cornerRadius = CLAY_CORNER_RADIUS(10) })` Note that the `CLAY_CORNER_RADIUS(radius)` function-like macro is available to provide short hand for setting all four corner radii to the same value. e.g. `CLAY_BORDER({ .cornerRadius = CLAY_CORNER_RADIUS(10) })`
---
**`.image`** - `Clay_ImageElementConfig`
`CLAY({ .image = { .imageData = &myImage, .sourceDimensions = { 640, 480 } } })`
Uses [Clay_ImageElementConfig](#clay_imageelementconfig). Configures the element as an image element. Causes a render command with type `IMAGE` to be emitted.
---
**`.floating`** - `Clay_FloatingElementConfig`
`CLAY({ .floating = { .attachTo = CLAY_ATTACH_TO_PARENT } })`
Uses [Clay_FloatingElementConfig](#clay_floatingelementconfig). Configures the element as an floating element, which allows it to stack "in front" and "on top" of other elements without affecting sibling or parent size or position.
---
**`.custom`** - `Clay_CustomElementConfig`
`CLAY({ .custom = { .customData = &my3DModel } })`
Uses [Clay_CustomElementConfig](#clay_customelementconfig). Configures the element as a custom element, which allows you to pass custom data through to the renderer. Causes a render command with type `CUSTOM` to be emitted.
---
**`.scroll`** - `Clay_ScrollElementConfig`
`CLAY({ .scroll = { .vertical = true } })`
Uses [Clay_ScrollElementConfig](#clay_scrollelementconfig). Configures the element as a scroll element, which causes child elements to be clipped / masked if they overflow, and together with [Clay_UpdateScrollContainer](#clay_updatescrollcontainers) enables scrolling of child contents.
---
**`.border`** - `Clay_BorderElementConfig`
`CLAY({ .border = { .width = { .left = 5 }, .color = COLOR_BLUE } })`
Uses [Clay_BorderElementConfig](#clay_borderelementconfig). Configures the element as a border element, which instructs the renderer to draw coloured border lines along the perimeter of this element's bounding box. Causes a render command with type `BORDER` to be emitted.
---
**`.userData`** - `void *`
`CLAY({ .userData = &extraData })`
Transparently passes a pointer through to the corresponding [Clay_RenderCommands](#clay_rendercommand)s generated by this element.
---
**Examples** **Examples**
```C ```C
@ -1285,6 +1352,14 @@ typedef struct Clay_BorderElementConfig
**Fields** **Fields**
**`.color`** - `Clay_Color`
`CLAY({ .border = { .color = { 255, 0, 0, 255 } } })`
Uses [Clay_Color](#clay_color). Specifies the shared color for all borders configured by this element. Conventionally accepts `rgba` float values between 0 and 255, but interpretation is left up to the renderer and does not affect layout.
---
**`.width`** - `Clay_BorderWidth` **`.width`** - `Clay_BorderWidth`
`CLAY({ .border = { .width = { .left = 2, .right = 10 } } })` `CLAY({ .border = { .width = { .left = 2, .right = 10 } } })`
@ -1303,12 +1378,6 @@ Configures the width and color of borders to be drawn between children. These bo
--- ---
**`.color`** - `Clay_Color`
`CLAY({ .border = { .color = { 255, 0, 0, 255 } } })`
Defines the radius in pixels for the arc of border corners (`0` is square, `rectangle.width / 2` is circular). It is up to the renderer to decide how to interpolate between differing border widths and colors across shared corners.
**Examples** **Examples**
```C ```C
@ -1657,6 +1726,17 @@ switch (renderCommand->commandType) {
Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM` will be created. Element is subject to [culling](#visibility-culling). Otherwise, a single `Clay_RenderCommand` with `commandType = CLAY_RENDER_COMMAND_TYPE_CUSTOM` will be created.
### Clay_Color
```C
typedef struct {
float r, g, b, a;
} Clay_Color;
```
`Clay_Color` is an RGBA color struct used in Clay's declarations and rendering. By convention the channels are represented as 0-255, but this is left up to the renderer.
Note: when using the debug tools, their internal colors are represented as 0-255.
### Clay_String ### Clay_String
```C ```C

View File

@ -96,6 +96,12 @@ TextWrapMode :: enum EnumBackingType {
None, None,
} }
TextAlignment :: enum EnumBackingType {
Left,
Center,
Right,
}
TextElementConfig :: struct { TextElementConfig :: struct {
textColor: Color, textColor: Color,
fontId: u16, fontId: u16,
@ -103,6 +109,7 @@ TextElementConfig :: struct {
letterSpacing: u16, letterSpacing: u16,
lineHeight: u16, lineHeight: u16,
wrapMode: TextWrapMode, wrapMode: TextWrapMode,
textAlignment: TextAlignment,
hashStringContents: bool, hashStringContents: bool,
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

11
clay.h
View File

@ -3346,7 +3346,7 @@ void Clay__RenderDebugView(void) {
// .letterSpacing // .letterSpacing
CLAY_TEXT(CLAY_STRING("Letter Spacing"), infoTitleConfig); CLAY_TEXT(CLAY_STRING("Letter Spacing"), infoTitleConfig);
CLAY_TEXT(Clay__IntToString(textConfig->letterSpacing), infoTextConfig); CLAY_TEXT(Clay__IntToString(textConfig->letterSpacing), infoTextConfig);
// .lineSpacing // .wrapMode
CLAY_TEXT(CLAY_STRING("Wrap Mode"), infoTitleConfig); CLAY_TEXT(CLAY_STRING("Wrap Mode"), infoTitleConfig);
Clay_String wrapMode = CLAY_STRING("WORDS"); Clay_String wrapMode = CLAY_STRING("WORDS");
if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE) { if (textConfig->wrapMode == CLAY_TEXT_WRAP_NONE) {
@ -3355,6 +3355,15 @@ void Clay__RenderDebugView(void) {
wrapMode = CLAY_STRING("NEWLINES"); wrapMode = CLAY_STRING("NEWLINES");
} }
CLAY_TEXT(wrapMode, infoTextConfig); CLAY_TEXT(wrapMode, infoTextConfig);
// .textAlignment
CLAY_TEXT(CLAY_STRING("Text Alignment"), infoTitleConfig);
Clay_String textAlignment = CLAY_STRING("LEFT");
if (textConfig->textAlignment == CLAY_TEXT_ALIGN_CENTER) {
textAlignment = CLAY_STRING("CENTER");
} else if (textConfig->textAlignment == CLAY_TEXT_ALIGN_RIGHT) {
textAlignment = CLAY_STRING("RIGHT");
}
CLAY_TEXT(textAlignment, infoTextConfig);
// .textColor // .textColor
CLAY_TEXT(CLAY_STRING("Text Color"), infoTitleConfig); CLAY_TEXT(CLAY_STRING("Text Color"), infoTitleConfig);
Clay__RenderDebugViewColor(textConfig->textColor, infoTextConfig); Clay__RenderDebugViewColor(textConfig->textColor, infoTextConfig);

View File

@ -19,13 +19,14 @@ static const Clay_Color COLOR_LIGHT = (Clay_Color) {224, 215, 210, 255};
typedef struct app_state { typedef struct app_state {
SDL_Window *window; SDL_Window *window;
SDL_Renderer *renderer; Clay_SDL3RendererData rendererData;
ClayVideoDemo_Data demoData; ClayVideoDemo_Data demoData;
} AppState; } AppState;
static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) static inline Clay_Dimensions SDL_MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData)
{ {
TTF_Font *font = gFonts[config->fontId]; TTF_Font **fonts = userData;
TTF_Font *font = fonts[config->fontId];
int width, height; int width, height;
if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) { if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {
@ -54,19 +55,31 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
} }
*appstate = state; *appstate = state;
if (!SDL_CreateWindowAndRenderer("Clay Demo", 640, 480, 0, &state->window, &state->renderer)) { if (!SDL_CreateWindowAndRenderer("Clay Demo", 640, 480, 0, &state->window, &state->rendererData.renderer)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window and renderer: %s", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window and renderer: %s", SDL_GetError());
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
SDL_SetWindowResizable(state->window, true); SDL_SetWindowResizable(state->window, true);
state->rendererData.textEngine = TTF_CreateRendererTextEngine(state->rendererData.renderer);
if (!state->rendererData.textEngine) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create text engine from renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
state->rendererData.fonts = SDL_calloc(1, sizeof(TTF_Font *));
if (!state->rendererData.fonts) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to allocate memory for the font array: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
TTF_Font *font = TTF_OpenFont("resources/Roboto-Regular.ttf", 24); TTF_Font *font = TTF_OpenFont("resources/Roboto-Regular.ttf", 24);
if (!font) { if (!font) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to load font: %s", SDL_GetError()); SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to load font: %s", SDL_GetError());
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
gFonts[FONT_ID] = font; state->rendererData.fonts[FONT_ID] = font;
/* Initialize Clay */ /* Initialize Clay */
uint64_t totalMemorySize = Clay_MinMemorySize(); uint64_t totalMemorySize = Clay_MinMemorySize();
@ -78,7 +91,7 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
int width, height; int width, height;
SDL_GetWindowSize(state->window, &width, &height); SDL_GetWindowSize(state->window, &width, &height);
Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }, (Clay_ErrorHandler) { HandleClayErrors }); Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }, (Clay_ErrorHandler) { HandleClayErrors });
Clay_SetMeasureTextFunction(SDL_MeasureText, 0); Clay_SetMeasureTextFunction(SDL_MeasureText, state->rendererData.fonts);
state->demoData = ClayVideoDemo_Initialize(); state->demoData = ClayVideoDemo_Initialize();
@ -99,10 +112,14 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
break; break;
case SDL_EVENT_MOUSE_MOTION: case SDL_EVENT_MOUSE_MOTION:
Clay_SetPointerState((Clay_Vector2) { event->motion.x, event->motion.y }, Clay_SetPointerState((Clay_Vector2) { event->motion.x, event->motion.y },
event->motion.state & SDL_BUTTON_LEFT); event->motion.state & SDL_BUTTON_LMASK);
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
Clay_SetPointerState((Clay_Vector2) { event->button.x, event->button.y },
event->button.button == SDL_BUTTON_LEFT);
break; break;
case SDL_EVENT_MOUSE_WHEEL: case SDL_EVENT_MOUSE_WHEEL:
Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->motion.xrel, event->motion.yrel }, 0.01f); Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->wheel.x, event->wheel.y }, 0.01f);
break; break;
default: default:
break; break;
@ -117,12 +134,12 @@ SDL_AppResult SDL_AppIterate(void *appstate)
Clay_RenderCommandArray render_commands = ClayVideoDemo_CreateLayout(&state->demoData); Clay_RenderCommandArray render_commands = ClayVideoDemo_CreateLayout(&state->demoData);
SDL_SetRenderDrawColor(state->renderer, 0, 0, 0, 255); SDL_SetRenderDrawColor(state->rendererData.renderer, 0, 0, 0, 255);
SDL_RenderClear(state->renderer); SDL_RenderClear(state->rendererData.renderer);
SDL_RenderClayCommands(state->renderer, &render_commands); SDL_Clay_RenderClayCommands(&state->rendererData, &render_commands);
SDL_RenderPresent(state->renderer); SDL_RenderPresent(state->rendererData.renderer);
return SDL_APP_CONTINUE; return SDL_APP_CONTINUE;
} }
@ -138,12 +155,23 @@ void SDL_AppQuit(void *appstate, SDL_AppResult result)
AppState *state = appstate; AppState *state = appstate;
if (state) { if (state) {
if (state->renderer) if (state->rendererData.renderer)
SDL_DestroyRenderer(state->renderer); SDL_DestroyRenderer(state->rendererData.renderer);
if (state->window) if (state->window)
SDL_DestroyWindow(state->window); SDL_DestroyWindow(state->window);
if (state->rendererData.fonts) {
for(size_t i = 0; i < sizeof(state->rendererData.fonts) / sizeof(*state->rendererData.fonts); i++) {
TTF_CloseFont(state->rendererData.fonts[i]);
}
SDL_free(state->rendererData.fonts);
}
if (state->rendererData.textEngine)
TTF_DestroyRendererTextEngine(state->rendererData.textEngine);
SDL_free(state); SDL_free(state);
} }
TTF_Quit(); TTF_Quit();

View File

@ -3,7 +3,7 @@
double windowWidth = 1024, windowHeight = 768; double windowWidth = 1024, windowHeight = 768;
float modelPageOneZRotation = 0; float modelPageOneZRotation = 0;
uint32_t ACTIVE_RENDERER_INDEX = 1; uint32_t ACTIVE_RENDERER_INDEX = 0;
const uint32_t FONT_ID_BODY_16 = 0; const uint32_t FONT_ID_BODY_16 = 0;
const uint32_t FONT_ID_TITLE_56 = 1; const uint32_t FONT_ID_TITLE_56 = 1;

View File

@ -3,15 +3,18 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h> #include <SDL3_ttf/SDL_ttf.h>
/* This needs to be global because the "MeasureText" callback doesn't have a typedef struct {
* user data parameter */ SDL_Renderer *renderer;
static TTF_Font *gFonts[1]; TTF_TextEngine *textEngine;
TTF_Font **fonts;
} Clay_SDL3RendererData;
/* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with /* Global for convenience. Even in 4K this is enough for smooth curves (low radius or rect size coupled with
* no AA or low resolution might make it appear as jagged curves) */ * no AA or low resolution might make it appear as jagged curves) */
static int NUM_CIRCLE_SEGMENTS = 16; static int NUM_CIRCLE_SEGMENTS = 16;
//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles. //all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.
static void SDL_RenderFillRoundedRect(SDL_Renderer *renderer, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) { static void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData, const SDL_FRect rect, const float cornerRadius, const Clay_Color _color) {
const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 }; const SDL_FColor color = { _color.r/255, _color.g/255, _color.b/255, _color.a/255 };
int indexCount = 0, vertexCount = 0; int indexCount = 0, vertexCount = 0;
@ -109,11 +112,11 @@ static void SDL_RenderFillRoundedRect(SDL_Renderer *renderer, const SDL_FRect re
indices[indexCount++] = vertexCount - 1; //LT indices[indexCount++] = vertexCount - 1; //LT
// Render everything // Render everything
SDL_RenderGeometry(renderer, NULL, vertices, vertexCount, indices, indexCount); SDL_RenderGeometry(rendererData->renderer, NULL, vertices, vertexCount, indices, indexCount);
} }
static void SDL_RenderArc(SDL_Renderer *renderer, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) { static void SDL_Clay_RenderArc(Clay_SDL3RendererData *rendererData, const SDL_FPoint center, const float radius, const float startAngle, const float endAngle, const float thickness, const Clay_Color color) {
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a); SDL_SetRenderDrawColor(rendererData->renderer, color.r, color.g, color.b, color.a);
const float radStart = startAngle * (SDL_PI_F / 180.0f); const float radStart = startAngle * (SDL_PI_F / 180.0f);
const float radEnd = endAngle * (SDL_PI_F / 180.0f); const float radEnd = endAngle * (SDL_PI_F / 180.0f);
@ -133,11 +136,11 @@ static void SDL_RenderArc(SDL_Renderer *renderer, const SDL_FPoint center, const
SDL_roundf(center.x + SDL_cosf(angle) * clampedRadius), SDL_roundf(center.x + SDL_cosf(angle) * clampedRadius),
SDL_roundf(center.y + SDL_sinf(angle) * clampedRadius) }; SDL_roundf(center.y + SDL_sinf(angle) * clampedRadius) };
} }
SDL_RenderLines(renderer, points, numCircleSegments + 1); SDL_RenderLines(rendererData->renderer, points, numCircleSegments + 1);
} }
} }
static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArray *rcommands) static void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Clay_RenderCommandArray *rcommands)
{ {
for (size_t i = 0; i < rcommands->length; i++) { for (size_t i = 0; i < rcommands->length; i++) {
Clay_RenderCommand *rcmd = Clay_RenderCommandArray_Get(rcommands, i); Clay_RenderCommand *rcmd = Clay_RenderCommandArray_Get(rcommands, i);
@ -147,24 +150,20 @@ static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArr
switch (rcmd->commandType) { switch (rcmd->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleRenderData *config = &rcmd->renderData.rectangle; Clay_RectangleRenderData *config = &rcmd->renderData.rectangle;
SDL_SetRenderDrawColor(renderer, config->backgroundColor.r, config->backgroundColor.g, config->backgroundColor.b, config->backgroundColor.a); SDL_SetRenderDrawColor(rendererData->renderer, config->backgroundColor.r, config->backgroundColor.g, config->backgroundColor.b, config->backgroundColor.a);
if (config->cornerRadius.topLeft > 0) { if (config->cornerRadius.topLeft > 0) {
SDL_RenderFillRoundedRect(renderer, rect, config->cornerRadius.topLeft, config->backgroundColor); SDL_Clay_RenderFillRoundedRect(rendererData, rect, config->cornerRadius.topLeft, config->backgroundColor);
} else { } else {
SDL_RenderFillRect(renderer, &rect); SDL_RenderFillRect(rendererData->renderer, &rect);
} }
} break; } break;
case CLAY_RENDER_COMMAND_TYPE_TEXT: { case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *config = &rcmd->renderData.text; Clay_TextRenderData *config = &rcmd->renderData.text;
const SDL_Color color = { config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a }; TTF_Font *font = rendererData->fonts[config->fontId];
TTF_Text *text = TTF_CreateText(rendererData->textEngine, font, config->stringContents.chars, config->stringContents.length);
TTF_Font *font = gFonts[config->fontId]; TTF_SetTextColor(text, config->textColor.r, config->textColor.g, config->textColor.b, config->textColor.a);
SDL_Surface *surface = TTF_RenderText_Blended(font, config->stringContents.chars, config->stringContents.length, color); TTF_DrawRendererText(text, rect.x, rect.y);
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); TTF_DestroyText(text);
SDL_RenderTexture(renderer, texture, NULL, &rect);
SDL_DestroySurface(surface);
SDL_DestroyTexture(texture);
} break; } break;
case CLAY_RENDER_COMMAND_TYPE_BORDER: { case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *config = &rcmd->renderData.border; Clay_BorderRenderData *config = &rcmd->renderData.border;
@ -177,57 +176,57 @@ static void SDL_RenderClayCommands(SDL_Renderer *renderer, Clay_RenderCommandArr
.bottomRight = SDL_min(config->cornerRadius.bottomRight, minRadius) .bottomRight = SDL_min(config->cornerRadius.bottomRight, minRadius)
}; };
//edges //edges
SDL_SetRenderDrawColor(renderer, config->color.r, config->color.g, config->color.b, config->color.a); SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);
if (config->width.left > 0) { if (config->width.left > 0) {
const float starting_y = rect.y + clampedRadii.topLeft; const float starting_y = rect.y + clampedRadii.topLeft;
const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft; const float length = rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft;
SDL_FRect line = { rect.x, starting_y, config->width.left, length }; SDL_FRect line = { rect.x, starting_y, config->width.left, length };
SDL_RenderFillRect(renderer, &line); SDL_RenderFillRect(rendererData->renderer, &line);
} }
if (config->width.right > 0) { if (config->width.right > 0) {
const float starting_x = rect.x + rect.w - (float)config->width.right; const float starting_x = rect.x + rect.w - (float)config->width.right;
const float starting_y = rect.y + clampedRadii.topRight; const float starting_y = rect.y + clampedRadii.topRight;
const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight; const float length = rect.h - clampedRadii.topRight - clampedRadii.bottomRight;
SDL_FRect line = { starting_x, starting_y, config->width.right, length }; SDL_FRect line = { starting_x, starting_y, config->width.right, length };
SDL_RenderFillRect(renderer, &line); SDL_RenderFillRect(rendererData->renderer, &line);
} }
if (config->width.top > 0) { if (config->width.top > 0) {
const float starting_x = rect.x + clampedRadii.topLeft; const float starting_x = rect.x + clampedRadii.topLeft;
const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight; const float length = rect.w - clampedRadii.topLeft - clampedRadii.topRight;
SDL_FRect line = { starting_x, rect.y, length, config->width.top }; SDL_FRect line = { starting_x, rect.y, length, config->width.top };
SDL_RenderFillRect(renderer, &line); SDL_RenderFillRect(rendererData->renderer, &line);
} }
if (config->width.bottom > 0) { if (config->width.bottom > 0) {
const float starting_x = rect.x + clampedRadii.bottomLeft; const float starting_x = rect.x + clampedRadii.bottomLeft;
const float starting_y = rect.y + rect.h - (float)config->width.bottom; const float starting_y = rect.y + rect.h - (float)config->width.bottom;
const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight; const float length = rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight;
SDL_FRect line = { starting_x, starting_y, length, config->width.bottom }; SDL_FRect line = { starting_x, starting_y, length, config->width.bottom };
SDL_SetRenderDrawColor(renderer, config->color.r, config->color.g, config->color.b, config->color.a); SDL_SetRenderDrawColor(rendererData->renderer, config->color.r, config->color.g, config->color.b, config->color.a);
SDL_RenderFillRect(renderer, &line); SDL_RenderFillRect(rendererData->renderer, &line);
} }
//corners //corners
if (config->cornerRadius.topLeft > 0) { if (config->cornerRadius.topLeft > 0) {
const float centerX = rect.x + clampedRadii.topLeft -1; const float centerX = rect.x + clampedRadii.topLeft -1;
const float centerY = rect.y + clampedRadii.topLeft; const float centerY = rect.y + clampedRadii.topLeft;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topLeft,
180.0f, 270.0f, config->width.top, config->color); 180.0f, 270.0f, config->width.top, config->color);
} }
if (config->cornerRadius.topRight > 0) { if (config->cornerRadius.topRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.topRight -1; const float centerX = rect.x + rect.w - clampedRadii.topRight -1;
const float centerY = rect.y + clampedRadii.topRight; const float centerY = rect.y + clampedRadii.topRight;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.topRight,
270.0f, 360.0f, config->width.top, config->color); 270.0f, 360.0f, config->width.top, config->color);
} }
if (config->cornerRadius.bottomLeft > 0) { if (config->cornerRadius.bottomLeft > 0) {
const float centerX = rect.x + clampedRadii.bottomLeft -1; const float centerX = rect.x + clampedRadii.bottomLeft -1;
const float centerY = rect.y + rect.h - clampedRadii.bottomLeft -1; const float centerY = rect.y + rect.h - clampedRadii.bottomLeft -1;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomLeft,
90.0f, 180.0f, config->width.bottom, config->color); 90.0f, 180.0f, config->width.bottom, config->color);
} }
if (config->cornerRadius.bottomRight > 0) { if (config->cornerRadius.bottomRight > 0) {
const float centerX = rect.x + rect.w - clampedRadii.bottomRight -1; //TODO: why need to -1 in all calculations??? const float centerX = rect.x + rect.w - clampedRadii.bottomRight -1; //TODO: why need to -1 in all calculations???
const float centerY = rect.y + rect.h - clampedRadii.bottomRight -1; const float centerY = rect.y + rect.h - clampedRadii.bottomRight -1;
SDL_RenderArc(renderer, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight, SDL_Clay_RenderArc(rendererData, (SDL_FPoint){centerX, centerY}, clampedRadii.bottomRight,
0.0f, 90.0f, config->width.bottom, config->color); 0.0f, 90.0f, config->width.bottom, config->color);
} }