Clean up SDL code and fix sloppy mistakes

This commit is contained in:
Rats 2025-10-21 18:41:25 -05:00
parent eb8368216a
commit 6824a942a7
4 changed files with 268 additions and 416 deletions

View file

@ -100,7 +100,7 @@ video_demo_layout :: proc(data: ^Video_Demo_Data) -> clay.ClayArray(clay.RenderC
// Child elements go inside braces
if clay.UI(clay.ID("HeaderBar"))(
{
layout = {sizing = {height = clay.SizingFixed(60), width = clay.SizingFixed(0)}, padding = {16, 16, 0, 0}, childGap = 16, childAlignment = {y = .Center}},
layout = {sizing = {height = clay.SizingFixed(60), width = clay.SizingGrow()}, padding = {16, 16, 0, 0}, childGap = 16, childAlignment = {y = .Center}},
backgroundColor = contentBackgroundColor,
cornerRadius = clay.CornerRadiusAll(8),
},
@ -154,7 +154,7 @@ video_demo_layout :: proc(data: ^Video_Demo_Data) -> clay.ClayArray(clay.RenderC
clay.TextDynamic(document.title, clay.TextConfig({fontId = VIDEO_DEMO_FONT_ID_BODY, fontSize = 20, textColor = {255, 255, 255, 255}}))
}
} else {
clickData := new_clone((Sidebar_Click_Data) {
clickData := new_clone(Sidebar_Click_Data {
requestedDocumentIndex = i,
selectedDocumentIndex = &data.selectedDocumentIndex,
}, context.temp_allocator)

View file

@ -1,4 +1,4 @@
Odin doesn't directly package SDL, so on Windows, you'll want to copy `SDL3.dll` and `SDL3_ttf.dll` into this directory. On Linux and Mac, you should install SDL3 via your package manager
Odin doesn't directly package SDL, so on Windows, you'll want to copy `SDL3.dll` and `SDL3_ttf.dll` into this directory from odin's `vendor/sdl3` directory located next to the compiler. You can use `odin root` to figure out where your compiler is installed. On Linux and Mac, you should install SDL3 via your package manager
## Running on Windows
In an embdedded termninal, missing dependencies will fail silently on Windows, so if you have any unexplainable crashes, make sure the DLLs are in your current working directory (the path you run the command from). It's easiest to just copy them next to this file, and run from here as well.

View file

@ -10,7 +10,7 @@ import "vendor:sdl3/ttf"
App_State :: struct {
window: ^sdl.Window,
rendererData: Clay_SDL3RendererData,
rendererData: Clay_SDL_Render_Data,
}
state: App_State
@ -22,7 +22,7 @@ errorHandler :: proc "c" (errorData: clay.ErrorData) {
}
load_font :: proc(data: []byte, size: f32, id: u16, fonts: ^[dynamic]^ttf.Font) {
font_stream := sdl.IOFromConstMem(raw_data(ROBOTO), uint(len(ROBOTO)))
font_stream := sdl.IOFromConstMem(raw_data(data), uint(len(data)))
font := ttf.OpenFontIO(font_stream, true, size * 2)
assign_at(fonts, int(id), font)
}
@ -38,7 +38,7 @@ measure_text :: proc "c" (text: clay.StringSlice, config: ^clay.TextElementConfi
sdl.LogError(i32(sdl.LogCategory.ERROR), "Failed to measure text: %s", sdl.GetError())
}
return clay.Dimensions{f32(width), f32(height)}
return {f32(width), f32(height)}
}
// Load at compile time, directly into the binary
ROBOTO := #load("./Roboto-Regular.ttf")
@ -58,9 +58,9 @@ main :: proc() {
state.window = window
state.rendererData.renderer = renderer
state.rendererData.textEngine = ttf.CreateRendererTextEngine(renderer)
state.rendererData.text_engine = ttf.CreateRendererTextEngine(renderer)
minMemorySize := (uint)(clay.MinMemorySize())
minMemorySize := uint(clay.MinMemorySize())
arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(minMemorySize, make([^]u8, minMemorySize))
clay.Initialize(arena, {1280, 720}, {handler = errorHandler})
clay.SetMeasureTextFunction(measure_text, &state.rendererData.fonts)
@ -78,12 +78,10 @@ main :: proc() {
window_width, window_height: i32
for !done {
defer free_all(context.temp_allocator)
scrollDelta: clay.Vector2
for (sdl.PollEvent(&event)) {
if (event.type == .QUIT) {
for sdl.PollEvent(&event) {
if event.type == .QUIT {
done = true
} else if event.type == .MOUSE_WHEEL {
scrollDelta.x = event.wheel.x
@ -99,10 +97,8 @@ main :: proc() {
clay.SetLayoutDimensions({width = f32(window_width), height = f32(window_height)})
mouseX: f32 = 0
mouseY: f32 = 0
mouseState := sdl.GetMouseState(&mouseX, &mouseY)
mousePosition := (clay.Vector2){mouseX, mouseY}
mousePosition : clay.Vector2
mouseState := sdl.GetMouseState(&mousePosition.x, &mousePosition.y)
clay.SetPointerState(mousePosition, .LEFT in mouseState)
clay.UpdateScrollContainers(false, scrollDelta, f32(deltaTime))
@ -112,7 +108,7 @@ main :: proc() {
sdl.SetRenderDrawColor(renderer, 0, 0, 0, 255)
sdl.RenderClear(renderer)
sdl_Clay_RenderClayCommands(&state.rendererData, &commands)
sdl_render_clay_commands(&state.rendererData, &commands)
sdl.RenderPresent(renderer)
}

View file

@ -3,16 +3,17 @@ package video_demo_sdl
import clay "../../clay-odin"
import "core:math"
import "core:math/linalg"
import sdl "vendor:sdl3"
import "vendor:sdl3/ttf"
Clay_SDL3RendererData :: struct {
renderer: ^sdl.Renderer,
textEngine: ^ttf.TextEngine,
fonts: [dynamic]^ttf.Font,
Clay_SDL_Render_Data :: struct {
renderer: ^sdl.Renderer,
text_engine: ^ttf.TextEngine,
fonts: [dynamic]^ttf.Font,
}
// SDL_ttf works in pts, but clay expects pixels.
// SDL_ttf works in pts, but clay expects pixels.
// 0.85 looks correct from what I've seen, but this calculation is probably incorrect.
px_to_pt :: proc "contextless" (pixels: f32) -> f32 {
return pixels * 0.85
@ -24,432 +25,287 @@ px_to_pt :: proc "contextless" (pixels: f32) -> f32 {
NUM_CIRCLE_SEGMENTS :: 16
//all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles.
sdl_Clay_RenderFillRoundedRect :: proc(
rendererData: ^Clay_SDL3RendererData,
rect: sdl.FRect,
cornerRadius: f32,
_color: clay.Color,
) {
color := sdl.FColor(_color / 255)
@(private = "file")
fill_rounded_rect :: proc(rendererData: ^Clay_SDL_Render_Data, rect: sdl.FRect, cornerRadius: f32, _color: clay.Color) {
color := sdl.FColor(_color / 255)
indexCount: i32 = 0
vertexCount: i32 = 0
indexCount: i32 = 0
vertexCount: i32 = 0
minRadius := sdl.min(rect.w, rect.h) / 2
clampedRadius := sdl.min(cornerRadius, minRadius)
minRadius := min(rect.w, rect.h) / 2
clampedRadius := min(cornerRadius, minRadius)
numCircleSegments := sdl.max(NUM_CIRCLE_SEGMENTS, i32(clampedRadius * 0.5))
numCircleSegments := max(NUM_CIRCLE_SEGMENTS, i32(clampedRadius * 0.5))
totalVertices := 4 + (4 * (numCircleSegments * 2)) + 2 * 4
totalIndices := 6 + (4 * (numCircleSegments * 3)) + 6 * 4
totalVertices := 4 + (4 * (numCircleSegments * 2)) + 2 * 4
totalIndices := 6 + (4 * (numCircleSegments * 3)) + 6 * 4
// Maybe instrinsics.alloca these?
vertices := make([]sdl.Vertex, totalVertices, allocator = context.temp_allocator)
indices := make([]i32, totalIndices, allocator = context.temp_allocator)
// Maybe instrinsics.alloca these?
vertices := make([]sdl.Vertex, totalVertices, allocator = context.temp_allocator)
indices := make([]i32, totalIndices, allocator = context.temp_allocator)
//define center rectangle
vertices[vertexCount + 0] = (sdl.Vertex) {
{rect.x + clampedRadius, rect.y + clampedRadius},
color,
{0, 0},
} //0 center TL
vertices[vertexCount + 1] = (sdl.Vertex) {
{rect.x + rect.w - clampedRadius, rect.y + clampedRadius},
color,
{1, 0},
} //1 center TR
vertices[vertexCount + 2] = (sdl.Vertex) {
{rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius},
color,
{1, 1},
} //2 center BR
vertices[vertexCount + 3] = (sdl.Vertex) {
{rect.x + clampedRadius, rect.y + rect.h - clampedRadius},
color,
{0, 1},
} //3 center BL
//define center rectangle
vertices[vertexCount + 0] = {{rect.x + clampedRadius, rect.y + clampedRadius}, color, {0, 0}} //0 center TL
vertices[vertexCount + 1] = {{rect.x + rect.w - clampedRadius, rect.y + clampedRadius}, color, {1, 0}} //1 center TR
vertices[vertexCount + 2] = {{rect.x + rect.w - clampedRadius, rect.y + rect.h - clampedRadius}, color, {1, 1}} //2 center BR
vertices[vertexCount + 3] = {{rect.x + clampedRadius, rect.y + rect.h - clampedRadius}, color, {0, 1}} //3 center BL
vertexCount += 4
vertexCount += 4
indices[indexCount + 0] = 0
indices[indexCount + 1] = 1
indices[indexCount + 2] = 3
indices[indexCount + 3] = 1
indices[indexCount + 4] = 2
indices[indexCount + 5] = 3
indices[indexCount + 0] = 0
indices[indexCount + 1] = 1
indices[indexCount + 2] = 3
indices[indexCount + 3] = 1
indices[indexCount + 4] = 2
indices[indexCount + 5] = 3
indexCount += 6
indexCount += 6
//define rounded corners as triangle fans
step := (f32(math.PI) / 2) / f32(numCircleSegments)
for i in 0 ..< numCircleSegments {
angle1 := f32(i) * step
angle2 := (f32(i) + 1) * step
//define rounded corners as triangle fans
step := (f32(math.PI) / 2) / f32(numCircleSegments)
for i in 0 ..< numCircleSegments {
angle1 := f32(i) * step
angle2 := (f32(i) + 1) * step
for j in i32(0) ..< 4 { // Iterate over four corners
cx, cy, signX, signY: f32
for j in i32(0) ..< 4 { // Iterate over four corners
cx, cy, signX, signY: f32
switch (j) {
case 0:
cx = rect.x + clampedRadius
cy = rect.y + clampedRadius
signX = -1
signY = -1
// Top-left
case 1:
cx = rect.x + rect.w - clampedRadius
cy = rect.y + clampedRadius
signX = 1
signY = -1 // Top-right
case 2:
cx = rect.x + rect.w - clampedRadius
cy = rect.y + rect.h - clampedRadius
signX = 1
signY = 1 // Bottom-right
case 3:
cx = rect.x + clampedRadius
cy = rect.y + rect.h - clampedRadius
signX = -1
signY = 1 // Bottom-left
case:
return
}
switch j {
case 0:
cx = rect.x + clampedRadius
cy = rect.y + clampedRadius
signX = -1
signY = -1
// Top-left
case 1:
cx = rect.x + rect.w - clampedRadius
cy = rect.y + clampedRadius
signX = 1
signY = -1 // Top-right
case 2:
cx = rect.x + rect.w - clampedRadius
cy = rect.y + rect.h - clampedRadius
signX = 1
signY = 1 // Bottom-right
case 3:
cx = rect.x + clampedRadius
cy = rect.y + rect.h - clampedRadius
signX = -1
signY = 1 // Bottom-left
case:
return
}
vertices[vertexCount + 0] = (sdl.Vertex) {
{
cx + sdl.cosf(angle1) * clampedRadius * signX,
cy + sdl.sinf(angle1) * clampedRadius * signY,
},
color,
{0, 0},
}
vertices[vertexCount + 1] = (sdl.Vertex) {
{
cx + sdl.cosf(angle2) * clampedRadius * signX,
cy + sdl.sinf(angle2) * clampedRadius * signY,
},
color,
{0, 0},
}
vertices[vertexCount + 0] = {{cx + math.cos(angle1) * clampedRadius * signX, cy + math.sin(angle1) * clampedRadius * signY}, color, {0, 0}}
vertices[vertexCount + 1] = {{cx + math.cos(angle2) * clampedRadius * signX, cy + math.sin(angle2) * clampedRadius * signY}, color, {0, 0}}
vertexCount += 2
vertexCount += 2
indices[indexCount + 0] = j // Connect to corresponding central rectangle vertex
indices[indexCount + 1] = vertexCount - 2
indices[indexCount + 2] = vertexCount - 1
indexCount += 3
}
}
indices[indexCount + 0] = j // Connect to corresponding central rectangle vertex
indices[indexCount + 1] = vertexCount - 2
indices[indexCount + 2] = vertexCount - 1
indexCount += 3
}
}
//Define edge rectangles
// Top edge
vertices[vertexCount + 0] = (sdl.Vertex){{rect.x + clampedRadius, rect.y}, color, {0, 0}} //TL
vertices[vertexCount + 1] = (sdl.Vertex) {
{rect.x + rect.w - clampedRadius, rect.y},
color,
{1, 0},
} //TR
//Define edge rectangles
// Top edge
vertices[vertexCount + 0] = {{rect.x + clampedRadius, rect.y}, color, {0, 0}} //TL
vertices[vertexCount + 1] = {{rect.x + rect.w - clampedRadius, rect.y}, color, {1, 0}} //TR
vertexCount += 2
vertexCount += 2
indices[indexCount + 0] = 0
indices[indexCount + 1] = vertexCount - 2 //TL
indices[indexCount + 2] = vertexCount - 1 //TR
indices[indexCount + 3] = 1
indices[indexCount + 4] = 0
indices[indexCount + 5] = vertexCount - 1 //TR
indexCount += 6
// Right edge
vertices[vertexCount + 0] = (sdl.Vertex) {
{rect.x + rect.w, rect.y + clampedRadius},
color,
{1, 0},
} //RT
vertices[vertexCount + 1] = (sdl.Vertex) {
{rect.x + rect.w, rect.y + rect.h - clampedRadius},
color,
{1, 1},
} //RB
vertexCount += 2
indices[indexCount + 0] = 0
indices[indexCount + 1] = vertexCount - 2 //TL
indices[indexCount + 2] = vertexCount - 1 //TR
indices[indexCount + 3] = 1
indices[indexCount + 4] = 0
indices[indexCount + 5] = vertexCount - 1 //TR
indexCount += 6
// Right edge
vertices[vertexCount + 0] = {{rect.x + rect.w, rect.y + clampedRadius}, color, {1, 0}} //RT
vertices[vertexCount + 1] = {{rect.x + rect.w, rect.y + rect.h - clampedRadius}, color, {1, 1}} //RB
vertexCount += 2
indices[indexCount + 0] = 1
indices[indexCount + 1] = vertexCount - 2 //RT
indices[indexCount + 2] = vertexCount - 1 //RB
indices[indexCount + 3] = 2
indices[indexCount + 4] = 1
indices[indexCount + 5] = vertexCount - 1 //RB
indexCount += 6
indices[indexCount + 0] = 1
indices[indexCount + 1] = vertexCount - 2 //RT
indices[indexCount + 2] = vertexCount - 1 //RB
indices[indexCount + 3] = 2
indices[indexCount + 4] = 1
indices[indexCount + 5] = vertexCount - 1 //RB
indexCount += 6
// Bottom edge
vertices[vertexCount + 0] = (sdl.Vertex) {
{rect.x + rect.w - clampedRadius, rect.y + rect.h},
color,
{1, 1},
} //BR
vertices[vertexCount + 1] = (sdl.Vertex) {
{rect.x + clampedRadius, rect.y + rect.h},
color,
{0, 1},
} //BL
vertexCount += 2
// Bottom edge
vertices[vertexCount + 0] = {{rect.x + rect.w - clampedRadius, rect.y + rect.h}, color, {1, 1}} //BR
vertices[vertexCount + 1] = {{rect.x + clampedRadius, rect.y + rect.h}, color, {0, 1}} //BL
vertexCount += 2
indices[indexCount + 0] = 2
indices[indexCount + 1] = vertexCount - 2 //BR
indices[indexCount + 2] = vertexCount - 1 //BL
indices[indexCount + 3] = 3
indices[indexCount + 4] = 2
indices[indexCount + 5] = vertexCount - 1 //BL
indexCount += 6
indices[indexCount + 0] = 2
indices[indexCount + 1] = vertexCount - 2 //BR
indices[indexCount + 2] = vertexCount - 1 //BL
indices[indexCount + 3] = 3
indices[indexCount + 4] = 2
indices[indexCount + 5] = vertexCount - 1 //BL
indexCount += 6
// Left edge
vertices[vertexCount + 0] = (sdl.Vertex) {
{rect.x, rect.y + rect.h - clampedRadius},
color,
{0, 1},
} //LB
vertices[vertexCount + 1] = (sdl.Vertex){{rect.x, rect.y + clampedRadius}, color, {0, 0}} //LT
vertexCount += 2
// Left edge
vertices[vertexCount + 0] = {{rect.x, rect.y + rect.h - clampedRadius}, color, {0, 1}} //LB
vertices[vertexCount + 1] = {{rect.x, rect.y + clampedRadius}, color, {0, 0}} //LT
vertexCount += 2
indices[indexCount + 0] = 3
indices[indexCount + 1] = vertexCount - 2 //LB
indices[indexCount + 2] = vertexCount - 1 //LT
indices[indexCount + 3] = 0
indices[indexCount + 4] = 3
indices[indexCount + 5] = vertexCount - 1 //LT
indexCount += 6
indices[indexCount + 0] = 3
indices[indexCount + 1] = vertexCount - 2 //LB
indices[indexCount + 2] = vertexCount - 1 //LT
indices[indexCount + 3] = 0
indices[indexCount + 4] = 3
indices[indexCount + 5] = vertexCount - 1 //LT
indexCount += 6
// Render everything
sdl.RenderGeometry(
rendererData.renderer,
nil,
raw_data(vertices),
vertexCount,
raw_data(indices),
indexCount,
)
// Render everything
sdl.RenderGeometry(rendererData.renderer, nil, raw_data(vertices), vertexCount, raw_data(indices), indexCount)
}
sdl_Clay_RenderArc :: proc(
rendererData: ^Clay_SDL3RendererData,
center: sdl.FPoint,
radius: f32,
startAngle: f32,
endAngle: f32,
thickness: f32,
color: clay.Color,
) {
sdl.SetRenderDrawColor(
rendererData.renderer,
u8(color.r),
u8(color.g),
u8(color.b),
u8(color.a),
)
@(private = "file")
render_arc :: proc(rendererData: ^Clay_SDL_Render_Data, center: sdl.FPoint, radius: f32, startAngle: f32, endAngle: f32, thickness: f32, color: clay.Color) {
sdl.SetRenderDrawColor(rendererData.renderer, clay_to_sdl_color(color))
radStart := startAngle * (math.PI / 180.0)
radEnd := endAngle * (math.PI / 180.0)
radStart := math.to_radians(startAngle)
radEnd := math.to_radians(endAngle)
numCircleSegments := sdl.max(NUM_CIRCLE_SEGMENTS, i32(radius * 1.5)) //increase circle segments for larger circles, 1.5 is arbitrary.
numCircleSegments := max(NUM_CIRCLE_SEGMENTS, i32(radius * 1.5)) //increase circle segments for larger circles, 1.5 is arbitrary.
angleStep := (radEnd - radStart) / f32(numCircleSegments)
thicknessStep: f32 = 0.4 //arbitrary value to avoid overlapping lines. Changing THICKNESS_STEP or numCircleSegments might cause artifacts.
angleStep := (radEnd - radStart) / f32(numCircleSegments)
thicknessStep: f32 = 0.4 //arbitrary value to avoid overlapping lines. Changing THICKNESS_STEP or numCircleSegments might cause artifacts.
for t := thicknessStep; t < thickness - thicknessStep; t += thicknessStep {
points := make([]sdl.FPoint, numCircleSegments + 1, allocator = context.temp_allocator)
clampedRadius := sdl.max(radius - t, 1)
for t := thicknessStep; t < thickness - thicknessStep; t += thicknessStep {
points := make([]sdl.FPoint, numCircleSegments + 1, allocator = context.temp_allocator)
clampedRadius := max(radius - t, 1)
for i in 0 ..= numCircleSegments {
angle := radStart + f32(i) * angleStep
points[i] = (sdl.FPoint) {
sdl.roundf(center.x + sdl.cosf(angle) * clampedRadius),
sdl.roundf(center.y + sdl.sinf(angle) * clampedRadius),
}
}
sdl.RenderLines(rendererData.renderer, raw_data(points), numCircleSegments + 1)
}
for i in 0 ..= numCircleSegments {
angle := radStart + f32(i) * angleStep
points[i] = sdl.FPoint{math.round(center.x + math.cos(angle) * clampedRadius), math.round(center.y + math.sin(angle) * clampedRadius)}
}
sdl.RenderLines(rendererData.renderer, raw_data(points), numCircleSegments + 1)
}
}
currentClippingRectangle: sdl.Rect
@(private = "file")
current_clipping_rect: sdl.Rect
sdl_Clay_RenderClayCommands :: proc(
rendererData: ^Clay_SDL3RendererData,
rcommands: ^clay.ClayArray(clay.RenderCommand),
) {
for i in 0 ..< rcommands.length {
rcmd := clay.RenderCommandArray_Get(rcommands, i)
bounding_box := rcmd.boundingBox
rect := sdl.FRect{bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height}
#partial switch (rcmd.commandType) {
case .Rectangle:
config := &rcmd.renderData.rectangle
sdl.SetRenderDrawBlendMode(rendererData.renderer, sdl.BLENDMODE_BLEND)
sdl.SetRenderDrawColor(
rendererData.renderer,
u8(config.backgroundColor.r),
u8(config.backgroundColor.g),
u8(config.backgroundColor.b),
u8(config.backgroundColor.a),
)
if (config.cornerRadius.topLeft > 0) {
sdl_Clay_RenderFillRoundedRect(
rendererData,
rect,
config.cornerRadius.topLeft,
config.backgroundColor,
)
} else {
sdl.RenderFillRect(rendererData.renderer, &rect)
}
case .Text:
config := &rcmd.renderData.text
font := rendererData.fonts[config.fontId]
ttf.SetFontSize(font, px_to_pt(f32(config.fontSize)))
text := ttf.CreateText(
rendererData.textEngine,
font,
cstring(config.stringContents.chars),
uint(config.stringContents.length),
)
ttf.SetTextColor(
text,
u8(config.textColor.r),
u8(config.textColor.g),
u8(config.textColor.b),
u8(config.textColor.a),
)
ttf.DrawRendererText(text, rect.x, rect.y)
ttf.DestroyText(text)
case .Border:
config := &rcmd.renderData.border
minRadius := sdl.min(rect.w, rect.h) / 2
clampedRadii := clay.CornerRadius {
topLeft = sdl.min(config.cornerRadius.topLeft, minRadius),
topRight = sdl.min(config.cornerRadius.topRight, minRadius),
bottomLeft = sdl.min(config.cornerRadius.bottomLeft, minRadius),
bottomRight = sdl.min(config.cornerRadius.bottomRight, minRadius),
}
//edges
sdl.SetRenderDrawColor(
rendererData.renderer,
u8(config.color.r),
u8(config.color.g),
u8(config.color.b),
u8(config.color.a),
)
if (config.width.left > 0) {
starting_y := rect.y + clampedRadii.topLeft
length := rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft
line := sdl.FRect{rect.x - 1, starting_y, f32(config.width.left), length}
sdl.RenderFillRect(rendererData.renderer, &line)
}
if (config.width.right > 0) {
starting_x := rect.x + rect.w - f32(config.width.right) + 1
starting_y := rect.y + clampedRadii.topRight
length := rect.h - clampedRadii.topRight - clampedRadii.bottomRight
line := sdl.FRect{starting_x, starting_y, f32(config.width.right), length}
sdl.RenderFillRect(rendererData.renderer, &line)
}
if (config.width.top > 0) {
starting_x := rect.x + clampedRadii.topLeft
length := rect.w - clampedRadii.topLeft - clampedRadii.topRight
line := sdl.FRect{starting_x, rect.y - 1, length, f32(config.width.top)}
sdl.RenderFillRect(rendererData.renderer, &line)
}
if (config.width.bottom > 0) {
starting_x := rect.x + clampedRadii.bottomLeft
starting_y := rect.y + rect.h - f32(config.width.bottom) + 1
length := rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight
line := sdl.FRect{starting_x, starting_y, length, f32(config.width.bottom)}
sdl.SetRenderDrawColor(
rendererData.renderer,
u8(config.color.r),
u8(config.color.g),
u8(config.color.b),
u8(config.color.a),
)
sdl.RenderFillRect(rendererData.renderer, &line)
}
//corners
if (config.cornerRadius.topLeft > 0) {
centerX := rect.x + clampedRadii.topLeft - 1
centerY := rect.y + clampedRadii.topLeft - 1
sdl_Clay_RenderArc(
rendererData,
(sdl.FPoint){centerX, centerY},
clampedRadii.topLeft,
180.0,
270.0,
f32(config.width.top),
config.color,
)
}
if (config.cornerRadius.topRight > 0) {
centerX := rect.x + rect.w - clampedRadii.topRight
centerY := rect.y + clampedRadii.topRight - 1
sdl_Clay_RenderArc(
rendererData,
(sdl.FPoint){centerX, centerY},
clampedRadii.topRight,
270.0,
360.0,
f32(config.width.top),
config.color,
)
}
if (config.cornerRadius.bottomLeft > 0) {
centerX := rect.x + clampedRadii.bottomLeft - 1
centerY := rect.y + rect.h - clampedRadii.bottomLeft
sdl_Clay_RenderArc(
rendererData,
(sdl.FPoint){centerX, centerY},
clampedRadii.bottomLeft,
90.0,
180.0,
f32(config.width.bottom),
config.color,
)
}
if (config.cornerRadius.bottomRight > 0) {
centerX := rect.x + rect.w - clampedRadii.bottomRight
centerY := rect.y + rect.h - clampedRadii.bottomRight
sdl_Clay_RenderArc(
rendererData,
(sdl.FPoint){centerX, centerY},
clampedRadii.bottomRight,
0.0,
90.0,
f32(config.width.bottom),
config.color,
)
}
case .ScissorStart:
boundingBox := rcmd.boundingBox
currentClippingRectangle = (sdl.Rect) {
x = i32(boundingBox.x),
y = i32(boundingBox.y),
w = i32(boundingBox.width),
h = i32(boundingBox.height),
}
sdl.SetRenderClipRect(rendererData.renderer, &currentClippingRectangle)
case .ScissorEnd:
sdl.SetRenderClipRect(rendererData.renderer, nil)
case .Image:
texture := (^sdl.Texture)(rcmd.renderData.image.imageData)
dest := sdl.FRect{rect.x, rect.y, rect.w, rect.h}
sdl.RenderTexture(rendererData.renderer, texture, nil, &dest)
case:
sdl.Log("Unknown render command type: %d", rcmd.commandType)
}
}
clay_to_sdl_color :: proc(color: clay.Color) -> (r, g, b, a: u8) {
return expand_values(linalg.array_cast(color, u8))
}
sdl_render_clay_commands :: proc(renderer_data: ^Clay_SDL_Render_Data, commands: ^clay.ClayArray(clay.RenderCommand)) {
for i in 0 ..< commands.length {
cmd := clay.RenderCommandArray_Get(commands, i)
bounding_box := cmd.boundingBox
rect := sdl.FRect{bounding_box.x, bounding_box.y, bounding_box.width, bounding_box.height}
#partial switch cmd.commandType {
case .Rectangle:
config := &cmd.renderData.rectangle
sdl.SetRenderDrawBlendMode(renderer_data.renderer, sdl.BLENDMODE_BLEND)
sdl.SetRenderDrawColor(renderer_data.renderer, clay_to_sdl_color(config.backgroundColor))
if config.cornerRadius.topLeft > 0 {
fill_rounded_rect(renderer_data, rect, config.cornerRadius.topLeft, config.backgroundColor)
} else {
sdl.RenderFillRect(renderer_data.renderer, &rect)
}
case .Text:
config := &cmd.renderData.text
font := renderer_data.fonts[config.fontId]
ttf.SetFontSize(font, px_to_pt(f32(config.fontSize)))
text := ttf.CreateText(renderer_data.text_engine, font, cstring(config.stringContents.chars), uint(config.stringContents.length))
ttf.SetTextColor(text, clay_to_sdl_color(config.textColor))
ttf.DrawRendererText(text, rect.x, rect.y)
ttf.DestroyText(text)
case .Border:
config := &cmd.renderData.border
minRadius := min(rect.w, rect.h) / 2
clampedRadii := clay.CornerRadius {
topLeft = min(config.cornerRadius.topLeft, minRadius),
topRight = min(config.cornerRadius.topRight, minRadius),
bottomLeft = min(config.cornerRadius.bottomLeft, minRadius),
bottomRight = min(config.cornerRadius.bottomRight, minRadius),
}
//edges
sdl.SetRenderDrawColor(renderer_data.renderer, clay_to_sdl_color(config.color))
if config.width.left > 0 {
starting_y := rect.y + clampedRadii.topLeft
length := rect.h - clampedRadii.topLeft - clampedRadii.bottomLeft
line := sdl.FRect{rect.x - 1, starting_y, f32(config.width.left), length}
sdl.RenderFillRect(renderer_data.renderer, &line)
}
if config.width.right > 0 {
starting_x := rect.x + rect.w - f32(config.width.right) + 1
starting_y := rect.y + clampedRadii.topRight
length := rect.h - clampedRadii.topRight - clampedRadii.bottomRight
line := sdl.FRect{starting_x, starting_y, f32(config.width.right), length}
sdl.RenderFillRect(renderer_data.renderer, &line)
}
if config.width.top > 0 {
starting_x := rect.x + clampedRadii.topLeft
length := rect.w - clampedRadii.topLeft - clampedRadii.topRight
line := sdl.FRect{starting_x, rect.y - 1, length, f32(config.width.top)}
sdl.RenderFillRect(renderer_data.renderer, &line)
}
if config.width.bottom > 0 {
starting_x := rect.x + clampedRadii.bottomLeft
starting_y := rect.y + rect.h - f32(config.width.bottom) + 1
length := rect.w - clampedRadii.bottomLeft - clampedRadii.bottomRight
line := sdl.FRect{starting_x, starting_y, length, f32(config.width.bottom)}
sdl.SetRenderDrawColor(renderer_data.renderer, clay_to_sdl_color(config.color))
sdl.RenderFillRect(renderer_data.renderer, &line)
}
//corners
if config.cornerRadius.topLeft > 0 {
centerX := rect.x + clampedRadii.topLeft - 1
centerY := rect.y + clampedRadii.topLeft - 1
render_arc(renderer_data, {centerX, centerY}, clampedRadii.topLeft, 180, 270, f32(config.width.top), config.color)
}
if config.cornerRadius.topRight > 0 {
centerX := rect.x + rect.w - clampedRadii.topRight
centerY := rect.y + clampedRadii.topRight - 1
render_arc(renderer_data, {centerX, centerY}, clampedRadii.topRight, 270, 360, f32(config.width.top), config.color)
}
if config.cornerRadius.bottomLeft > 0 {
centerX := rect.x + clampedRadii.bottomLeft - 1
centerY := rect.y + rect.h - clampedRadii.bottomLeft
render_arc(renderer_data, {centerX, centerY}, clampedRadii.bottomLeft, 90, 180, f32(config.width.bottom), config.color)
}
if config.cornerRadius.bottomRight > 0 {
centerX := rect.x + rect.w - clampedRadii.bottomRight
centerY := rect.y + rect.h - clampedRadii.bottomRight
render_arc(renderer_data, {centerX, centerY}, clampedRadii.bottomRight, 0, 90, f32(config.width.bottom), config.color)
}
case .ScissorStart:
boundingBox := cmd.boundingBox
current_clipping_rect = sdl.Rect {
x = i32(boundingBox.x),
y = i32(boundingBox.y),
w = i32(boundingBox.width),
h = i32(boundingBox.height),
}
sdl.SetRenderClipRect(renderer_data.renderer, &current_clipping_rect)
case .ScissorEnd:
sdl.SetRenderClipRect(renderer_data.renderer, nil)
case .Image:
texture := (^sdl.Texture)(cmd.renderData.image.imageData)
dest := sdl.FRect{rect.x, rect.y, rect.w, rect.h}
sdl.RenderTexture(renderer_data.renderer, texture, nil, &dest)
case:
sdl.Log("Unknown render command type: %d", cmd.commandType)
}
}
}