shithub: picker

ref: 3897a967274ce9d36bb9ea10432bb7bec5de8b16
dir: /picker.c/

View raw version
#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 max[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,
		.max = {360.0, 100.0, 100.0},
	},
	{
		.name = "HPLuv",
		.opt = 'l',
		.torgb = _hpluv2rgb,
		.fromrgb = _rgb2hpluv,
		.max = {360.0, 100.0, 100.0},
	},
	{
		.name = "RGB",
		.opt = 'r',
		.torgb = _rgbto,
		.fromrgb = _rgbfrom,
		.max = {1.0, 1.0, 1.0},
	},
};

static double *colors;
static int ncolors, alpha;
static Mode *mode;

static Image *
gradient(double *color, int w, int e)
{
	Rectangle rect;
	u8int *data;
	Image *im;
	double c[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");

	memmove(c, color, sizeof(c));
	data = malloc(3*w);
	x = mode->max[e] / w;
	mi = c[e] / x;
	c[e] = 0.0;
	for (i = 0; i < w; i++) {
		mode->torgb(c, &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];
		}
		c[e] += x;
		if (c[e] > mode->max[e])
			c[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

	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], (m.xy.x - screen->r.min.x) * mode->max[i]/Dx(screen->r));
						redraw();
						break;
					}
					p.y += dh;
				}
			}
			break;

		case Cresize:
			getwindow(display, Refnone);
			redraw();
			break;
		}
	}

end:
	threadexitsall(nil);
}