From 5a0d301c60e6db5425537b7b808969ce54bb55d9 Mon Sep 17 00:00:00 2001 From: rivten Date: Mon, 8 Sep 2025 03:35:18 +0200 Subject: [PATCH] [Renderers/Sokol] allow usage of images with sokol renderer (#489) Co-authored-by: Hugo Viala Co-authored-by: Andrew Sampson <1297077+andrewmd5@users.noreply.github.com> --- renderers/sokol/sokol_clay.h | 147 ++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/renderers/sokol/sokol_clay.h b/renderers/sokol/sokol_clay.h index 35ba3ff..1319335 100644 --- a/renderers/sokol/sokol_clay.h +++ b/renderers/sokol/sokol_clay.h @@ -91,6 +91,15 @@ Unfortunately Clay does not currently provide feedback on whether a mouse click was handled or not. + --- if you want to use images with clay, you should pass a pointer to a + sclay_image to the CLAY macro, like this: + CLAY({ + ... + .image = { .imageData = &(sclay_image){ .view = view, .sampler = 0 } }, + }) + Using 0 as a sampler uses the sokol default sampler with linear interpolation. + The image should be created using sg_make_image from sokol_gfx. + --- finally, on application shutdown, call sclay_shutdown() @@ -101,6 +110,15 @@ typedef int sclay_font_t; +typedef struct sclay_image { + sg_view view; + sg_sampler sampler; + struct { + float u0, v0, u1, v1; + } uv; +} sclay_image; + + void sclay_setup(); void sclay_shutdown(); @@ -247,6 +265,15 @@ static void _draw_rect(float x, float y, float w, float h){ sgl_v2f(x+w, y+h); } +static void _draw_rect_textured(float x, float y, float w, float h, float u0, float v0, float u1, float v1){ + sgl_v2f_t2f(x, y, u0, v0); + sgl_v2f_t2f(x, y, u0, v0); + sgl_v2f_t2f(x+w, y, u1, v0); + sgl_v2f_t2f(x, y+h, u0, v1); + sgl_v2f_t2f(x+w, y+h, u1, v1); + sgl_v2f_t2f(x+w, y+h, u1, v1); +} + static float _SIN[16] = { 0.000000f, 0.104528f, 0.207912f, 0.309017f, 0.406737f, 0.500000f, 0.587785f, 0.669131f, @@ -266,6 +293,23 @@ static void _draw_corner(float x, float y, float rx, float ry){ sgl_v2f(x+(rx*_SIN[0]), y+(ry*_SIN[15])); } +static void _draw_corner_textured(float x, float y, float rx, float ry, float bx, float by, float bw, float bh, float u0, float v0, float u1, float v1) { + x -= rx; + y -= ry; +#define MAP_U(x) (u0+(((x)-bx)/bw)*(u1-u0)) +#define MAP_V(y) (v0+(((y)-by)/bh)*(v1-v0)) + sgl_v2f_t2f(x, y, MAP_U(x), MAP_V(y)); + for(int i = 0; i < 16; ++i){ + sgl_v2f_t2f(x, y, MAP_U(x), MAP_V(y)); + float px = x+(rx*_SIN[15-i]); + float py = y+(ry*_SIN[i]); + sgl_v2f_t2f(px, py, MAP_U(px), MAP_V(py)); + } + sgl_v2f_t2f(x+(rx*_SIN[0]), y+(ry*_SIN[15]), MAP_U(x+(rx*_SIN[0])), MAP_V(y+(ry*_SIN[15]))); +#undef MAP_U +#undef MAP_V +} + /* 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; @@ -390,7 +434,108 @@ void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) { break; } case CLAY_RENDER_COMMAND_TYPE_IMAGE: { - //TODO + Clay_ImageRenderData *config = &renderCommand->renderData.image; + sclay_image* img = (sclay_image*)config->imageData; + // by default, u1 and v1 are 1. if we pass 0. + // note, we are modifying a copy ! + float u0 = img->uv.u0; + float v0 = img->uv.v0; + float u1 = img->uv.u1; + float v1 = img->uv.v1; + if (u1 == 0.f) { + u1 = 1.f; + } + if (v1 == 0.f) { + v1 = 1.f; + } + + int untinted = config->backgroundColor.r == 0 && config->backgroundColor.g == 0 && config->backgroundColor.b == 0 && config->backgroundColor.a == 0; + float cr = untinted ? 1.f : (config->backgroundColor.r / 255.0f); + float gr = untinted ? 1.f : (config->backgroundColor.g / 255.0f); + float br = untinted ? 1.f : (config->backgroundColor.b / 255.0f); + float ar = untinted ? 1.f : (config->backgroundColor.a / 255.0f); + + sgl_c4f(cr, gr, br, ar); + + Clay_CornerRadius r = config->cornerRadius; + + sgl_enable_texture(); + sgl_texture(img->view, img->sampler); + + sgl_begin_triangle_strip(); + if(r.topLeft > 0 || r.topRight > 0){ + _draw_corner_textured(bbox.x, bbox.y, -r.topLeft, -r.topLeft, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1); + _draw_corner_textured(bbox.x+bbox.width, bbox.y, r.topRight, -r.topRight, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1); + _draw_rect_textured(bbox.x+r.topLeft, bbox.y, + bbox.width-r.topLeft-r.topRight, CLAY__MAX(r.topLeft, r.topRight), + u0 + (r.topLeft/bbox.width)*(u1-u0), v0, u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (CLAY__MAX(r.topLeft, r.topRight)/bbox.height)*(v1-v0)); + } + if(r.bottomLeft > 0 || r.bottomRight > 0){ + _draw_corner_textured(bbox.x, bbox.y+bbox.height, -r.bottomLeft, r.bottomLeft, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1); + _draw_corner_textured(bbox.x+bbox.width, bbox.y+bbox.height, r.bottomRight, r.bottomRight, bbox.x, bbox.y, bbox.width, bbox.height, u0, v0, u1, v1); + _draw_rect_textured(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), + u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (CLAY__MAX(r.bottomLeft, r.bottomRight)/bbox.height)*(v1-v0), u1 - (r.bottomRight/bbox.width)*(u1-u0), v1); + } + if(r.topLeft < r.bottomLeft){ + if(r.topLeft < r.topRight){ + _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft, + u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0)); + _draw_rect_textured(bbox.x+r.topLeft, bbox.y+r.topRight, + r.bottomLeft-r.topLeft, bbox.height-r.topRight-r.bottomLeft, + u0 + (r.topLeft/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0)); + } else { + _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft, + u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0)); + } + } else { + if(r.bottomLeft < r.bottomRight){ + _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.bottomLeft, bbox.height-r.topLeft-r.bottomLeft, + u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.bottomLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0)); + _draw_rect_textured(bbox.x+r.bottomLeft, bbox.y+r.topLeft, + r.topLeft-r.bottomLeft, bbox.height-r.topLeft-r.bottomRight, + u0 + (r.bottomLeft/bbox.width)*(u1-u0), v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomRight/bbox.height)*(v1-v0)); + } else { + _draw_rect_textured(bbox.x, bbox.y+r.topLeft, r.topLeft, bbox.height-r.topLeft-r.bottomLeft, + u0, v0 + (r.topLeft/bbox.height)*(v1-v0), u0 + (r.topLeft/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0)); + } + } + if(r.topRight < r.bottomRight){ + if(r.topRight < r.topLeft){ + _draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topLeft, + r.bottomRight-r.topRight, bbox.height-r.topLeft-r.bottomRight, + u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topLeft/bbox.height)*(v1-v0), u1 - (r.topRight/bbox.width)*(u1-u0), v1 - (r.bottomRight/bbox.height)*(v1-v0)); + _draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight, + r.topRight, bbox.height-r.topRight-r.bottomRight, + u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0)); + } else { + _draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight, + r.bottomRight, bbox.height-r.topRight-r.bottomRight, + u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0)); + } + } else { + if(r.bottomRight < r.bottomLeft){ + _draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight, + r.topRight-r.bottomRight, bbox.height-r.topRight-r.bottomLeft, + u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1 - (r.bottomRight/bbox.width)*(u1-u0), v1 - (r.bottomLeft/bbox.height)*(v1-v0)); + _draw_rect_textured(bbox.x+bbox.width-r.bottomRight, bbox.y+r.topRight, + r.bottomRight, bbox.height-r.topRight-r.bottomRight, + u1 - (r.bottomRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0)); + } else { + _draw_rect_textured(bbox.x+bbox.width-r.topRight, bbox.y+r.topRight, + r.topRight, bbox.height-r.topRight-r.bottomRight, + u1 - (r.topRight/bbox.width)*(u1-u0), v0 + (r.topRight/bbox.height)*(v1-v0), u1, v1 - (r.bottomRight/bbox.height)*(v1-v0)); + } + } + _draw_rect_textured(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), + u0+CLAY__MAX(r.topLeft,r.bottomLeft)/bbox.width*(u1-u0), v0+CLAY__MAX(r.topLeft,r.topRight)/bbox.height*(v1-v0), + u1-CLAY__MAX(r.topRight,r.bottomRight)/bbox.width*(u1-u0), v1-CLAY__MAX(r.bottomLeft,r.bottomRight)/bbox.height*(v1-v0)); + sgl_end(); + sgl_disable_texture(); break; } case CLAY_RENDER_COMMAND_TYPE_BORDER: {