ref: 33a7fcd3976241e3e22a69f24bff13e5999e2aa1
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))
enum
{
Ckey,
Cmouse,
Cresize,
Numchan,
};
typedef struct Mode Mode;
struct Mode {
char *name;
char opt;
void (*torgb)(double *x, double *r, double *g, double *b);
void (*fromrgb)(double r, double g, double b, double *x);
double min[3], max[3], init[3];
};
static void
_hsluv2rgb(double *x, double *r, double *g, double *b)
{
hsluv2rgb(x[0], x[1], x[2], r, g, b);
}
static void
_rgb2hsluv(double r, double g, double b, double *x)
{
rgb2hsluv(r, g, b, x+0, x+1, x+2);
}
static void
_hpluv2rgb(double *x, double *r, double *g, double *b)
{
hpluv2rgb(x[0], x[1], x[2], r, g, b);
}
static void
_rgb2hpluv(double r, double g, double b, double *x)
{
rgb2hpluv(r, g, b, x+0, x+1, x+2);
}
static void
_rgbto(double *x, double *r, double *g, double *b)
{
*r = x[0];
*g = x[1];
*b = x[2];
}
static void
_rgbfrom(double r, double g, double b, double *x)
{
x[0] = r;
x[1] = g;
x[2] = b;
}
static Mode modes[] = {
{
.name = "HSLuv",
.opt = 's',
.torgb = _hsluv2rgb,
.fromrgb = _rgb2hsluv,
.min = {0.0, 0.0, 0.0},
.max = {360.0, 100.0, 100.0},
.init = {0.0, 100.0, 50.0},
},
{
.name = "HPLuv",
.opt = 'l',
.torgb = _hpluv2rgb,
.fromrgb = _rgb2hpluv,
.min = {0.0, 0.0, 0.0},
.max = {360.0, 100.0, 100.0},
.init = {0.0, 100.0, 50.0},
},
{
.name = "RGB",
.opt = 'r',
.torgb = _rgbto,
.fromrgb = _rgbfrom,
.min = {0.0, 0.0, 0.0},
.max = {1.0, 1.0, 1.0},
.init = {0.5, 0.5, 0.5},
},
};
static double *colors;
static int ncolors, alpha;
static Mode *mode;
static Image *
gradient(double *col_, int w, int e)
{
Rectangle rect;
u8int *data;
Image *im;
double color[3];
double r, g, b, x;
int i, mi;
rect = Rect(0, 0, w, 1);
if ((im = allocimage(display, rect, BGR24, 1, DNofill)) == nil)
sysfatal("allocimage: %r");
color[0] = col_[0];
color[1] = col_[1];
color[2] = col_[2];
data = malloc(3*w);
x = (mode->max[e] - mode->min[e]) / w;
mi = (color[e] - mode->min[e]) / x;
color[e] = mode->min[e];
for (i = 0; i < w; i++) {
mode->torgb(color, &r, &g, &b);
data[i*3+0] = r*255.0;
data[i*3+1] = g*255.0;
data[i*3+2] = b*255.0;
if (mi == i) {
data[i*3+0] = ~data[i*3+0];
data[i*3+1] = ~data[i*3+1];
data[i*3+2] = ~data[i*3+2];
}
color[e] += x;
if (color[e] > mode->max[e])
color[e] = mode->max[e];
}
loadimage(im, rect, data, 3*w);
free(data);
return im;
}
static void
redraw(void)
{
Rectangle re;
int dh, i;
Image *im;
double r, g, b;
ulong u;
char hex[8];
lockdisplay(display);
//draw(screen, screen->r, display->white, nil, ZP);
re = screen->r;
dh = Dy(re) / 4;
re.max.y = re.min.y + dh;
for (i = 0; i < 3; i++) {
im = gradient(&colors[4*0], Dx(screen->r), i);
draw(screen, re, im, nil, ZP);
freeimage(im);
re.min.y += dh;
re.max.y += dh;
}
mode->torgb(&colors[4*0], &r, &g, &b);
u = (int)(r*255.0)<<24 | (int)(r*255.0)<<24 | (int)(g*255.0)<<16 | (int)(b*255.0)<<8 | 0xff;
im = allocimage(display, Rect(0, 0, 1, 1), BGR24, 1, u);
draw(screen, re, im, nil, ZP);
freeimage(im);
im = allocimage(display, Rect(0, 0, 1, 1), BGR24, 1, ~u);
sprint(hex, "#%06lux", u>>8);
re.min.x += Dx(re)/2 - 7*stringwidth(font, "#")/2;
re.max.x += dh/2 - font->height/2;
string(screen, re.min, im, ZP, font, hex);
freeimage(im);
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("] 0xrrggbbaa ...\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 },
};
char *s;
vlong v;
int i;
mode = &modes[0];
ARGBEGIN{
case 'a':
alpha = 1;
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
//setfcr(FPRZ|FPPDBL);
ncolors = argc;
if (ncolors < 1) {
fprint(2, "no colors specified\n");
usage();
}
colors = calloc(ncolors, 4*sizeof(double));
for (i = 0; i < ncolors; i++) {
double r, g, b;
if ((v = strtoll(argv[i], &s, 0)) == 0 && (s == argv[i] || *s || v > 0xffffffff || v < 0)) {
fprint(2, "invalid color '%s'\n", argv[i]);
usage();
}
mode->fromrgb(
(double)((v>>24)&0xff) / 255.0,
(double)((v>>16)&0xff) / 255.0,
(double)((v>>8)&0xff) / 255.0,
&colors[i*4]
);
colors[i*4+3] = (double)(v&0xff) / 255.0;
mode->torgb(&colors[i*4], &r, &g, &b);
}
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 (;;) {
switch (alt(a)) {
case -1:
goto end;
case Ckey:
switch (r) {
case Kdel:
goto end;
}
break;
case Cmouse:
if (m.buttons == 1) {
Point p;
int dh;
p = screen->r.min;
dh = Dy(screen->r)/4;
m.xy.x = MAX(screen->r.min.x, MIN(screen->r.max.x, m.xy.x));
for (i = 0; i < 3; i++) {
if (m.xy.y >= p.y && m.xy.y < p.y+dh) {
colors[4*0 + i] = MIN(mode->max[i], MAX(mode->min[i], mode->min[i] + (m.xy.x - screen->r.min.x) * (mode->max[i] - mode->min[i])/Dx(screen->r)));
redraw();
break;
}
p.y += dh;
}
}
break;
case Cresize:
getwindow(display, Refnone);
redraw();
break;
}
}
end:
threadexitsall(nil);
}