diff --git a/CMakeLists.txt b/CMakeLists.txt index 570b314..c12deff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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_SOKOL_EXAMPLES "Build Sokol examples" OFF) message(STATUS "CLAY_INCLUDE_DEMOS: ${CLAY_INCLUDE_DEMOS}") @@ -36,6 +37,10 @@ endif () if(NOT MSVC AND (CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SDL3_EXAMPLES)) add_subdirectory("examples/SDL3-simple-demo") endif() +if(CLAY_INCLUDE_ALL_EXAMPLES OR CLAY_INCLUDE_SOKOL_EXAMPLES) + add_subdirectory("examples/sokol-video-demo") + add_subdirectory("examples/sokol-corner-radius") +endif() # add_subdirectory("examples/cairo-pdf-rendering") Some issue with github actions populating cairo, disable for now diff --git a/examples/sokol-corner-radius/CMakeLists.txt b/examples/sokol-corner-radius/CMakeLists.txt new file mode 100644 index 0000000..92e1210 --- /dev/null +++ b/examples/sokol-corner-radius/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.27) +project(sokol_corner_radius C) + +add_executable(sokol_corner_radius main.c) +target_link_libraries(sokol_corner_radius PUBLIC sokol) diff --git a/examples/sokol-corner-radius/main.c b/examples/sokol-corner-radius/main.c new file mode 100644 index 0000000..6fa3b2b --- /dev/null +++ b/examples/sokol-corner-radius/main.c @@ -0,0 +1,110 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "sokol_glue.h" +#include "sokol_log.h" + +#define CLAY_IMPLEMENTATION +#include "../../clay.h" + +#include "util/sokol_gl.h" +#include "fontstash.h" +#include "util/sokol_fontstash.h" +#define SOKOL_CLAY_IMPL +#include "../../renderers/sokol/sokol_clay.h" + +static void init() { + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + .logger.func = slog_func, + }); + sgl_setup(&(sgl_desc_t){ + .logger.func = slog_func, + }); + sclay_setup(); + uint64_t totalMemorySize = Clay_MinMemorySize(); + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize)); + Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){}); + Clay_SetMeasureTextFunction(sclay_measure_text, NULL); +} + +Clay_RenderCommandArray CornerRadiusTest(){ + Clay_BeginLayout(); + Clay_Sizing layoutExpand = { + .width = CLAY_SIZING_GROW(0), + .height = CLAY_SIZING_GROW(0) + }; + CLAY({ .id = CLAY_ID("OuterContainer"), + .backgroundColor = {43, 41, 51, 255}, + .layout = { + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = layoutExpand, + .padding = {0, 0, 20, 20}, + .childGap = 20 + } + }) { + for(int i = 0; i < 6; ++i){ + CLAY({ .id = CLAY_IDI("Row", i), + .layout = { + .layoutDirection = CLAY_LEFT_TO_RIGHT, + .sizing = layoutExpand, + .padding = {20, 20, 0, 0}, + .childGap = 20 + } + }) { + for(int j = 0; j < 6; ++j){ + CLAY({ .id = CLAY_IDI("Tile", i*6+j), + .backgroundColor = {120, 140, 255, 128}, + .cornerRadius = {(i%3)*15, (j%3)*15, (i/2)*15, (j/2)*15}, + .border = { + .color = {120, 140, 255, 255}, + .width = {3, 9, 6, 12, 0}, + }, + .layout = { .sizing = layoutExpand } + }); + } + } + } + } + return Clay_EndLayout(); +} + +static void frame() { + sclay_new_frame(); + Clay_RenderCommandArray renderCommands = CornerRadiusTest(); + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() }); + sgl_matrix_mode_modelview(); + sgl_load_identity(); + sclay_render(renderCommands, NULL); + sgl_draw(); + sg_end_pass(); + sg_commit(); +} + +static void event(const sapp_event *ev) { + if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){ + Clay_SetDebugModeEnabled(true); + } else { + sclay_handle_event(ev); + } +} + +static void cleanup() { + sclay_shutdown(); + sgl_shutdown(); + sg_shutdown(); +} + +sapp_desc sokol_main(int argc, char **argv) { + return (sapp_desc){ + .init_cb = init, + .frame_cb = frame, + .event_cb = event, + .cleanup_cb = cleanup, + .window_title = "Clay - Corner Radius Test", + .width = 800, + .height = 600, + .icon.sokol_default = true, + .logger.func = slog_func, + }; +} diff --git a/examples/sokol-video-demo/CMakeLists.txt b/examples/sokol-video-demo/CMakeLists.txt new file mode 100644 index 0000000..533a865 --- /dev/null +++ b/examples/sokol-video-demo/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.27) +project(sokol_video_demo C) + +include(FetchContent) +set(FETCHCONTENT_QUIET FALSE) + +FetchContent_Declare( + fontstash + GIT_REPOSITORY "https://github.com/memononen/fontstash.git" + GIT_TAG "b5ddc9741061343740d85d636d782ed3e07cf7be" + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(fontstash) + +FetchContent_Declare( + sokol + GIT_REPOSITORY "https://github.com/floooh/sokol.git" + GIT_TAG "da9de496f938b7575eff7f01ab774d77469bd390" + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(sokol) +set(sokol_HEADERS + ${sokol_SOURCE_DIR}/sokol_app.h + ${sokol_SOURCE_DIR}/sokol_gfx.h + ${sokol_SOURCE_DIR}/sokol_glue.h + ${sokol_SOURCE_DIR}/sokol_log.h + ${sokol_SOURCE_DIR}/util/sokol_gl.h + ${fontstash_SOURCE_DIR}/src/fontstash.h + ${sokol_SOURCE_DIR}/util/sokol_fontstash.h) +if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + add_library(sokol STATIC sokol.c ${sokol_HEADERS}) + target_compile_options(sokol PRIVATE -x objective-c) + target_link_libraries(sokol PUBLIC + "-framework QuartzCore" + "-framework Cocoa" + "-framework MetalKit" + "-framework Metal") +else() + add_library(sokol STATIC sokol.c ${sokol_HEADERS}) + if (CMAKE_SYSTEM_NAME STREQUAL Linux) + target_link_libraries(sokol INTERFACE X11 Xi Xcursor GL dl m) + target_link_libraries(sokol PUBLIC Threads::Threads) + endif() +endif() +target_include_directories(sokol INTERFACE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src + PRIVATE ${sokol_SOURCE_DIR} ${fontstash_SOURCE_DIR}/src) + +add_executable(sokol_video_demo main.c) +target_link_libraries(sokol_video_demo PUBLIC sokol) +add_custom_command( + TARGET sokol_video_demo POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/resources + ${CMAKE_CURRENT_BINARY_DIR}/resources) diff --git a/examples/sokol-video-demo/main.c b/examples/sokol-video-demo/main.c new file mode 100644 index 0000000..09657cb --- /dev/null +++ b/examples/sokol-video-demo/main.c @@ -0,0 +1,77 @@ +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "sokol_glue.h" +#include "sokol_log.h" + +#define CLAY_IMPLEMENTATION +#include "../../clay.h" + +#include "util/sokol_gl.h" +#include "fontstash.h" +#include "util/sokol_fontstash.h" +#define SOKOL_CLAY_IMPL +#include "../../renderers/sokol/sokol_clay.h" + +#include "../shared-layouts/clay-video-demo.c" + +static ClayVideoDemo_Data demoData; +static sclay_font_t fonts[1]; + +static void init() { + sg_setup(&(sg_desc){ + .environment = sglue_environment(), + .logger.func = slog_func, + }); + sgl_setup(&(sgl_desc_t){ + .logger.func = slog_func, + }); + sclay_setup(); + uint64_t totalMemorySize = Clay_MinMemorySize(); + Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize)); + Clay_Initialize(clayMemory, (Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, (Clay_ErrorHandler){}); + fonts[FONT_ID_BODY_16] = sclay_add_font("resources/Roboto-Regular.ttf"); + Clay_SetMeasureTextFunction(sclay_measure_text, &fonts); + demoData = ClayVideoDemo_Initialize(); +} + +static void frame() { + sclay_new_frame(); + Clay_RenderCommandArray renderCommands = ClayVideoDemo_CreateLayout(&demoData); + + sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain() }); + sgl_matrix_mode_modelview(); + sgl_load_identity(); + sclay_render(renderCommands, fonts); + sgl_draw(); + sg_end_pass(); + sg_commit(); +} + +static void event(const sapp_event *ev) { + if(ev->type == SAPP_EVENTTYPE_KEY_DOWN && ev->key_code == SAPP_KEYCODE_D){ + Clay_SetDebugModeEnabled(true); + } else { + sclay_handle_event(ev); + } +} + +static void cleanup() { + sclay_shutdown(); + sgl_shutdown(); + sg_shutdown(); +} + +sapp_desc sokol_main(int argc, char **argv) { + return (sapp_desc){ + .init_cb = init, + .frame_cb = frame, + .event_cb = event, + .cleanup_cb = cleanup, + .window_title = "Clay - Sokol Renderer Example", + .width = 800, + .height = 600, + .high_dpi = true, + .icon.sokol_default = true, + .logger.func = slog_func, + }; +} diff --git a/examples/sokol-video-demo/resources/Roboto-Regular.ttf b/examples/sokol-video-demo/resources/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/examples/sokol-video-demo/resources/Roboto-Regular.ttf differ diff --git a/examples/sokol-video-demo/sokol.c b/examples/sokol-video-demo/sokol.c new file mode 100644 index 0000000..ed43e9c --- /dev/null +++ b/examples/sokol-video-demo/sokol.c @@ -0,0 +1,24 @@ +#define SOKOL_IMPL +#if defined(_WIN32) +#define SOKOL_D3D11 +#elif defined(__EMSCRIPTEN__) +#define SOKOL_GLES2 +#elif defined(__APPLE__) +#define SOKOL_METAL +#else +#define SOKOL_GLCORE33 +#endif +#include "sokol_app.h" +#include "sokol_gfx.h" +#include "sokol_time.h" +#include "sokol_fetch.h" +#include "sokol_glue.h" +#include "sokol_log.h" + +#include "util/sokol_gl.h" +#include // fontstash requires this +#include // fontstash requires this +#define FONTSTASH_IMPLEMENTATION +#include "fontstash.h" +#define SOKOL_FONTSTASH_IMPL +#include "util/sokol_fontstash.h" diff --git a/renderers/sokol/sokol_clay.h b/renderers/sokol/sokol_clay.h new file mode 100644 index 0000000..0436a50 --- /dev/null +++ b/renderers/sokol/sokol_clay.h @@ -0,0 +1,450 @@ +#ifndef SOKOL_CLAY_INCLUDED +#define SOKOL_CLAY_INCLUDED (1) +/* + sokol_clay.h -- drop-in Clay renderer for sokol_gfx.h + + Do this: + #define SOKOL_CLAY_IMPL + + before you include this file in *one* C file to create the + implementation. + + Optionally provide the following configuration define both before including the + the declaration and implementation: + + SOKOL_CLAY_NO_SOKOL_APP - don't depend on sokol_app.h (see below for details) + + Include the following headers before sokol_clay.h (both before including + the declaration and implementation): + + sokol_gl.h + sokol_fontstash.h + sokol_app.h (except SOKOL_CLAY_NO_SOKOL_APP) + clay.h + + FEATURE OVERVIEW: + ================= + sokol_clay.h implements the rendering and event-handling code for Clay + (https://github.com/nicbarker/clay) on top of sokol_gl.h and (optionally) + sokol_app.h. + + Since sokol_fontstash.h already depends on sokol_gl.h, the rendering is + implemented using sokol_gl calls. (TODO: make fontstash optional?) + + The sokol_app.h dependency is optional and used for input event handling. + If you only use sokol_gfx.h but not sokol_app.h in your application, + define SOKOL_CLAY_NO_SOKOL_APP before including the implementation + of sokol_clay.h, this will remove any dependency to sokol_app.h, but + you must call sclay_set_layout_dimensions and handle input yourself. + + sokol_clay.h is not thread-safe, all calls must be made from the + same thread where sokol_gfx.h is running. + + HOWTO: + ====== + + --- To initialize sokol-clay, call sclay_setup(). This can be done + before or after Clay_Initialize. + + --- Create an array of sclay_font_t and fill it by calling one of: + + sclay_font_t sclay_add_font(const char *filename); + sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen); + + The fontId value in Clay corresponds to indices in this array. After calling + Clay_Initialize but before calling any layout code, do this: + + Clay_SetMeasureTextFunction(sclay_measure_text, &fonts); + + where `fonts` is the abovementioned array. + + --- At the start of a frame, call sclay_new_frame() if you're using sokol_app.h. + If you're not using sokol_app.h, call: + + void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale); + + at the start of the frame (or just when the window is resized.) + + Either way, do some layout, then at the end of the frame call sclay_render: + + sg_begin_pass(...) + // other rendering... + sclay_render(renderCommands, &fonts); + // other rendering... + sgl_draw(); + sg_end_pass(); + sg_commit(); + + One caveat: sclay_render assumes the default gl view matrix, and handles scaling + automatically. If you've adjusted the view matrix, remember to first call: + + sgl_matrix_mode_modelview(); + sgl_load_identity(); + + before calling sclay_render. + + --- if you're using sokol_app.h, from inside the sokol_app.h event callback, + call: + + void sclay_handle_event(const sapp_event* ev); + + Unfortunately Clay does not currently provide feedback on whether a mouse + click was handled or not. + + --- finally, on application shutdown, call + + sclay_shutdown() + */ +#if !defined(SOKOL_CLAY_NO_SOKOL_APP) && !defined(SOKOL_APP_INCLUDED) +#error "Please include sokol_app.h before sokol_clay.h (or define SOKOL_CLAY_NO_SOKOL_APP)" +#endif + +typedef int sclay_font_t; + +void sclay_setup(); +void sclay_shutdown(); + +sclay_font_t sclay_add_font(const char *filename); +sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen); +Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData); + +#ifndef SOKOL_CLAY_NO_SOKOL_APP +void sclay_new_frame(); +void sclay_handle_event(const sapp_event *ev); +#endif /* SOKOL_CLAY_NO_SOKOL_APP */ + +/* Use this if you don't call sclay_new_frame. `size` is the "virtual" size which + * your layout is relative to (ie. the actual framebuffer size divided by dpi_scale.) + * Set dpi_scale to 1 if you're not using high-dpi support. */ +void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale); + +void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts); + +#endif /* SOKOL_CLAY_INCLUDED */ + +#ifdef SOKOL_CLAY_IMPL +#define SOKOL_CLAY_IMPL_INCLUDED (1) +#ifndef SOKOL_GL_INCLUDED +#error "Please include sokol_gl.h before sokol_clay.h" +#endif +#ifndef SOKOL_FONTSTASH_INCLUDED +#error "Please include sokol_fontstash.h before sokol_clay.h" +#endif +#ifndef CLAY_HEADER +#error "Please include clay.h before sokol_clay.h" +#endif + +typedef struct { + sgl_pipeline pip; +#ifndef SOKOL_CLAY_NO_SOKOL_APP + Clay_Vector2 mouse_pos, scroll; + bool mouse_down; +#endif + Clay_Dimensions size; + float dpi_scale; + FONScontext *fonts; +} _sclay_state_t; +static _sclay_state_t _sclay; + +void sclay_setup() { + _sclay.pip = sgl_make_pipeline(&(sg_pipeline_desc){ + .colors[0] = { + .blend = { + .enabled = true, + .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA, + .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + }, + } + }); +#ifndef SOKOL_CLAY_NO_SOKOL_APP + _sclay.mouse_pos = (Clay_Vector2){0, 0}; + _sclay.scroll = (Clay_Vector2){0, 0}; + _sclay.mouse_down = false; +#endif + _sclay.size = (Clay_Dimensions){1, 1}; + _sclay.dpi_scale = 1; + _sclay.fonts = sfons_create(&(sfons_desc_t){ 0 }); + //TODO clay error handler? +} + +void sclay_shutdown() { + sgl_destroy_pipeline(_sclay.pip); + sfons_destroy(_sclay.fonts); +} + +#ifndef SOKOL_CLAY_NO_SOKOL_APP +void sclay_handle_event(const sapp_event* ev) { + switch(ev->type){ + case SAPP_EVENTTYPE_MOUSE_MOVE: + _sclay.mouse_pos.x = ev->mouse_x / _sclay.dpi_scale; + _sclay.mouse_pos.y = ev->mouse_y / _sclay.dpi_scale; + break; + case SAPP_EVENTTYPE_MOUSE_DOWN: + _sclay.mouse_down = true; + break; + case SAPP_EVENTTYPE_MOUSE_UP: + _sclay.mouse_down = false; + break; + case SAPP_EVENTTYPE_MOUSE_SCROLL: + _sclay.scroll.x += ev->scroll_x; + _sclay.scroll.y += ev->scroll_y; + break; + default: break; + } +} + +void sclay_new_frame() { + sclay_set_layout_dimensions((Clay_Dimensions){ (float)sapp_width(), (float)sapp_height() }, + sapp_dpi_scale()); + Clay_SetPointerState(_sclay.mouse_pos, _sclay.mouse_down); + Clay_UpdateScrollContainers(true, _sclay.scroll, sapp_frame_duration()); + _sclay.scroll = (Clay_Vector2){0, 0}; +} +#endif /* SOKOL_CLAY_NO_SOKOL_APP */ + +void sclay_set_layout_dimensions(Clay_Dimensions size, float dpi_scale) { + size.width /= dpi_scale; + size.height /= dpi_scale; + _sclay.size = size; + if(_sclay.dpi_scale != dpi_scale){ + _sclay.dpi_scale = dpi_scale; + Clay_ResetMeasureTextCache(); + } + Clay_SetLayoutDimensions(size); +} + +sclay_font_t sclay_add_font(const char *filename) { + //TODO log something if we get FONS_INVALID + return fonsAddFont(_sclay.fonts, "", filename); +} + +sclay_font_t sclay_add_font_mem(unsigned char *data, int dataLen) { + //TODO log something if we get FONS_INVALID + return fonsAddFontMem(_sclay.fonts, "", data, dataLen, false); +} + +Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) { + sclay_font_t *fonts = (sclay_font_t *)userData; + if(!fonts) return (Clay_Dimensions){ 0 }; + fonsSetFont(_sclay.fonts, fonts[config->fontId]); + fonsSetSize(_sclay.fonts, config->fontSize); + fonsSetSpacing(_sclay.fonts, config->letterSpacing); + float ascent, descent, lineh; + fonsVertMetrics(_sclay.fonts, &ascent, &descent, &lineh); + return (Clay_Dimensions) { + .width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL), + .height = ascent - descent + }; +} + +static void _draw_rect(float x, float y, float w, float h){ + sgl_v2f(x, y); + sgl_v2f(x, y); + sgl_v2f(x+w, y); + sgl_v2f(x, y+h); + sgl_v2f(x+w, y+h); + sgl_v2f(x+w, y+h); +} + +static float _SIN[16] = { + 0.000000f, 0.104528f, 0.207912f, 0.309017f, + 0.406737f, 0.500000f, 0.587785f, 0.669131f, + 0.743145f, 0.809017f, 0.866025f, 0.913545f, + 0.951057f, 0.978148f, 0.994522f, 1.000000f, +}; + +/* rx,ry = radius */ +static void _draw_corner(float x, float y, float rx, float ry){ + x -= rx; + y -= ry; + sgl_v2f(x, y); + for(int i = 0; i < 16; ++i){ + sgl_v2f(x, y); + sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i])); + } + sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15])); +} + +/* rx,ry = radius ix,iy = inner radius */ +static void _draw_corner_border(float x, float y, float rx, float ry, float ix, float iy){ + x -= rx; + y -= ry; + sgl_v2f(x+(ix*_SIN[15]), y+(iy*_SIN[0])); + for(int i = 0; i < 16; ++i){ + sgl_v2f(x+(ix*_SIN[15-i]), y+(iy*_SIN[i])); + sgl_v2f(x+(rx*_SIN[15-i]), y+(ry*_SIN[i])); + } + sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15])); +} + +void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) { + sgl_matrix_mode_modelview(); + sgl_translate(-1.0f, 1.0f, 0.0f); + sgl_scale(2.0f/_sclay.size.width, -2.0f/_sclay.size.height, 1.0f); + sgl_disable_texture(); + sgl_push_pipeline(); + sgl_load_pipeline(_sclay.pip); + for (uint32_t i = 0; i < renderCommands.length; i++) { + Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i); + Clay_BoundingBox bbox = renderCommand->boundingBox; + switch (renderCommand->commandType) { + case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: { + Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle; + sgl_c4f(config->backgroundColor.r / 255.0f, + config->backgroundColor.g / 255.0f, + config->backgroundColor.b / 255.0f, + config->backgroundColor.a / 255.0f); + Clay_CornerRadius r = config->cornerRadius; + sgl_begin_triangle_strip(); + if(r.topLeft > 0 || r.topRight > 0){ + _draw_corner(bbox.x, bbox.y, -r.topLeft, -r.topLeft); + _draw_corner(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight); + _draw_rect(bbox.x+r.topLeft, bbox.y, + bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight)); + } + if(r.bottomLeft > 0 || r.bottomRight > 0){ + _draw_corner(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft); + _draw_corner(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight); + _draw_rect(bbox.x+r.bottomLeft, + bbox.y+bbox.height-CLAY__MAX(r.bottomLeft, r.bottomRight), + bbox.width-r.bottomLeft-r.bottomRight, CLAY__MAX(r.bottomLeft, r.bottomRight)); + } + if(r.topLeft < r.bottomLeft){ + if(r.topLeft < r.topRight){ + _draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft); + _draw_rect(bbox.x+r.topLeft, bbox.y+r.topRight, + r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft); + } else { + _draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft); + } + } else { + if(r.bottomLeft < r.bottomRight){ + _draw_rect(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft); + _draw_rect(bbox.x+r.bottomLeft, bbox.y+r.topLeft, + r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight); + } else { + _draw_rect(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft); + } + } + if(r.topRight < r.bottomRight){ + if(r.topRight < r.topLeft){ + _draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft, + r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight); + _draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight, + r.topRight, bbox.height-r.topRight-r.bottomRight); + } else { + _draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight, + r.bottomRight, bbox.height-r.topRight-r.bottomRight); + } + } else { + if(r.bottomRight < r.bottomLeft){ + _draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight, + r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft); + _draw_rect(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight, + r.bottomRight, bbox.height-r.topRight-r.bottomRight); + } else { + _draw_rect(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight, + r.topRight, bbox.height-r.topRight-r.bottomRight); + } + } + _draw_rect(bbox.x+CLAY__MAX(r.topLeft, r.bottomLeft), + bbox.y+CLAY__MAX(r.topLeft, r.topRight), + bbox.width-CLAY__MAX(r.topLeft, r.bottomLeft)-CLAY__MAX(r.topRight, r.bottomRight), + bbox.height-CLAY__MAX(r.topLeft, r.topRight)-CLAY__MAX(r.bottomLeft, r.bottomRight)); + sgl_end(); + break; + } + case CLAY_RENDER_COMMAND_TYPE_TEXT: { + if(!fonts) break; + Clay_TextRenderData *config = &renderCommand->renderData.text; + Clay_StringSlice text = config->stringContents; + fonsSetFont(_sclay.fonts, fonts[config->fontId]); + uint32_t color = sfons_rgba( + config->textColor.r, + config->textColor.g, + config->textColor.b, + config->textColor.a); + fonsSetColor(_sclay.fonts, color); + fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale); + fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale); + sgl_matrix_mode_modelview(); + sgl_push_matrix(); + sgl_scale(1.0f/_sclay.dpi_scale, 1.0f/_sclay.dpi_scale, 1.0f); + fonsDrawText(_sclay.fonts, bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale, + text.chars, text.chars + text.length); + sgl_pop_matrix(); + break; + } + case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: { + sgl_scissor_rectf(bbox.x*_sclay.dpi_scale, bbox.y*_sclay.dpi_scale, + bbox.width*_sclay.dpi_scale, bbox.height*_sclay.dpi_scale, + true); + break; + } + case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: { + sgl_scissor_rectf(0, 0, + _sclay.size.width*_sclay.dpi_scale, _sclay.size.height*_sclay.dpi_scale, + true); + break; + } + case CLAY_RENDER_COMMAND_TYPE_IMAGE: { + //TODO + break; + } + case CLAY_RENDER_COMMAND_TYPE_BORDER: { + Clay_BorderRenderData *config = &renderCommand->renderData.border; + sgl_c4f(config->color.r / 255.0f, + config->color.g / 255.0f, + config->color.b / 255.0f, + config->color.a / 255.0f); + Clay_BorderWidth w = config->width; + Clay_CornerRadius r = config->cornerRadius; + sgl_begin_triangle_strip(); + if(w.left > 0){ + _draw_rect(bbox.x, bbox.y + r.topLeft, + w.left, bbox.height - r.topLeft - r.bottomLeft); + } + if(w.right > 0){ + _draw_rect(bbox.x + bbox.width - w.right, bbox.y + r.topRight, + w.right, bbox.height - r.topRight - r.bottomRight); + } + if(w.top > 0){ + _draw_rect(bbox.x + r.topLeft, bbox.y, + bbox.width - r.topLeft - r.topRight, w.top); + } + if(w.bottom > 0){ + _draw_rect(bbox.x + r.bottomLeft, bbox.y + bbox.height - w.bottom, + bbox.width - r.bottomLeft - r.bottomRight, w.bottom); + } + if(r.topLeft > 0 && (w.top > 0 || w.left > 0)){ + _draw_corner_border(bbox.x, bbox.y, + -r.topLeft, -r.topLeft, + -r.topLeft+w.left, -r.topLeft+w.top); + } + if(r.topRight > 0 && (w.top > 0 || w.right > 0)){ + _draw_corner_border(bbox.x+bbox.width, bbox.y, + r.topRight, -r.topRight, + r.topRight-w.right, -r.topRight+w.top); + } + if(r.bottomLeft > 0 && (w.bottom > 0 || w.left > 0)){ + _draw_corner_border(bbox.x, bbox.y+bbox.height, + -r.bottomLeft, r.bottomLeft, + -r.bottomLeft+w.left, r.bottomLeft-w.bottom); + } + if(r.bottomRight > 0 && (w.bottom > 0 || w.right > 0)){ + _draw_corner_border(bbox.x+bbox.width, bbox.y+bbox.height, + r.bottomRight, r.bottomRight, + r.bottomRight-w.right, r.bottomRight-w.bottom); + } + sgl_end(); + break; + } + default: + break; + } + } + sgl_pop_pipeline(); + sfons_flush(_sclay.fonts); +} +#endif /* SOKOL_CLAY_IMPL */