ref: fe07e6a3a0099fdd447919004e6b65912de30bd8
dir: /pill/pill.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#define INTERNAL_HEIGHT 480
#define INTERNAL_WIDTH 480
#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 INNERBORDERRADIUS (COLORPICKERPADDING + COLORPICKERRADIUS)
#define RECTANGLE(w, h) ((Rectangle){(Point){(0),(0)}, (Point){(w),(h)}})
#define COLORINDICATORSIZE 0.15
#define VALUEINCREASE 0.05
#define UPDATEDELAY 100
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 = .0;
float HueStart = PI*.35;
float Value = 1.0;
float Hue = 0.f;
float Saturation = 1.0;
int RunApp = 1;
Point MouseGlobalPosition;
Point MouseRelativePosition;
double MouseAngle;
double MouseRadius;
double MouseHueRadiusStart;
double MouseHueRadiusEnd;
int MouseInHue;
int MouseInGradient;
u32int Color = DNofill;
u32int ColorUnderMouse;
int UpdateCanvas = 1;
int ShouldDraw = 1;
int UpdateGradient = 1;
ulong DrawAt = 0;
ulong
msec(void)
{
return (ulong)(nsec()/1000000);
}
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 (Dy(dr) < Dy(sr)) {
tr.max.y = tr.min.y + Dy(sr);
}
if ((t = allocimage(display, tr, s->chan, 1, DTransparent)) == 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;
}
scale = ((float)Dx(sr))/((float)Dx(tr));
p = ZP;
r.min = tr.min;
r.max.y = tr.max.y;
i = 0;
while (i < Dx(tr)) {
p.x = (int)(((float)i)*scale);
drawop(c, c->r, s, nil, p, S);
r.min.x = i + tr.min.x;
r.max.x = r.min.x + 1;
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;
}
scale = ((float)Dy(sr))/((float)Dy(dr));
p = ZP;
r.min = d->r.min;
r.max.x = d->r.max.x;
i = 0;
while (i < Dy(dr)) {
p.y = (int)(((float)i)*scale);
drawop(c, c->r, t, nil, p, S);
// Move the draw rectangle for the temporary image scale
// rectangles to the right
r.min.y = i + d->r.min.y;
r.max.y = r.min.y + 1;
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, h;
Point c, e;
int rc, dx, dy;
if (UpdateGradient == 0) {
return;
}
dx = Dx(Gradient->r);
dy = Dy(Gradient->r);
c.x = dx/2 + 1;
c.y = dy/2 + 1;
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;
h = HueStart + u;
if (h > 2*PI) {
h = h - 2*PI;
}
RawGradient[dx * e.y + e.x] = hsv2rgb(
h,
(SaturationStart)+(s/l)*(MAXSATURATION - SaturationStart),
Value);
}
}
rc = loadimage(Gradient, Gradient->r, (uchar *)RawGradient, RawGradientSize);
if (rc < 0) {
sysfatal("loadimage: %r");
}
UpdateGradient = 0;
}
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
updatecolorindicator(void)
{
Image * c;
Rectangle r;
if (VirtualWindow == nil) {
return;
}
c = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, Color);
if (c == nil) {
sysfatal("allocimage: %r");
}
r = RECTANGLE( ((float)Dx(screen->r)) * COLORINDICATORSIZE,
((float)Dx(screen->r)) * COLORINDICATORSIZE);
r.min = screen->r.min;
r.max = addpt(r.max, r.min);
draw(screen, r, c, nil, ZP);
freeimage(c);
}
u32int
pickcolor(void)
{
Image * c;
u32int colors[4];
if (VirtualWindow == nil) {
return DWhite;
}
//print("r: %R\n", r);
c = allocimage(display, RECTANGLE(1,1), RGBA32, 1, DNofill);
if (c == nil) {
sysfatal("allocimage: %r");
}
drawop(c, c->r, VirtualWindow, nil, MouseGlobalPosition, S);
unloadimage(c, c->r, (uchar*)colors, sizeof(colors));
freeimage(c);
return colors[0];
}
static void
drawcanvas(void)
{
if (ShouldDraw == 0) {
return;
}
draw(screen, screen->r, Background, nil, ZP);
draw(screen, screen->r, VirtualWindow, nil, screen->r.min);
updatecolorindicator();
ShouldDraw = 0;
}
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
updatecanvas(void)
{
int rc;
if (VirtualWindow == nil ||
Dx(VirtualWindow->r) != Dx(screen->r) ||
Dy(VirtualWindow->r) != Dx(screen->r) || UpdateCanvas) {
if (VirtualWindow != nil) {
freeimage(VirtualWindow);
}
VirtualWindow = allocimage(display, screen->r, RGBA32, 0, DTransparent);
if (VirtualWindow == nil) {
sysfatal("initcanvas: %r");
}
rc = resizeimage(VirtualWindow, HuePicker);
if (rc < 0) {
sysfatal("resizeimage: %r");
}
rc = resizeimage(VirtualWindow, Gradient);
if (rc < 0) {
sysfatal("resizeimage: %r");
}
}
}
static void
initcanvas(void)
{
setbackground(BACKGROUNDCOLOR);
}
void
handlemouse(Mouse mouse)
{
Point c;
double x, y, v;
MouseGlobalPosition = mouse.xy;
MouseRelativePosition = subpt(mouse.xy, screen->r.min);
c = MouseRelativePosition;
c.x -= Dx(screen->r)/2.0;
c.y -= Dy(screen->r)/2;
x = ((double)c.x)/(Dx(screen->r)/2);
y = ((double)c.y)/(Dy(screen->r)/2);
MouseRadius = sqrt(pow(x, 2) + pow(y, 2));
MouseAngle = atan2(-y, x);
Hue = -MouseAngle + 2*PI;
if (Hue > 2*PI) {
Hue -= 2*PI;
}
MouseHueRadiusStart = RADIUS * (1+COLORPICKERPADDING);
MouseHueRadiusEnd = MouseHueRadiusStart + (RADIUS * COLORPICKERRADIUS);
MouseInHue = MouseRadius >= MouseHueRadiusStart && MouseRadius <= MouseHueRadiusEnd;
MouseInGradient = MouseRadius <= RADIUS;
ColorUnderMouse = pickcolor();
if (MouseInGradient && mouse.buttons & 0x01) {
if (-MouseAngle < CIRCLESHIFT &&
-MouseAngle >= (CIRCLESHIFT - CIRCLESLICE)) {
Color = DNofill;
} else {
Color = ColorUnderMouse;
}
print("%08ux\n", Color);
ShouldDraw = 1;
DrawAt = msec();
}
if (MouseInHue && mouse.buttons & 0x01) {
HueStart = Hue;
UpdateGradient = 1;
UpdateCanvas = 1;
ShouldDraw = 1;
DrawAt = msec() + UPDATEDELAY;
}
v = Value;
if (mouse.buttons & 0x08) {
v += VALUEINCREASE;
}
if (mouse.buttons & 0x16) {
v -= VALUEINCREASE;
}
if (v != Value) {
if (v > 1.0) {
v = 1.0;
}
if (v < 0.0) {
v = 0.0;
}
Value = v;
UpdateGradient = 1;
ShouldDraw = 1;
UpdateCanvas = 1;
DrawAt = msec() + UPDATEDELAY;
}
}
void
eresized(int)
{
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
initcanvas();
ShouldDraw = 1;
DrawAt = msec();
}
void
main(int argc, char * argv[])
{
Event e;
USED(argc);
USED(argv);
if (initdraw(nil, nil, "pill") < 0)
sysfatal("initdraw: %r\n");
inithuepicker();
initgradient();
einit(Emouse | Ekeyboard);
initcanvas();
etimer(0, UPDATEDELAY);
DrawAt = msec();
for (;RunApp;) {
if (DrawAt > 0 && DrawAt <= msec() ) {
setgradienthue();
updatecanvas();
drawcanvas();
DrawAt = 0;
}
switch(event(&e)) {
case Ekeyboard:
if (e.kbdc == 'q') {
RunApp = 0;
}
break;
case Emouse:
handlemouse(e.mouse);
break;
case 0:
break;
}
}
}