ref: f8d65f9f1c74b101cccfdc08250e31c5e554b899
dir: /eentercolor.c/
#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;
}
static Image *colors = nil;
static 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);
}
static 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;
}
static double hue = 0.;
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;
Rgb rgb;
Hsv hsv;
Image *blitimg;
Image *selcol;
char buf[1 + 3 + 1 + 3 + 1 + 3 + 1 + 1]; /* (XXX,XXX,XXX) */
if (!ask)
ask = "color:";
fy = stringsize(font, "M").y;
buf[0] = 0;
if (!huegrad)
inithuegrad();
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.s = (double)xy.x/100;
hsv.v = 1.-(double)xy.y/100;
Setcolor:
hsv.h = hue;
rgb = hsv2rgb(hsv);
Setpixel:
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);
goto Setcolor;
}
if (ptinrect(ev.mouse.xy, ro)) {
if (!eenter("color:", buf, sizeof buf, &ev.mouse))
break;
rgb = parsergb(buf);
goto Setpixel;
}
}
}
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;
}