shithub: pain

ref: 672d39e9b1697da4b955f7a8b692994dfe916be7
dir: /pill/pill.c/

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

#define INTERNAL_HEIGHT 640
#define INTERNAL_WIDTH 640
#define STEPS 3600
#define RADIUS .60
#define MAXSATURATION 1.0
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define RAD2DEG(r_) ((180.0)/PI * (r_))
#define DEG2RAD(r_) (PI/(180.0) * (r_))
#define BACKGROUNDCOLOR 0x22272EFF
#define CIRCLESLICE (2*PI/5)
#define CIRCLESHIFT (PI/2 + CIRCLESLICE/2)
#define CIRCLEDIAMETER (2*PI - CIRCLESLICE)
#define COLORPICKERPADDING .10
#define COLORPICKERRADIUS .25
#define RECTANGLE(w, h) ((Rectangle){(Point){(0),(0)}, (Point){(w),(h)}})

Image * VirtualWindow = nil;
Image * HuePicker = nil;
Image * Gradient = nil;
u32int * RawGradient = nil;
u32int RawGradientSize = 0;
int DrawGradient = 1;
int DrawHuePicker = 1;
int WindowMoved = 1;

Image * Circle = nil;
uint CircleBPL = 0;
u32int * RawCircle = nil;
uint RawCircleSize = 0;
Image * Background = nil;
float SaturationStart = .50;
float HueStart = PI*.35;
float Value = 1.0;
int RunApp = 1;
int UpdateCircle = 1;

int resizeimage(Image * d, Image * s)
{
	int i;
	Point p;
	Image * t, * c;
	float scale;
	Rectangle dr, sr, tr, r;

	dr = rectsubpt(d->r, d->r.min);
	sr = rectsubpt(s->r, s->r.min);

	tr = dr;
	if (Dx(dr) < Dx(sr)) {
		tr.max.x = tr.min.x + Dx(sr);
	}
	if (Dy(dr) < Dy(sr)) {
		tr.max.y = tr.min.y + Dy(sr);
	}

	if ((t = allocimage(display, tr, s->chan, 1, DNofill)) == nil) {
		werrstr("allocimage: %r");
		return -1;
	}

	if ((c = allocimage(display, Rect(0, 0, 1, Dy(tr)), s->chan, 1, DNofill)) == nil)
	{
		werrstr("allocimage: %r");
		freeimage(t);
		return -1;
	}

	if (Dx(tr) > Dx(sr)) {
		scale = ((float)Dx(tr))/((float)Dx(sr));
	} else {
		scale = ((float)Dx(sr))/((float)Dx(tr));
	}
	p = dr.min;
	r.min = tr.min;
	r.max.y = tr.max.y;
	
	i = 0;
	while (r.min.x < tr.max.x) {
		draw(c, c->r, s, nil, p);
		p.x += 1;

		r.min.x = (int)(((float)(i))*scale) + tr.min.x;
		r.max.x = (int)(((float)(i+1))*scale) + tr.min.x;
		draw(t, r, c, nil, ZP);
		i+=1;
	}
	
	freeimage(c);

	if ((c = allocimage(display, Rect(0, 0, Dx(tr), 1), s->chan, 1, DNofill)) == nil)
	{
		werrstr("allocimage: %r");
		freeimage(t);
		return -1;
	}

	if (Dy(tr) > Dy(sr)) {
		scale = ((float)Dy(tr))/((float)Dy(sr));
	} else {
		scale = ((float)Dy(sr))/((float)Dy(tr));
	}

	p = dr.min;
	r.min = tr.min;
	r.max.x = tr.max.x;
	r.max.y = r.min.y + MAX(1.0, scale);

	i = 0;
	while (r.min.y < dr.max.y) {
		draw(c, c->r, t, nil, p);
		p.y += 1;
		
		// Move the draw rectangle for the temporary image scale
		// rectangles to the right
		r.min.y = (int)(((float)(i))*scale) + dr.min.x;
		r.max.y = (int)(((float)(i+1))*scale) + dr.min.x;
		draw(d, r, c, nil, ZP);
		i+=1;
	}

	freeimage(t);
	freeimage(c);
	
	return 1;
}

static unsigned int
hsv2rgb(float h, float s, float v)
{
	// Taken from Computer Graphics: Principle and Practice
	unsigned int rgb;
	float f, p, q, t, r = 0.0, g = 0.0, b = 0.0;
	int i;
	
	h = RAD2DEG(h);
	h /= 60.0;
	i = (int)floor(h);
	f = h - i;
	p = v * (1.0 - s);
	q = v * (1.0 - (s * f));
	t = v * (1.0 - (s * (1.0 - f)));

	switch(i)
	{
		case 0:
			r = v;
			g = t;
			b = p;
			break;
		case 1:
			r = q;
			g = v;
			b = p;
			break;
		case 2:
			r = p;
			g = v;
			b = t;
			break;
		case 3:
			r = p;
			g = q;
			b = v;
			break;
		case 4:
			r = t;
			g = p;
			b = v;
			break;
		case 5:
			r = v;
			g = p;
			b = q;
			break;
		default:
			break;
	}

	rgb = (0xff & (uint)(b*255));
	rgb |= (0xff & (uint)(g*255)) << 8;
	rgb |= (0xff & (uint)(r*255)) << 16;
	return (rgb << 8) | 0xff;
}

