shithub: blie

ref: 807afae69b7551b681b638d64f1099af5c672fdd
dir: /eentercolor.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>

/* KNOWN BUGS

First pixel row of the image doesn't yield proper values.

*/

typedef struct Hsv Hsv;
typedef struct Rgb Rgb;
struct Hsv {
	double h;
	double s;
	double v;
};
struct Rgb {
	double r;
	double g;
	double b;
};

static Rgb
hsv2rgb(Hsv hsv)
{
	Rgb r;
	int i;
	double ff, p, q, t;
	
	if (hsv.s <= 0.) {
		r.r = hsv.v;
		r.g = hsv.v;
		r.b = hsv.v;
		return r;
	}
	hsv.h *= 360.;
	if (hsv.h >= 360.0)
		hsv.h = 0.;
	hsv.h /= 60.;
	i = hsv.h;
	ff = hsv.h - i;
	p = hsv.v * (1.0 - hsv.s);
	q = hsv.v * (1.0 - (hsv.s * ff));
	t = hsv.v * (1.0 - (hsv.s * (1.0 - ff)));
	
	switch (i) {
	case 0:
		r.r = hsv.v;
		r.g = t;
		r.b = p;
		break;
	case 1:
		r.r = q;
		r.g = hsv.v;
		r.b = p;
		break;
	case 2:
		r.r = p;
		r.g = hsv.v;
		r.b = t;
		break;
	case 3:
		r.r = p;
		r.g = q;
		r.b = hsv.v;
		break;
	case 4:
		r.r = t;
		r.g = p;
		r.b = hsv.v;
		break;
	case 5:
	default:
		r.r = hsv.v;
		r.g = p;
		r.b = q;
		break;
	}
	return r;
}

Image *colors = nil;
uchar *data;

static void
initimage(double hue)
{
	uchar *d;
	int x, y, n;
	Hsv hsv;
	Rgb rgb;
	
	n = 100 * 100 * 4*sizeof(uchar);
	if (!data)
		data = malloc(n);
	if (!colors)
		colors = allocimage(display, Rect(0, 0, 100, 100), RGBA32, 0, DNofill);
	if (!colors || !data)
		sysfatal("out of memory: %r");
	
	hsv.h = hue;
	for (y = 0; y < 100; y++)
		for (x = 0; x < 100; x++) {
			d = data + (y*100 + x)*4*sizeof(uchar);
			
			hsv.s = (double)x/100;
			hsv.v = 1.-(double)y/100;
			rgb = hsv2rgb(hsv);
			
			d[0] = 255;
			d[1] = rgb.b * 255;
			d[2] = rgb.g * 255;
			d[3] = rgb.r * 255;
		}
	
	loadimage(colors, colors->r, data, n);
}

Image *huegrad = nil;

static void
inithuegrad(void)
{
	uchar *dat, *d;
	int x, n, p;
	Rgb rgb;
	Hsv hsv;
	
	if (!huegrad)
		huegrad = allocimage(display, Rect(0, 0, 100, 1), RGBA32, 1, DNofill);
	if (!huegrad)
		sysfatal("out of memory: %r");
	p = huegrad->depth/8;
	n = 100 * p;
	dat = malloc(n);
	if (!dat)
		sysfatal("out of memory: %r");
	
	hsv.v = 1.;
	hsv.s = 1.;
	for (x = 0; x < 100; x++) {
		d = dat + x*p;
		hsv.h = (double)x/100;
		rgb = hsv2rgb(hsv);
		
		d[0] = 255;
		d[1] = rgb.b * 255;
		d[2] = rgb.g * 255;
		d[3] = rgb.r * 255;
	}
	loadimage(huegrad, huegrad->r, dat, n);
	free(dat);
}

static Rgb
parsergb(char *str)
{
	Rgb rgb;
	ulong c;
	uchar *p;
	char *s;
	
	/* valid formats are: (spaces ignored)
	   (RRR,GGG,BBB)
	   RRR,GGG,BBB
	   #RRGGBB
	 */
	
	while (*str == ' ' || *str == '\t')
		str++;
	
	switch (*str) {
	case '#':
		str++;
		c = strtoul(str, nil, 16);
		p = (uchar*)&c;
		rgb.r = (double)p[2] / 255;
		rgb.g = (double)p[1] / 255;
		rgb.b = (double)p[0] / 255;
		return rgb;
	case '(':
		str++;
	}
	
	rgb.r = (double)strtol(str, &s, 10) / 255;
	str = s+1;
	rgb.g = (double)strtol(str, &s, 10) / 255;
	str = s+1;
	rgb.b = (double)strtol(str, &s, 10) / 255;
	return rgb;
}

