Commit: 272a24c00d46f0006276ee2b8c15e55785c1e875
Parent: 4c2545a25a1029b986dc7165db81ddff0ec516aa
Author: Randy Palamar
Date: Sat, 8 Jun 2024 17:47:46 -0600
first attempt at converting to HSV
Raylib has a problem where Gradients seemingly can only be drawn
in RGB space so HSV slide looks like garbage. This needs to fixed
later.
Diffstat:
M | colourpicker.c | | | 223 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
M | main.c | | | 3 | +++ |
M | util.c | | | 12 | ++++++++---- |
3 files changed, 166 insertions(+), 72 deletions(-)
diff --git a/colourpicker.c b/colourpicker.c
@@ -1,7 +1,13 @@
/* See LICENSE for copyright details */
+#include <stdio.h>
#include <raylib.h>
#include "util.c"
+static const char *mode_labels[CPM_LAST][4] = {
+ [CPM_RGB] = { "R:", "G:", "B:", "A:" },
+ [CPM_HSV] = { "H:", "S:", "V:", "A:" },
+};
+
static v2
center_text_in_rect(Rect r, const char *text, Font font, f32 fontsize)
{
@@ -37,111 +43,197 @@ cut_rect_right(Rect r, f32 fraction)
return r;
}
-static f32
-do_slider(Rect r, char *label, f32 current, Font font)
+static v4
+rgb_to_hsv(v4 rgb)
+{
+ Vector3 hsv = ColorToHSV(ColorFromNormalized(rgb.rv));
+ return (v4){ .x = hsv.x / 360, .y = hsv.y, .z = hsv.z, .w = rgb.a };
+}
+
+static v4
+hsv_to_rgb(v4 hsv)
{
- Rect lr = cut_rect_left(r, 0.1);
+ Color rgba = ColorFromHSV(hsv.x * 360, hsv.y, hsv.z);
+ rgba.a = hsv.a * 255;
+ return (v4){ .rv = ColorNormalize(rgba) };
+}
+
+static void
+draw_slider_rect(Rect r, v4 colour, enum colour_picker_mode mode, i32 idx, Color bg)
+{
+ Color sel, left, right;
+ if (mode != CPM_HSV) {
+ v4 cl = colour;
+ v4 cr = colour;
+ cl.E[idx] = 0;
+ cr.E[idx] = 1;
+ left = ColorFromNormalized(cl.rv);
+ right = ColorFromNormalized(cr.rv);
+ sel = ColorFromNormalized(colour.rv);
+ } else {
+ v4 tmp = colour;
+ switch (idx) {
+ case 0: /* H */
+ left = ColorFromHSV(0, colour.y, colour.z);
+ right = ColorFromHSV(360, colour.y, colour.z);
+ break;
+ case 1: /* S */
+ left = ColorFromHSV(colour.x * 360, 0, colour.z);
+ right = ColorFromHSV(colour.x * 360, 1, colour.z);
+ break;
+ case 2: /* V */
+ left = ColorFromHSV(colour.x * 360, colour.y, 0);
+ right = ColorFromHSV(colour.x * 360, colour.y, 1);
+ break;
+ case 3:
+ tmp.a = 0;
+ left = ColorFromNormalized(hsv_to_rgb(tmp).rv);
+ tmp.a = 1;
+ right = ColorFromNormalized(hsv_to_rgb(tmp).rv);
+ break;
+ }
+ if (idx != 3) {
+ left.a = colour.a;
+ right.a = colour.a;
+ }
+ sel = ColorFromNormalized(hsv_to_rgb(colour).rv);
+ }
+
+ f32 current = colour.E[idx];
+ Rect srl = cut_rect_left(r, current);
+ Rect srr = cut_rect_right(r, current);
+ DrawRectangleGradientEx(srl.rr, left, left, sel, sel);
+ DrawRectangleGradientEx(srr.rr, sel, sel, right, right);
+ DrawRectangleRoundedLines(r.rr, 1, 0, 12, bg);
+}
+
+static void
+do_slider(ColourPickerCtx *ctx, Rect r, i32 label_idx)
+{
+ Rect lr = cut_rect_left(r, 0.08);
Rect vr = cut_rect_right(r, 0.85);
Rect sr = cut_rect_middle(r, 0.1, 0.85);
sr.size.h *= 0.75;
sr.pos.y += r.size.h * 0.125;
- v2 fpos = center_text_in_rect(lr, label, font, 48);
- DrawTextEx(font, label, fpos.rv, 48, 0, LIGHTGRAY);
+ const char *label = mode_labels[ctx->mode][label_idx];
+ v2 fpos = center_text_in_rect(lr, label, ctx->font, 48);
+ DrawTextEx(ctx->font, label, fpos.rv, 48, 0, ctx->fg);
+ f32 current = ctx->colour.E[label_idx];
v2 mouse = { .rv = GetMousePosition() };
if (CheckCollisionPointRec(mouse.rv, sr.rr)) {
f32 wheel = GetMouseWheelMove();
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
current = (mouse.x - sr.pos.x) / sr.size.w;
- current += 4 * wheel / sr.size.w;
+ current += wheel / 255;
CLAMP(current, 0.0, 1.0);
+ ctx->colour.E[label_idx] = current;
}
- DrawRectangleRounded(sr.rr, 1.0, 0, PURPLE);
- v2 start = {
- .x = sr.pos.x + current * sr.size.w,
- .y = sr.pos.y + sr.size.h,
- };
- v2 end = start;
- end.y -= sr.size.h;
- DrawLineEx(start.rv, end.rv, 8, BLACK);
+ draw_slider_rect(sr, ctx->colour, ctx->mode, label_idx, ctx->bg);
- const char *value = TextFormat("%0.02f", current);
- fpos = center_text_in_rect(vr, value, font, 36);
- DrawTextEx(font, value, fpos.rv, 36, 0, LIGHTGRAY);
+ {
+ f32 half_tri_w = 8;
+ f32 tri_h = 12;
+ v2 t_mid = {
+ .x = sr.pos.x + current * sr.size.w,
+ .y = sr.pos.y,
+ };
+ v2 t_left = {
+ .x = t_mid.x - half_tri_w,
+ .y = t_mid.y - tri_h,
+ };
+ v2 t_right = {
+ .x = t_mid.x + half_tri_w,
+ .y = t_mid.y - tri_h,
+ };
+ DrawTriangle(t_right.rv, t_left.rv, t_mid.rv, ctx->fg);
- return current;
+ t_mid.y += sr.size.h;
+ t_left.y += sr.size.h + 2 * tri_h;
+ t_right.y += sr.size.h + 2 * tri_h;
+ DrawTriangle(t_mid.rv, t_left.rv, t_right.rv, ctx->fg);
+ }
+
+ const char *value = TextFormat("%0.02f", current);
+ fpos = center_text_in_rect(vr, value, ctx->font, 36);
+ DrawTextEx(ctx->font, value, fpos.rv, 36, 0, ctx->fg);
}
-static enum colour_picker_mode
-do_status_bar(Rect r, enum colour_picker_mode mode, Color colour, Font font)
+static void
+do_status_bar(ColourPickerCtx *ctx, Rect r)
{
- Rect hr = cut_rect_left(r, 0.5);
- Rect sr = cut_rect_right(r, 0.7);
-
- const char *hex = TextFormat("%02x%02x%02x%02x",
- colour.r, colour.b, colour.g,
- colour.a);
- v2 fpos = center_text_in_rect(hr, hex, font, 48);
- DrawTextEx(font, hex, fpos.rv, 48, 0, LIGHTGRAY);
+ Rect hex_r = cut_rect_left(r, 0.5);
+ Rect mode_r = cut_rect_right(r, 0.7);
v2 mouse = { .rv = GetMousePosition() };
- if (CheckCollisionPointRec(mouse.rv, hr.rr)) {
- if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
- SetClipboardText(hex);
- }
-
- if (CheckCollisionPointRec(mouse.rv, sr.rr)) {
+ if (CheckCollisionPointRec(mouse.rv, mode_r.rr)) {
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
- if (mode == HSV)
- mode = RGB;
- else
- mode++;
+ if (ctx->mode == CPM_HSV) {
+ ctx->mode = CPM_RGB;
+ ctx->colour = hsv_to_rgb(ctx->colour);
+ } else {
+ ctx->mode++;
+ ctx->colour = rgb_to_hsv(ctx->colour);
+ }
}
if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) {
- if (mode == RGB)
- mode = HSV;
- else
- mode--;
+ if (ctx->mode == CPM_RGB) {
+ ctx->mode = CPM_HSV;
+ ctx->colour = rgb_to_hsv(ctx->colour);
+ } else {
+ ctx->mode--;
+ ctx->colour = hsv_to_rgb(ctx->colour);
+ }
}
}
- char *mtext;
- switch (mode) {
- case RGB: mtext = "RGB"; break;
- case HSV: mtext = "HSV"; break;
+ i32 hex_collides = CheckCollisionPointRec(mouse.rv, hex_r.rr);
+ if (hex_collides && IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) {
+ const char *new = TextToLower(GetClipboardText());
+ u32 r, g, b, a;
+ sscanf(new, "%02x%02x%02x%02x", &r, &g, &b, &a);
+ ctx->colour.rv = ColorNormalize((Color){ .r = r, .g = g, .b = b, .a = a });
}
- fpos = center_text_in_rect(sr, mtext, font, 48);
- DrawTextEx(font, mtext, fpos.rv, 48, 0, LIGHTGRAY);
- return mode;
+ Color hc = ColorFromNormalized(ctx->colour.rv);
+ const char *hex = TextFormat("%02x%02x%02x%02x", hc.r, hc.g, hc.b, hc.a);
+ v2 fpos = center_text_in_rect(hex_r, hex, ctx->font, 48);
+ DrawTextEx(ctx->font, hex, fpos.rv, 48, 0, ctx->fg);
+
+ if (hex_collides && IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+ SetClipboardText(hex);
+
+ char *mtext;
+ switch (ctx->mode) {
+ case CPM_RGB: mtext = "RGB"; break;
+ case CPM_HSV: mtext = "HSV"; break;
+ case CPM_LAST: ASSERT(0); break;
+ }
+ fpos = center_text_in_rect(mode_r, mtext, ctx->font, 48);
+ DrawTextEx(ctx->font, mtext, fpos.rv, 48, 0, ctx->fg);
}
void
do_colour_picker(ColourPickerCtx *ctx)
{
- ClearBackground(BLACK);
+ ClearBackground(ctx->bg);
DrawFPS(20, 20);
uv2 ws = ctx->window_size;
- Color colour = ColorFromNormalized(ctx->colour.rv);
{
+ v4 vcolour = ctx->mode == CPM_HSV ? hsv_to_rgb(ctx->colour) : ctx->colour;
+ Color colour = ColorFromNormalized(vcolour.rv);
v2 cc = { .x = (f32)ws.w / 2, .y = (f32)ws.h / 4 };
DrawCircleSector(cc.rv, 0.6 * cc.x, 0, 360, 69, RED);
DrawCircleSector(cc.rv, 0.58 * cc.x, 0, 360, 69, colour);
}
{
- v2 start = { .x = 0, .y = (f32)ws.h / 2 };
- v2 end = { .x = ws.h, .y = (f32)ws.h / 2 };
- DrawLineEx(start.rv, end.rv, 8, BLUE);
- }
-
- {
Rect subregion = {
.pos = { .x = 0.05 * (f32)ws.w, .y = (f32)ws.h / 2, },
.size = { .w = (f32)ws.w * 0.9, .h = (f32)ws.h / 2 * 0.9 },
@@ -149,21 +241,16 @@ do_colour_picker(ColourPickerCtx *ctx)
Rect sb = subregion;
sb.size.h *= 0.25;
- ctx->mode = do_status_bar(sb, ctx->mode, colour, ctx->font);
+
+ do_status_bar(ctx, sb);
Rect r = subregion;
r.pos.y += 0.25 * ((f32)ws.h / 2);
r.size.h *= 0.15;
- ctx->colour.r = do_slider(r, "R:", ctx->colour.r, ctx->font);
-
- r.pos.y += r.size.h + 0.03 * ((f32)ws.h / 2);
- ctx->colour.g = do_slider(r, "G:", ctx->colour.g, ctx->font);
-
- r.pos.y += r.size.h + 0.03 * ((f32)ws.h / 2);
- ctx->colour.b = do_slider(r, "B:", ctx->colour.b, ctx->font);
-
- r.pos.y += r.size.h + 0.03 * ((f32)ws.h / 2);
- ctx->colour.a = do_slider(r, "A:", ctx->colour.a, ctx->font);
+ for (i32 i = 0; i < 4; i++) {
+ do_slider(ctx, r, i);
+ r.pos.y += r.size.h + 0.03 * ((f32)ws.h / 2);
+ }
}
}
diff --git a/main.c b/main.c
@@ -47,6 +47,9 @@ main(void)
{
ColourPickerCtx ctx = {0};
ctx.window_size = (uv2){.w = 720, .h = 960};
+ ctx.bg = (Color){ .r = 0x26, .g = 0x1e, .b = 0x22, .a = 0xff };
+ ctx.fg = (Color){ .r = 0xea, .g = 0xe1, .b = 0xb4, .a = 0xff };
+ ctx.colour.a = 1.0;
SetConfigFlags(FLAG_VSYNC_HINT);
InitWindow(ctx.window_size.w, ctx.window_size.h, "Colour Picker");
diff --git a/util.c b/util.c
@@ -36,14 +36,18 @@ typedef union {
} Rect;
enum colour_picker_mode {
- RGB,
- HSV,
+ CPM_RGB,
+ CPM_HSV,
+ CPM_LAST
};
typedef struct {
- uv2 window_size;
- Font font;
v4 colour;
+ Font font;
+ uv2 window_size;
+ Color bg;
+ Color fg;
+
enum colour_picker_mode mode;
} ColourPickerCtx;