ref: 2a853dc838b882d3eb0dc3f5bcb7cfad32baf416
dir: /picker.c/
#include <u.h> #include <libc.h> #include <thread.h> #include <draw.h> #include <memdraw.h> #include <mouse.h> #include <keyboard.h> #include "hsluv.h" #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b)) #define D2C(x) (int)MAX(0, MIN(0xff, x*256.0)) enum { Ckey, Cmouse, Cresize, Numchan, Offset = 6, Sliderheight = 32, }; typedef struct Color Color; typedef struct Mode Mode; struct Color { Rectangle r; double v[4]; double rgba[4]; ulong u; Image *i; }; struct Mode { char *name; char opt; void (*torgb)(double *v, double *rgb); void (*fromrgb)(double *rgb, double *v); double max[4]; }; static void _hsluv2rgb(double *v, double *rgb) { hsluv2rgb(v[0], v[1], v[2], rgb+0, rgb+1, rgb+2); } static void _rgb2hsluv(double *rgb, double *v) { rgb2hsluv(rgb[0], rgb[1], rgb[2], v+0, v+1, v+2); } static void _hpluv2rgb(double *v, double *rgb) { hpluv2rgb(v[0], v[1], v[2], rgb+0, rgb+1, rgb+2); } static void _rgb2hpluv(double *rgb, double *v) { rgb2hpluv(rgb[0], rgb[1], rgb[2], v+0, v+1, v+2); } static void _torgb(double *v, double *rgb) { v[0] = rgb[0]; v[1] = rgb[1]; v[2] = rgb[2]; } static void _fromrgb(double *rgb, double *v) { rgb[0] = v[0]; rgb[1] = v[1]; rgb[2] = v[2]; } static Mode modes[] = { { .name = "HSLuv", .opt = 's', .torgb = _hsluv2rgb, .fromrgb = _rgb2hsluv, .max = {360.0, 100.0, 100.0, 1.0}, }, { .name = "HPLuv", .opt = 'l', .torgb = _hpluv2rgb, .fromrgb = _rgb2hpluv, .max = {360.0, 100.0, 100.0, 1.0}, }, { .name = "RGB", .opt = 'r', .torgb = _torgb, .fromrgb = _fromrgb, .max = {1.0, 1.0, 1.0, 1.0}, }, }; static Color *colors; static int ncolors, curcolor, nchan; static Rectangle srects[3]; static Mode *mode; static Image * slider(int si, int w) { static Image *s, *sliders[4]; static u8int *b, *buf[4]; double c[4], rgb[3], dt; Rectangle rect; int i, n, mi; ulong u; rect = Rect(0, 0, w, 1); s = sliders[si]; if (s == nil || Dx(s->r) != w) { buf[si] = realloc(buf[si], 4*w); if (s != nil) freeimage(s); if ((s = sliders[si] = allocimage(display, rect, RGBA32, 1, DNofill)) == nil) sysfatal("allocimage: %r"); } b = buf[si]; memmove(c, colors[curcolor].v, sizeof(c)); dt = mode->max[si] / w; mi = c[si] / dt; c[si] = 0.0; for (i = n = 0; i < w; i++, n += 4) { mode->torgb(c, rgb); u = setalpha(D2C(rgb[0])<<24 | D2C(rgb[1])<<16 | D2C(rgb[2])<<8, D2C(c[3])); b[n] = 0xff; if (mi-2 == i) memset(b+n+1, 0, 3); else if (mi-1 == i) memset(b+n+1, 0xff, 3); else if (mi+1 == i) memset(b+n+1, 0xff, 3); else if (mi+2 == i) memset(b+n+1, 0, 3); else { b[n+0] = u & 0xff; b[n+1] = (u>>8) & 0xff; b[n+2] = (u>>16) & 0xff; b[n+3] = (u>>24) & 0xff; } c[si] = MIN(mode->max[si], c[si] + dt); } loadimage(s, rect, b, 4*w); return s; } static void redraw(void) { Rectangle r, cr; Image *im; char hex[8]; int i, colw; lockdisplay(display); draw(screen, screen->r, display->white, nil, ZP); r = screen->r; r.min.x += Offset; r.min.y += Offset; r.max.x -= Offset; r.max.y = r.min.y + Sliderheight; /* sliders */ for (i = 0; i < nchan; i++) { srects[i] = r; im = slider(i, Dx(r)); draw(screen, r, im, nil, ZP); r.min.y += Sliderheight + Offset; r.max.y += Sliderheight + Offset; } /* palette */ colw = MIN(Sliderheight, (Dx(r)-(ncolors-1)*Offset)/ncolors); cr = r; cr.min.x += (Dx(cr) - colw*ncolors - (ncolors-1)*Offset) / 2; for (i = 0; i < ncolors; i++) { if (i == curcolor) { freeimage(colors[i].i); colors[i].i = nil; } if (colors[i].i == nil) { colors[i].i = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, setalpha(colors[i].u | 0xff, colors[i].u & 0xff)); if (colors[i].i == nil) sysfatal("allocimage: %r"); } cr.max.x = cr.min.x + colw; draw(screen, cr, colors[i].i, nil, ZP); colors[i].r = insetrect(cr, -3); if (i == curcolor) border(screen, colors[i].r, 2, display->black, ZP); cr.min.x += colw + Offset; } r.min.y += Sliderheight + Offset; /* current color */ r.max.y = screen->r.max.y - Offset; draw(screen, r, colors[curcolor].i, nil, ZP); /* current color in hex */ if (nchan > 3) sprint(hex, "#%08lux", colors[curcolor].u); else sprint(hex, "#%06lux", colors[curcolor].u>>8); r.min.x += Dx(r)/2 - 7*stringwidth(font, "#")/2; r.min.y += Dy(r)/2 - font->height/2; stringbg(screen, r.min, display->white, ZP, font, hex, display->black, ZP); flushimage(display, 1); unlockdisplay(display); } static void usage(void) { int i; print("usage: %s [-a] [-", argv0); for (i = 0; i < nelem(modes); i++) print("%c", modes[i].opt); print("] rrggbbaa ...\n"); threadexitsall("usage"); } void threadmain(int argc, char **argv) { Mousectl *mctl; Keyboardctl *kctl; Rune r; Mouse m; Alt a[Numchan+1] = { [Ckey] = { nil, &r, CHANRCV }, [Cmouse] = { nil, &m, CHANRCV }, [Cresize] = { nil, nil, CHANRCV }, { nil, nil, CHANEND }, }; Color *c; char *s, buf[16]; vlong v; int i, j; mode = &modes[0]; nchan = 3; ARGBEGIN{ case 'a': nchan = 4; break; default: mode = nil; for (i = 0; i < nelem(modes); i++) { if (modes[i].opt == ARGC()) { mode = &modes[i]; break; } } if (mode == nil) { fprint(2, "unknown mode '%c'\n", ARGC()); usage(); } break; }ARGEND ncolors = argc; if (ncolors < 1) { fprint(2, "no colors specified\n"); usage(); } colors = calloc(ncolors, sizeof(Color)); for (i = 0; i < ncolors; i++) { if (strlen(argv[i]) != nchan*2) { fprint(2, "wrong number of components: '%s'\n", argv[i]); usage(); } if ((v = strtoll(argv[i], &s, 16)) == 0 && (s == argv[i] || *s || v < 0)) { fprint(2, "invalid color: '%s'\n", argv[i]); usage(); } if (nchan < 4) { v <<= 8; v |= 0xff; } colors[i].u = v; for (j = 0; j < 4; j++) { colors[i].rgba[j] = (double)((v>>24)&0xff) / 255.0; v <<= 8; } colors[i].v[3] = colors[i].rgba[3]; mode->fromrgb(colors[i].rgba, colors[i].v); } if (initdraw(nil, nil, "picker") < 0) sysfatal("initdraw: %r"); if ((kctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); a[Ckey].c = kctl->c; if ((mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); a[Cmouse].c = mctl->c; a[Cresize].c = mctl->resizec; display->locking = 1; unlockdisplay(display); redraw(); for (;;) { next: c = &colors[curcolor]; switch (alt(a)) { case -1: goto end; case Ckey: switch (r) { case Kleft: curcolor = MAX(0, curcolor-1); redraw(); break; case Kright: curcolor = MIN(ncolors-1, curcolor+1); redraw(); break; case Kdel: goto end; } break; case Cmouse: if (m.buttons == 1) { m.xy.x = MAX(screen->r.min.x, MIN(screen->r.max.x, m.xy.x)); for (i = 0; i < nchan; i++) { Rectangle r = srects[i]; r.max.x += 1; if (ptinrect(m.xy, r)) { ulong u; c->v[i] = MIN(mode->max[i], (double)(m.xy.x - r.min.x) * mode->max[i]/(double)(Dx(r)-1)); mode->torgb(c->v, c->rgba); u = D2C(c->rgba[0])<<24 | D2C(c->rgba[1])<<16 | D2C(c->rgba[2])<<8 | D2C(c->v[3]); if (c->u != u) { c->u = u; if (nchan < 4) j = sprint(buf, "%d %06lux\n", curcolor, u>>8); else j = sprint(buf, "%d %08lux\n", curcolor, u); if (write(1, buf, j) != j) goto end; redraw(); } goto next; } } for (i = 0; i < ncolors; i++) { if (ptinrect(m.xy, colors[i].r)) { curcolor = i; redraw(); goto next; } } } break; case Cresize: getwindow(display, Refnone); redraw(); break; } } end: threadexitsall(nil); }