
A simple HSV color picker for Plan9.
Just a simple color picker utility written in Plan9 C, following the style of the Plan9 Clock. Alternatively, you can use Sigrid's Color Picker if you want to use something made by someone who knows what they're doing.
Installation
Compile this source with the compiler for your platform, if you are using an ARM device:
5c color.c && 5l -o color color.c
#include <u.h> #include <libc.h> #include <draw.h> #include <thread.h> #include <event.h> /* Conversion */ typedef struct RgbColor { unsigned char r, g, b; } RgbColor; typedef struct HsvColor { unsigned char h, s, v; } HsvColor; static HsvColor selection; double ptangle(Point a, Point b) { return atan2(b.y - a.y, b.x - a.x); } double ptdistance(Point a, Point b) { int x = a.x - b.x; int y = a.y - b.y; return sqrt(x * x + y * y); } Point circlept(Point c, int r, int degrees) { double rad = (double)degrees * PI / 180.0; c.x += cos(rad) * r; c.y -= sin(rad) * r; return c; } Point getcenter(Rectangle r) { return divpt(addpt(r.min, r.max), 2); } int within(Point p, Rectangle r) { return p.x > r.min.x && p.x < r.max.x && p.y > r.min.y && p.y < r.max.y; } RgbColor hsv2rgb(HsvColor hsv) { RgbColor rgb; unsigned char region, remainder, p, q, t; if(hsv.s == 0) { return ((RgbColor){hsv.v, hsv.v, hsv.v}); } region = hsv.h / 43; remainder = (hsv.h - (region * 43)) * 6; p = (hsv.v * (255 - hsv.s)) >> 8; q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; switch(region) { case 0: rgb.r = hsv.v; rgb.g = t; rgb.b = p; break; case 1: rgb.r = q; rgb.g = hsv.v; rgb.b = p; break; case 2: rgb.r = p; rgb.g = hsv.v; rgb.b = t; break; case 3: rgb.r = p; rgb.g = q; rgb.b = hsv.v; break; case 4: rgb.r = t; rgb.g = p; rgb.b = hsv.v; break; default: rgb.r = hsv.v; rgb.g = p; rgb.b = q; break; } return rgb; } HsvColor rgb2hsv(RgbColor rgb) { HsvColor hsv; unsigned char rgbMin, rgbMax; rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); hsv.v = rgbMax; if(hsv.v == 0) { hsv.h = 0; hsv.s = 0; return hsv; } hsv.s = 255 * (double)(rgbMax - rgbMin) / hsv.v; if(hsv.s == 0) { hsv.h = 0; return hsv; } if(rgbMax == rgb.r) hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin); else if(rgbMax == rgb.g) hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin); else hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin); return hsv; } unsigned int rgb2hex(RgbColor clr) { return ((clr.r & 0xFF) << 24) + ((clr.g & 0xFF) << 16) + ((clr.b & 0xFF) << 8) + (255 & 0xFF); } unsigned int hsv2hex(HsvColor hsvclr) { return rgb2hex(hsv2rgb(hsvclr)); } /* Defaults */ void lineb(Image* dst, Point p0, Point p1, Image* src, Point sp) { int dx = abs(p1.x - p0.x), sx = p0.x < p1.x ? 1 : -1; int dy = -abs(p1.y - p0.y), sy = p0.y < p1.y ? 1 : -1; int err = dx + dy, e2; for(;;) { draw(dst, Rect(p0.x, p0.y, p0.x + 1, p0.y + 1), src, nil, sp); if(p0.x == p1.x && p0.y == p1.y) break; e2 = 2 * err; if(e2 >= dy) { err += dy; p0.x += sx; } if(e2 <= dx) { err += dx; p0.y += sy; } } } unsigned int gradeint(int a, int b, double ratio) { return (a * ratio) + (b * (1 - ratio)); } Point gradept(Point a, Point b, double ratio) { return Pt( gradeint(a.x, b.x, ratio), gradeint(a.y, b.y, ratio)); } unsigned int gradecolor(HsvColor a, HsvColor b, double ratio) { HsvColor clr = (HsvColor){ gradeint(a.h, b.h, ratio), gradeint(a.s, b.s, ratio), gradeint(a.v, b.v, ratio)}; return hsv2hex(clr); } void gradeline(Image* dst, Point p0, Point p1, HsvColor clr0, HsvColor clr1, int segs, Point sp) { for(int i = 0; i < segs; i++) { double ratio = (double)i / segs; Image* clrimg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, gradecolor(clr0, clr1, ratio)); lineb(dst, gradept(p0, p1, ratio), gradept(p0, p1, (double)(i + 1) / segs), clrimg, ZP); freeimage(clrimg); } } void redraw(Image* dst) { Point size = subpt(screen->r.max, screen->r.min); Point center = divpt(size, 2); Rectangle frame = (Rectangle){Pt(0, 0), size}; int pad = 20; int rad = ((size.x < size.y ? size.x : size.y) / 2) - pad; Image* view = allocimage(display, frame, RGBA32, 1, 0x000000FF); /* draw ring */ for(int i = 0; i < 180; i++) { Point p0 = circlept(center, rad, i * 2); Point p1 = circlept(center, rad, (i + 1) * 2); unsigned int angle = ptangle(center, p0) / PI / 2 * 255; int hexclr = hsv2hex((HsvColor){angle, 255, 255}); Image* imgclr = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, hexclr); lineb(view, p0, p1, imgclr, ZP); freeimage(imgclr); } /* draw selection */ RgbColor selrgb = hsv2rgb(selection); HsvColor selhue = (HsvColor){selection.h, 255, 255}; unsigned int selhex = rgb2hex(selrgb); Image* selclr = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, selhex); Image* selhueclr = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, hsv2hex(selhue)); /* draw hue */ double angle = (selection.h / 255.0) * -360.0; Point huepos = circlept(center, rad, angle); fillellipse(view, huepos, 2, 2, selhueclr, ZP); /* draw sat */ double distance = (selection.s / 255.0) * rad; Point satpos = circlept(center, distance, angle); ellipse(view, center, distance, distance, 0, selclr, ZP); fillellipse(view, satpos, 2, 2, selclr, ZP); gradeline(view, huepos, satpos, selhue, selection, 8, ZP); /* collapse if window is horizontal */ if(size.y > size.x + 2 * pad) { Rectangle sliderect = Rect(0, size.y - pad * 2, size.x, size.y - pad); draw(view, sliderect, display->black, nil, ZP); gradeline(view, addpt(sliderect.min, Pt(pad, pad / 2)), addpt(sliderect.max, Pt(-pad, -pad / 2)), (HsvColor){selection.h, selection.s, 0}, (HsvColor){selection.h, selection.s, 255}, 16, ZP); Point valpos = addpt(sliderect.min, Pt((selection.v / 255.0) * (size.x - 2 * pad) + pad, pad / 2)); fillellipse(view, valpos, 2, 2, selclr, ZP); } /* header */ char hexstr[16]; char rgbstr[12]; snprint(hexstr, sizeof(hexstr), "#%02ux%02ux%02ux", (selhex >> 24) & 0xFF, (selhex >> 16) & 0xFF, (selhex >> 8) & 0xFF); snprint(rgbstr, sizeof(rgbstr), "%ud,%ud,%ud", selrgb.r, selrgb.g, selrgb.b); Point hexstrsize = stringsize(display->defaultfont, hexstr); Point rgbstrsize = stringsize(display->defaultfont, rgbstr); /* collapse if window is horizontal */ if(size.y > size.x + 2 * pad) { Rectangle clearrect = Rect(pad, pad, size.x - pad, 2 * pad); draw(view, clearrect, display->black, nil, ZP); string(view, Pt(pad, pad), display->white, ZP, display->defaultfont, hexstr); if(hexstrsize.x + rgbstrsize.x < size.x) string(view, Pt(size.x - pad - rgbstrsize.x, pad), selclr, ZP, display->defaultfont, rgbstr); } draw(dst, screen->r, view, nil, ZP); flushimage(display, 1); freeimage(selclr); freeimage(selhueclr); freeimage(view); } void select(HsvColor clr) { selection.h = clr.h; selection.s = clr.s; selection.v = clr.v; redraw(screen); } void touch(Point m) { Rectangle r = screen->r; Point center = getcenter(r); int pad = 20; int radius = ((Dx(r) < Dy(r) ? Dx(r) : Dy(r)) / 2) - pad; HsvColor newsel = selection; if(ptdistance(center, m) > radius) { int width = (r.max.x - r.min.x) - pad * 2; Rectangle sliderect = Rect(r.min.x, r.max.y - pad * 2, r.max.x, r.max.y); if(within(m, sliderect)) { int touchx = m.x - screen->r.min.x - pad; double ratio = touchx / (double)width; newsel.v = ratio > 1 ? 255.0 : ratio < 0 ? 0 : ratio * 255.0; } else { double angle = ptangle(center, m); newsel.h = (int)(angle / PI / 2 * 255) % 255; } } else { double distance = ptdistance(center, m); newsel.s = (distance / radius) * 255.0; } select(newsel); } int hex2int(char a, char b) { a = (a <= '9') ? a - '0' : (a & 0x7) + 9; b = (b <= '9') ? b - '0' : (b & 0x7) + 9; return (a << 4) + b; } void dopaste(void) { char* p; int f; if((f = open("/dev/snarf", OREAD)) >= 0) { char body[8]; read(f, body, 8); if(body[0] == '#') { RgbColor rgbclr = (RgbColor){ hex2int(body[1], body[2]), hex2int(body[3], body[4]), hex2int(body[5], body[6])}; select(rgb2hsv(rgbclr)); } close(f); } } void dosnarf(void) { int f; if((f = open("/dev/snarf", OWRITE)) >= 0) { unsigned int selhex = rgb2hex(hsv2rgb(selection)); char hexstr[16]; snprint(hexstr, sizeof(hexstr), "#%02ux%02ux%02ux", (selhex >> 24) & 0xFF, (selhex >> 16) & 0xFF, (selhex >> 8) & 0xFF); write(f, hexstr, strlen(hexstr)); close(f); } } void eresized(int new) { if(new&& getwindow(display, Refnone) < 0) fprint(2, "can't reattach to window"); draw(screen, screen->r, display->black, nil, ZP); redraw(screen); } void main(int argc, char* argv[]) { USED(argc, argv); Event e; Mouse m; Menu menu; char* mstr[] = {"Snarf", "Paste", "Exit", 0}; int key; if(initdraw(0, 0, "Color") < 0) sysfatal("initdraw failed"); /* initial color */ selection.h = 110; selection.s = 120; selection.v = 220; eresized(0); einit(Emouse); menu.item = mstr; menu.lasthit = 0; redraw(screen); /* Break on mouse3 */ for(;;) { key = event(&e); if(key == Emouse) { m = e.mouse; if(m.buttons & 4) { if(emenuhit(3, &m, &menu) == 0) dosnarf(); if(emenuhit(3, &m, &menu) == 1) dopaste(); if(emenuhit(3, &m, &menu) == 2) exits(0); } else if(m.buttons & 1) { touch(m.xy); } } } }
incoming(1): plan9 clock
Last update on 14Q03, edited 3 times. +16/16fh ----|-