static void
initgradient(void)
{
	if (Gradient != nil) {
		freeimage(Gradient);
	}

	if (RawGradient != nil) {
		free(RawGradient);
	}
	
	Gradient = allocimage(display, RECTANGLE(INTERNAL_WIDTH, INTERNAL_HEIGHT), RGBA32, 1, DTransparent);
	if(Gradient == nil) {
		sysfatal("initgradient: %r");
	}

	RawGradientSize = bytesperline(Gradient->r, Gradient->depth) * Dy(Gradient->r);
	RawGradient = calloc(RawGradientSize, 1);
	if (RawGradient == nil) {
		sysfatal("calloc: out of memory");
	}
}

static void
setgradienthue(void)
{
	float t, i, l, s, u, ct, st;
	Point c, e;
	int rc, dx, dy;

	dx = Dx(Gradient->r);
	dy = Dy(Gradient->r);
	c.x = dx/2;
	c.y = dy/2;

	l = MIN(dx, dy);
	l *= RADIUS/2.0;
	
	i = 1/(l*2);
	for (t = 0.0; t < CIRCLEDIAMETER; t += i) {
		u = t/CIRCLEDIAMETER;
		ct = cos(t + CIRCLESHIFT);
		st = sin(t + CIRCLESHIFT);
		for (s = 0; s < l; s += 0.1) {
			e.x = c.x + s * ct;
			e.y = c.y + s * st;
			RawGradient[dx * e.y + e.x] = hsv2rgb(
				HueStart + u, 
				(SaturationStart)+(s/l)*(MAXSATURATION - SaturationStart),
				Value);
		}
	}

	rc = loadimage(Gradient, Gradient->r, (uchar *)RawGradient, RawGradientSize);
	if (rc < 0) {
		sysfatal("loadimage: %r");
	}
}

static void
inithuepicker(void)
{
	int rc, dx, dy, h;
	u32int * raw, rawSize;
	float t, i, s, u, g, st, ct, l;
	Point c;

	if (HuePicker != nil) {
		freeimage(HuePicker);
	}
	
	HuePicker = allocimage(display, RECTANGLE(INTERNAL_WIDTH, INTERNAL_HEIGHT), RGBA32, 1, DTransparent);
	if(HuePicker == nil) {
		sysfatal("inithuepicker: %r");
	}

	rawSize = bytesperline(HuePicker->r, HuePicker->depth) * Dy(HuePicker->r);
	raw = calloc(rawSize, 1);
	if (raw == nil) {
		sysfatal("calloc: out of memory");
	}

	dx = Dx(HuePicker->r);
	dy = Dy(HuePicker->r);
	c.x = dx/2;
	c.y = dy/2;

	l = MIN(dx, dy);
	l *= RADIUS/2.0;

	s = l * (1+COLORPICKERPADDING);
	u = s + (l*COLORPICKERRADIUS);
	i = 1/(u*2);
	for (t = .0; t < 2*PI; t+=i) {
		ct = cos(t);
		st = sin(t);
		
		h = hsv2rgb(t, 1.0, 1.0);
		for (g = s; g < u; g++) {
				raw[dx * (c.y + ((int)(g * st))) + (c.x + ((int)(g * ct)))] = h;
		}
	}

	rc = loadimage(HuePicker, HuePicker->r, (uchar *)raw, rawSize);
	if (rc < 0) {
		sysfatal("loadimage: %r");
	}

	if (raw != nil) {
		free(raw);
	}
}

static void
drawcanvas(void)
{
	print("VirtualWindow: %R\n", VirtualWindow->r);
	print("screen: %R\n", screen->r);
	draw(screen, screen->r, Background, nil, ZP);
	draw(screen, screen->r, VirtualWindow, nil, ZP);
}

void
setbackground(ulong col)
{	
	if (Background != nil)
		freeimage(Background);

	Background = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, col);
	if (Background == nil)
		sysfatal("setbackground: %r");
}

static void
initcanvas(void)
{
	int rc;

	if (VirtualWindow != nil) {
		freeimage(VirtualWindow);
	}

	VirtualWindow = allocimage(display, screen->r, RGBA32, 1, DTransparent);
	if (VirtualWindow == nil) {
		sysfatal("initcanvas: %r");
	}

	rc = resizeimage(VirtualWindow, HuePicker);
	if (rc < 0) {
		sysfatal("resizeimage: %r");
	}

	setbackground(BACKGROUNDCOLOR);
	drawcanvas();
}

void
eresized(int)
{
	if(getwindow(display, Refnone) < 0)
		sysfatal("getwindow: %r");
	initcanvas();
}

void
main(int argc, char * argv[])
{
	Event e;

	USED(argc);
	USED(argv);
	
	if (initdraw(nil, nil, "pill") < 0)
		sysfatal("initdraw: %r\n");

	inithuepicker();
	initgradient();
	setgradienthue();

	einit(Emouse | Ekeyboard);
	initcanvas();
	for (;RunApp;) {
		switch(event(&e)) {
		case Ekeyboard:
			if (e.kbdc == 'q') {
				RunApp = 0;
			}
			break;
		}
	}
}