int
eentercolor(char *ask, ulong *out, Mouse *m)
{
	Event ev;
	int e;
	Rectangle r, ri, rh, rc, ro;
	Point askp, outp;
	Point xy;
	uchar *pixel;
	int fy;
	double hue;
	Rgb rgb;
	Hsv hsv;
	Image *blitimg;
	Image *selcol;
	char buf[1 + 3 + 1 + 3 + 1 + 3 + 1 + 1]; /* (XXX,XXX,XXX) */
	
	hue = 0.;
	
	if (!ask)
		ask = "color:";
	
	fy = stringsize(font, "M").y;
	buf[0] = 0;
	
	if (!huegrad)
		inithuegrad();
	if (!colors)
		initimage(hue);
	
	pixel = (uchar*)out;
	
	/* dimensions of full box */
	r.min = m->xy;
	r.max = addpt(r.min, Pt(110, 110));
	r.max.y += 2 * (fy + 10); /* two lines of text */
	r.max.y += 15; /* value gradient */
	
	/* dimensions of inline image */
	ri = r;
	ri.min.y += fy + 10;
	ri.min.x += 5;
	ri.min.y += 5;
	ri.max.x = ri.min.x + 100;
	ri.max.y = ri.min.y + 100;
	
	/* dimensions of hue image */
	rh.min.x = ri.min.x;
	rh.min.y = ri.max.y;
	rh.max.x = ri.max.x;
	rh.max.y = rh.min.y + 15;
	
	/* dimensions of selected color */
	rc.min.x = rh.min.x;
	rc.min.y = rh.max.y;
	rc.max.x = ri.max.x;
	rc.max.y = rc.min.y + 15;
	
	/* position of ask string */
	askp = r.min;
	askp.x += 5;
	askp.y += 5;
	
	/* position of out string */
	outp = rc.min;
	outp.y += 15 + 5;
	ro.min = outp;
	ro.max = addpt(outp, Pt(100, fy));
	
	r.max.x = r.min.x + 110;
	r.max.y = outp.y + fy + 10;
	
	blitimg = allocimage(display, r, screen->chan, 0, DNofill);
	draw(blitimg, blitimg->r, screen, nil, blitimg->r.min);
	
	selcol = nil;
	for (;;) {
		/* redraw routines */
		draw(screen, r, display->white, nil, ZP);
		border(screen, r, 1, display->black, ZP);
		string(screen, askp, display->black, ZP, font, ask);
		string(screen, outp, display->black, ZP, font, buf);
		draw(screen, ri, colors, nil, ZP);
		draw(screen, rh, huegrad, nil, ZP);
		if (selcol)
			freeimage(selcol);
		selcol = allocimage(display, rc, RGBA32, 1, *out);
		draw(screen, rc, selcol, nil, ZP);
		
		/* event handling */
		e = event(&ev);
		switch (e) {
		case Ekeyboard:
			if (ev.kbdc == Kesc || ev.kbdc == 'q')
				goto Abort;
			if (ev.kbdc == '\n')
				goto Accept;
			break;
		case Emouse:
			if (!ev.mouse.buttons)
				break;
			if (ptinrect(ev.mouse.xy, ri)) {
				xy = subpt(ev.mouse.xy, ri.min);
				hsv.h = hue;
				hsv.s = (double)xy.x/100;
				hsv.v = 1.-(double)xy.y/100;
				rgb = hsv2rgb(hsv);
				pixel[3] = rgb.r * 256;
				pixel[2] = rgb.g * 256;
				pixel[1] = rgb.b * 256;
				pixel[0] = 255;
				snprint(buf, sizeof buf, "%3d,%3d,%3d", pixel[3], pixel[2], pixel[1]);
				break;
			}
			if (ptinrect(ev.mouse.xy, rh)) {
				xy.x = ev.mouse.xy.x - rh.min.x;
				hue = ((double)xy.x / 100);
				initimage(hue);
				break;
			}
			if (ptinrect(ev.mouse.xy, ro)) {
				if (!eenter("color:", buf, sizeof buf, &ev.mouse))
					break;
				rgb = parsergb(buf);
				pixel[3] = rgb.r * 255;
				pixel[2] = rgb.g * 255;
				pixel[1] = rgb.b * 255;
				pixel[0] = 255;
				snprint(buf, sizeof buf, "%3d,%3d,%3d", pixel[3], pixel[2], pixel[1]);
				break;
			}
		}
	}
Abort:
	draw(screen, blitimg->r, blitimg, nil, blitimg->r.min);
	freeimage(blitimg);
	if (selcol)
		freeimage(selcol);
	return 0;
Accept:
	draw(screen, blitimg->r, blitimg, nil, blitimg->r.min);
	freeimage(blitimg);
	if (selcol)
		freeimage(selcol);
	return 1;
}