ref: 2dc7bd15eb2a52b6c5a7bb32eb4cdbe60fa180be
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 = 48, }; typedef struct Color Color; typedef struct Space Space; struct Color { Rectangle r; double v[4]; double rgba[4]; ulong u; Image *i; }; struct Space { char *name; char opt; char single; 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) { rgb[0] = v[0]; rgb[1] = v[1]; rgb[2] = v[2]; } static void _fromrgb(double *rgb, double *v) { v[0] = rgb[0]; v[1] = rgb[1]; v[2] = rgb[2]; } static Space spaces[] = { { .name = "HSLuv", .opt = 's', .single = 0, .torgb = _hsluv2rgb, .fromrgb = _rgb2hsluv, .max = {360.0, 100.0, 100.0, 1.0}, }, { .name = "HPLuv", .opt = 'l', .single = 0, .torgb = _hpluv2rgb, .fromrgb = _rgb2hpluv, .max = {360.0, 100.0, 100.0, 1.0}, }, { .name = "RGB", .opt = 'r', .single = 1, .torgb = _torgb, .fromrgb = _fromrgb, .max = {1.0, 1.0, 1.0, 1.0}, }, }; static char *menu2i[nelem(spaces)+3] = { "snarf", "snarf all", }; static Menu menu2 = { .item = menu2i }; static Color *colors; static int ncolors, curcolor, nchan; static Rectangle srects[3]; static Space *space; static Image *bg; static char hex[12]; static ulong rgba2u(double *rgba) { return D2C(rgba[0])<<24 | D2C(rgba[1])<<16 | D2C(rgba[2])<<8 | D2C(rgba[3]); } static Image * slider(int si, int w) { static Image *s, *sliders[4]; static u8int *b, *buf[4]; double c[4], rgba[4], 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)); if (space->single) { memset(c, 0, 3*sizeof(double)); c[si] = colors[curcolor].v[si]; } dt = space->max[si] / w; mi = c[si] / dt; c[si] = 0.0; for (i = n = 0; i < w; i++, n += 4) { space->torgb(c, rgba); rgba[3] = c[3]; u = rgba2u(rgba); u = setalpha(u, u&0xff); 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(space->max[si], c[si] + dt); } loadimage(s, rect, b, 4*w); return s; } static int color2str(Color *c, char *s) { return nchan > 3 ? sprint(s, "%08lux", c->u) : sprint(s, "%06lux", c->u>>8); } static void redraw(void) { Rectangle r, cr; Image *im; 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, bg, nil, ZP); 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, bg, nil, ZP); 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, bg, nil, ZP); draw(screen, r, colors[curcolor].i, nil, ZP); /* current color in hex */ color2str(&colors[curcolor], hex); r.min.x += Dx(r)/2 - stringwidth(font, hex)/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 [-e] [-", argv0); for (i = 0; i < nelem(spaces); i++) print("%c", spaces[i].opt); print("] [-a] rrggbb[aa] ...\n"); threadexitsall("usage"); } static void loadbg(void) { Rectangle r; u8int *d; int i, j; r = Rect(0, 0, Sliderheight, Sliderheight); d = calloc(1, Sliderheight*Sliderheight); for (i = 0; i < Sliderheight/2; i++) { for (j = 0; j < Sliderheight/2; j++) d[j*Sliderheight+i] = 0xff; } for (; i < Sliderheight; i++) { for (j = Sliderheight/2; j < Sliderheight; j++) d[j*Sliderheight+i] = 0xff; } if ((bg = allocimage(display, r, GREY8, 1, DNofill)) == nil) sysfatal("allocimage: %r"); if (loadimage(bg, r, d, Sliderheight*Sliderheight) < 0) sysfatal("loadimage: %r"); free(d); } static int printcolor(void) { int n; char buf[16]; if (nchan < 4) n = sprint(buf, "%d %06lux\n", curcolor, colors[curcolor].u>>8); else n = sprint(buf, "%d %08lux\n", curcolor, colors[curcolor].u); return write(1, buf, n) == n ? 0 : -1; } static int str2color(char *s, Color *c) { vlong v; char *e; int i; if (strlen(s) != nchan*2) { werrstr("wrong number of components"); return -1; } if ((v = strtoll(s, &e, 16)) == 0 && (e == s || *e || v < 0)) { werrstr("invalid color"); return -1; } if (nchan < 4) { v <<= 8; v |= 0xff; } c->u = v; for (i = 0; i < 4; i++) { c->rgba[i] = (double)((v>>24)&0xff) / 255.0; v <<= 8; } c->v[3] = c->rgba[3]; space->fromrgb(c->rgba, c->v); return 0; } 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; int i, once; ulong u; char buf[16]; space = &spaces[0]; nchan = 3; once = 0; ARGBEGIN{ case 'a': nchan = 4; break; case 'e': once = 1; break; default: space = nil; for (i = 0; i < nelem(spaces); i++) { if (spaces[i].opt == ARGC()) { space = &spaces[i]; break; } } if (space == nil) { fprint(2, "unknown color space '%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 (str2color(argv[i], &colors[i]) != 0) { fprint(2, "'%s': %r\n", argv[i]); usage(); } } for (i = 0; i < nelem(spaces); i++) menu2i[i+2] = spaces[i].name; 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); if (nchan > 3) loadbg(); 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)) { c->v[i] = MIN(space->max[i], (double)(m.xy.x - r.min.x) * space->max[i]/(double)(Dx(r)-1)); c->rgba[3] = c->v[3]; changed: space->torgb(c->v, c->rgba); u = rgba2u(c->rgba); if (c->u != u) { c->u = u; if (!once) printcolor(); } redraw(); goto next; } } for (i = 0; i < ncolors; i++) { if (ptinrect(m.xy, colors[i].r)) { curcolor = i; redraw(); goto next; } } } else if (m.buttons == 2 && (i = menuhit(2, mctl, &menu2, nil)) >= 0) { if (i >= 2) { space = &spaces[i-2]; space->fromrgb(c->rgba, c->v); for (i = 0; i < 3; i++) c->v[i] = MAX(0.0, MIN(space->max[i], c->v[i])); goto changed; } int f; if ((f = open("/dev/snarf", OWRITE)) >= 0) { if (i == 0) { write(f, hex, strlen(hex)); } else { for (i = 0; i < ncolors; i++) { write(f, buf, color2str(&colors[i], buf)); if (i != ncolors-1) write(f, " ", 1); } } close(f); } } else if (m.buttons == 4) { strcpy(buf, hex); if (enter(nchan < 4 ? "rgb:" : "rgba:", buf, sizeof(buf), mctl, kctl, nil) > 0) { u = c->u; if (str2color(buf, c) == 0 && c->u != u) { c->u = ~c->u; /* just for the update to kick in */ goto changed; } } } break; case Cresize: getwindow(display, Refnone); redraw(); break; } } end: if (once) { for (i = 0; i < ncolors; i++) { if (nchan < 4) print("%06lux ", colors[i].u>>8); else print("%08lux ", colors[i].u); } print("\n"); } threadexitsall(nil); }