colourpicker

Simple Colour Picker written in C
git clone anongit@rnpnr.xyz:colourpicker.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: fc1113ca25feced0272057ea921e9a45eecadcea
Parent: 671d3053c84fa25aab1a163ed961fa7ae4edeb6c
Author: Randy Palamar
Date:   Tue,  6 Aug 2024 23:02:15 -0600

move picker slider code to single shader invocation

This is slightly more code for now because we are replacing raylib
code. This should cause an actual reduction in code when the
slider mode gets migrated.

Diffstat:
Mcolourpicker.c | 123+++++++++++++++++++++++++++++++++++--------------------------------------------
Mhsv_lerp.glsl | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mutil.c | 10++++++----
3 files changed, 121 insertions(+), 87 deletions(-)

diff --git a/colourpicker.c b/colourpicker.c @@ -853,17 +853,8 @@ static v4 do_vertical_slider(ColourPickerCtx *ctx, v2 test_pos, Rect r, i32 idx, v4 bot_colour, v4 top_colour, v4 colour) { - v4 hsv[] = {bot_colour, top_colour}; - b32 hovering = CheckCollisionPointRec(test_pos.rv, r.rr); - BeginShaderMode(ctx->picker_shader); - - i32 mode = 0; - SetShaderValue(ctx->picker_shader, ctx->mode_id, &mode, SHADER_UNIFORM_INT); - SetShaderValue(ctx->picker_shader, ctx->size_id, r.size.E, SHADER_UNIFORM_VEC2); - SetShaderValueV(ctx->picker_shader, ctx->hsv_id, (f32 *)hsv, SHADER_UNIFORM_VEC4, 2); - if (hovering && ctx->held_idx == -1) { colour.x -= GetMouseWheelMove() * (bot_colour.x - top_colour.x) / 36; CLAMP(colour.x, top_colour.x, bot_colour.x); @@ -878,14 +869,6 @@ do_vertical_slider(ColourPickerCtx *ctx, v2 test_pos, Rect r, i32 idx, colour.x = top_colour.x + new_t * (bot_colour.x - top_colour.x); } - DrawRectangleRec(r.rr, BLACK); - - EndShaderMode(); - - DrawRectangleRoundedLinesEx(r.rr, SLIDER_ROUNDNESS, 0, 4.85 * SLIDER_BORDER_WIDTH, ctx->bg); - DrawRectangleRoundedLinesEx(r.rr, SLIDER_ROUNDNESS, 0, SLIDER_BORDER_WIDTH, - SLIDER_BORDER_COLOUR); - f32 param = (colour.x - top_colour.x) / (bot_colour.x - top_colour.x); { /* TODO: move this to ctx */ @@ -914,14 +897,16 @@ do_picker_mode(ColourPickerCtx *ctx, v2 relative_origin) { if (!IsShaderReady(ctx->picker_shader)) { #ifdef _DEBUG - ctx->picker_shader = LoadShader(0, HSV_LERP_SHADER_NAME); + ctx->picker_shader = LoadShader(0, HSV_LERP_SHADER_NAME); #else - ctx->picker_shader = LoadShaderFromMemory(0, g_hsv_shader_text); + ctx->picker_shader = LoadShaderFromMemory(0, g_hsv_shader_text); #endif - ctx->mode_id = GetShaderLocation(ctx->picker_shader, "u_mode"); - ctx->hsv_id = GetShaderLocation(ctx->picker_shader, "u_hsv"); - ctx->size_id = GetShaderLocation(ctx->picker_shader, "u_size"); - ctx->offset_id = GetShaderLocation(ctx->picker_shader, "u_offset"); + ctx->colour_mode_id = GetShaderLocation(ctx->picker_shader, "u_colour_mode"); + ctx->colours_id = GetShaderLocation(ctx->picker_shader, "u_colours"); + ctx->regions_id = GetShaderLocation(ctx->picker_shader, "u_regions"); + ctx->size_id = GetShaderLocation(ctx->picker_shader, "u_size"); + ctx->radius_id = GetShaderLocation(ctx->picker_shader, "u_radius"); + ctx->border_thick_id = GetShaderLocation(ctx->picker_shader, "u_border_thick"); } v4 colour = get_formatted_colour(ctx, CM_HSV); @@ -934,9 +919,9 @@ do_picker_mode(ColourPickerCtx *ctx, v2 relative_origin) } }; - Rect hs1 = cut_rect_left(tr, 0.2); - Rect hs2 = cut_rect_middle(tr, 0.2, 0.4); - Rect sv = cut_rect_right(tr, 0.4); + Rect hs1 = scale_rect_centered(cut_rect_left(tr, 0.2), (v2){.x = 0.5, .y = 0.95}); + Rect hs2 = scale_rect_centered(cut_rect_middle(tr, 0.2, 0.4), (v2){.x = 0.5, .y = 0.95}); + Rect sv = scale_rect_centered(cut_rect_right(tr, 0.4), (v2){.x = 1.0, .y = 0.95}); v2 test_pos = ctx->mouse_pos; test_pos.x -= relative_origin.x; @@ -944,63 +929,65 @@ do_picker_mode(ColourPickerCtx *ctx, v2 relative_origin) BeginTextureMode(ctx->picker_texture); - ClearBackground(ctx->bg); - - v4 hsv[2] = {colour, colour}; - hsv[0].x = 0; - hsv[1].x = 1; + v4 hsv[3] = {colour, colour, colour}; + hsv[1].x = 0; + hsv[2].x = 1; f32 last_hue = colour.x; - colour = do_vertical_slider(ctx, test_pos, - scale_rect_centered(hs1, (v2){.x = 0.5, .y = 0.95}), - PM_LEFT, hsv[1], hsv[0], colour); + colour = do_vertical_slider(ctx, test_pos, hs1, PM_LEFT, hsv[2], hsv[1], colour); if (colour.x != last_hue) ctx->pms.base_hue = colour.x - ctx->pms.fractional_hue; f32 fraction = 0.1; if (ctx->pms.base_hue - 0.5 * fraction < 0) { - hsv[0].x = 0; - hsv[1].x = fraction; + hsv[1].x = 0; + hsv[2].x = fraction; } else if (ctx->pms.base_hue + 0.5 * fraction > 1) { - hsv[0].x = 1 - fraction; - hsv[1].x = 1; + hsv[1].x = 1 - fraction; + hsv[2].x = 1; } else { - hsv[0].x = ctx->pms.base_hue - 0.5 * fraction; - hsv[1].x = ctx->pms.base_hue + 0.5 * fraction; + hsv[1].x = ctx->pms.base_hue - 0.5 * fraction; + hsv[2].x = ctx->pms.base_hue + 0.5 * fraction; } - colour = do_vertical_slider(ctx, test_pos, - scale_rect_centered(hs2, (v2){.x = 0.5, .y = 0.95}), - PM_MIDDLE, hsv[1], hsv[0], colour); + colour = do_vertical_slider(ctx, test_pos, hs2, PM_MIDDLE, hsv[2], hsv[1], colour); ctx->pms.fractional_hue = colour.x - ctx->pms.base_hue; + ClearBackground(ctx->bg); BeginShaderMode(ctx->picker_shader); - /* NOTE: S-V Plane */ - sv.pos.y += SLIDER_BORDER_WIDTH; - sv.pos.x += SLIDER_BORDER_WIDTH; - sv.size.y -= 2 * SLIDER_BORDER_WIDTH; - sv.size.x -= 2 * SLIDER_BORDER_WIDTH; - v2 offset = {.x = sv.pos.x}; - - b32 hovering = CheckCollisionPointRec(test_pos.rv, sv.rr); - if (hovering && IsMouseButtonDown(MOUSE_BUTTON_LEFT) && ctx->held_idx == -1) - ctx->held_idx = PM_RIGHT; - - if (ctx->held_idx == PM_RIGHT) { - CLAMP(test_pos.x, sv.pos.x, sv.pos.x + sv.size.w); - CLAMP(test_pos.y, sv.pos.y, sv.pos.y + sv.size.h); - colour.y = (test_pos.x - sv.pos.x) / sv.size.w; - colour.z = (sv.pos.y + sv.size.h - test_pos.y) / sv.size.h; - } - - i32 mode = 1; - SetShaderValue(ctx->picker_shader, ctx->mode_id, &mode, SHADER_UNIFORM_INT); - SetShaderValue(ctx->picker_shader, ctx->size_id, sv.size.E, SHADER_UNIFORM_VEC2); - SetShaderValue(ctx->picker_shader, ctx->offset_id, offset.E, SHADER_UNIFORM_VEC2); - SetShaderValueV(ctx->picker_shader, ctx->hsv_id, colour.E, SHADER_UNIFORM_VEC4, 1); - DrawRectangleRec(sv.rr, BLACK); + { + f32 regions[] = { + hs1.pos.x, hs1.pos.y, hs1.pos.x + hs1.size.w, hs1.pos.y + hs1.size.h, + hs2.pos.x, hs2.pos.y, hs2.pos.x + hs2.size.w, hs2.pos.y + hs2.size.h, + sv.pos.x, sv.pos.y, sv.pos.x + sv.size.w, sv.pos.y + sv.size.h + }; + f32 radius = SLIDER_BORDER_RADIUS; + f32 border_thick = SLIDER_BORDER_WIDTH; + i32 cm_mode = CM_HSV; + + SetShaderValue(ctx->picker_shader, ctx->radius_id, &radius, SHADER_UNIFORM_FLOAT); + SetShaderValue(ctx->picker_shader, ctx->border_thick_id, &border_thick, + SHADER_UNIFORM_FLOAT); + SetShaderValue(ctx->picker_shader, ctx->colour_mode_id, &cm_mode, + SHADER_UNIFORM_INT); + SetShaderValue(ctx->picker_shader, ctx->size_id, tr.size.E, SHADER_UNIFORM_VEC2); + SetShaderValueV(ctx->picker_shader, ctx->colours_id, (f32 *)hsv, + SHADER_UNIFORM_VEC4, 3); + SetShaderValueV(ctx->picker_shader, ctx->regions_id, regions, + SHADER_UNIFORM_VEC4, 3); + DrawRectangleRec(tr.rr, BLACK); + } EndShaderMode(); - DrawRectangleLinesEx(sv.rr, SLIDER_BORDER_WIDTH, SLIDER_BORDER_COLOUR); + b32 hovering = CheckCollisionPointRec(test_pos.rv, sv.rr); + if (hovering && IsMouseButtonDown(MOUSE_BUTTON_LEFT) && ctx->held_idx == -1) + ctx->held_idx = PM_RIGHT; + + if (ctx->held_idx == PM_RIGHT) { + CLAMP(test_pos.x, sv.pos.x, sv.pos.x + sv.size.w); + CLAMP(test_pos.y, sv.pos.y, sv.pos.y + sv.size.h); + colour.y = (test_pos.x - sv.pos.x) / sv.size.w; + colour.z = (sv.pos.y + sv.size.h - test_pos.y) / sv.size.h; + } f32 radius = 4; v2 param = {.x = colour.y, .y = 1 - colour.z}; diff --git a/hsv_lerp.glsl b/hsv_lerp.glsl @@ -6,13 +6,15 @@ in vec4 fragColor; out vec4 out_colour; -#define MODE_H 0 -#define MODE_SV 1 +#define CM_RGB 0 +#define CM_HSV 1 -uniform int u_mode; -uniform vec4 u_hsv[2]; -uniform vec2 u_offset; -uniform vec2 u_size; +uniform int u_colour_mode; +uniform float u_radius; +uniform float u_border_thick; +uniform vec4 u_colours[3]; +uniform vec4 u_regions[4]; +uniform vec2 u_size; /* input: h [0,360] | s,v [0, 1] * * output: rgb [0,1] */ @@ -23,19 +25,62 @@ vec3 hsv2rgb(vec3 hsv) return hsv.z - hsv.z * hsv.y * k; } +bool in_rounded_region(vec2 pos, vec4 min_max, float radius) +{ + vec2 min_xy = min_max.xy; + vec2 max_xy = min_max.zw; + vec2 ulp = vec2(min_xy.x + radius, max_xy.y - radius); + vec2 urp = vec2(max_xy.x - radius, max_xy.y - radius); + vec2 blp = vec2(min_xy.x + radius, min_xy.y + radius); + vec2 brp = vec2(max_xy.x - radius, min_xy.y + radius); + + bool ulr = length(ulp - pos) < radius; + bool urr = length(urp - pos) < radius; + bool blr = length(blp - pos) < radius; + bool brr = length(brp - pos) < radius; + bool rect = (pos.x > min_xy.x) && (pos.x < max_xy.x) && + (pos.y < max_xy.y - radius) && (pos.y > min_xy.y + radius); + bool upr = (pos.x > ulp.x) && (pos.x < urp.x) && (pos.y < max_xy.y) && (pos.y > min_xy.y); + + return rect || ulr || urr || blr || brr || upr; +} + void main() { - vec2 pos = (gl_FragCoord.xy - u_offset) / u_size; + vec2 pos = gl_FragCoord.xy; - vec4 out_hsv; - switch (u_mode) { - case MODE_H: - out_hsv = mix(u_hsv[0], u_hsv[1], pos.y); + vec4 colour = vec4(0); + switch (u_colour_mode) { + case CM_RGB: + out_colour = vec4(0); break; - case MODE_SV: - out_hsv = vec4(u_hsv[0].x, pos.x, pos.y, 1); + case CM_HSV: + vec4 shift = vec4(vec2(u_border_thick), -vec2(u_border_thick)); + + if (in_rounded_region(pos, u_regions[0], u_radius + u_border_thick)) { + colour.w = 1; + if (in_rounded_region(pos, u_regions[0] + shift, u_radius)) { + float scale = u_regions[0].w - u_regions[0].y; + colour = mix(vec4(1, u_colours[0].yzw), vec4(0, u_colours[0].yzw), + (pos.y - u_regions[0].y) / scale); + } + } else if (in_rounded_region(pos, u_regions[1], u_radius + u_border_thick)) { + colour.w = 1; + if (in_rounded_region(pos, u_regions[1] + shift, u_radius)) { + float scale = u_regions[1].w - u_regions[1].y; + colour = mix(u_colours[2], u_colours[1], + (pos.y - u_regions[1].y) / scale); + } + } else if (in_rounded_region(pos, u_regions[2], u_radius + u_border_thick)) { + colour.w = 1; + if (in_rounded_region(pos, u_regions[2] + shift, u_radius)) { + vec2 scale = u_regions[2].zw - u_regions[2].xy; + vec2 t = (pos - u_regions[2].xy) / scale; + colour = vec4(u_colours[0].x, t.x, t.y, 1); + } + } + colour.x *= 360; + out_colour = vec4(hsv2rgb(colour.xyz), colour.w); break; } - out_hsv.x *= 360; - out_colour = vec4(hsv2rgb(out_hsv.xyz), 1); } diff --git a/util.c b/util.c @@ -58,8 +58,8 @@ typedef union { } Rect; enum colour_mode { - CM_RGB, - CM_HSV, + CM_RGB = 0, + CM_HSV = 1, CM_LAST }; @@ -90,6 +90,7 @@ enum cardinal_direction { NORTH, EAST, SOUTH, WEST }; #define SLIDER_BORDER_COLOUR (Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0xCC} #define SLIDER_BORDER_WIDTH 3.0f #define SLIDER_ROUNDNESS 0.5f +#define SLIDER_BORDER_RADIUS 10.0f #define SLIDER_SCALE_SPEED 8.0f #define SLIDER_SCALE_TARGET 1.5f #define SLIDER_TRI_SIZE (v2){.x = 6, .y = 8} @@ -182,8 +183,9 @@ typedef struct { RenderTexture picker_texture; RenderTexture hsv_texture; - i32 mode_id, hsv_id; - i32 offset_id, size_id; + i32 colour_mode_id, colours_id; + i32 regions_id, size_id; + i32 radius_id, border_thick_id; u32 flags; enum colour_mode colour_mode;