shithub: blie

Download patch

ref: f5d679a1fea46f5f6128679ed766ec0cace95c79
parent: 2d1e449a474444fa3288c56937a033c4f5f019f7
author: sirjofri <sirjofri@sirjofri.de>
date: Thu Aug 15 15:57:39 EDT 2024

implement eentercolor (generic color picker), adds initlayer editor function

--- a/blie.c
+++ b/blie.c
@@ -109,6 +109,13 @@
 	dstate.curoffset = off;
 }
 
+static void
+initlayer(Layer *l, int, int, void*)
+{
+	if (l->editor && l->editor->initlayer)
+		l->editor->initlayer(l);
+}
+
 int
 loadfile(void)
 {
@@ -128,6 +135,8 @@
 	while (s = Brdstr(b, '\n', 1))
 		addlayer(s);
 	
+	foreachlayer(initlayer, nil);
+	
 	return 1;
 }
 
@@ -350,6 +359,7 @@
 	Point xy;
 	Mouse *m = &ev.mouse;
 	xy = m->xy;
+	vstate.lastmouse = ev.mouse;
 	vstate.mousepos = xy;
 	n = Rnil;
 	if (ptinrect(m->xy, panels.layers->r)) {
--- a/blie.h
+++ b/blie.h
@@ -2,7 +2,6 @@
 typedef struct Editor Editor;
 typedef struct Vdata Vdata;
 typedef struct Vstate Vstate;
-typedef struct Color Color;
 
 extern Vdata vdata;
 extern Vstate vstate;
@@ -9,16 +8,9 @@
 extern int bliedebug;
 extern int headless;
 
-struct Color {
-	float r;
-	float g;
-	float b;
-	float a;
-};
+/* could be extracted to libdraw */
+int eentercolor(char*, ulong*, Mouse*);
 
-Color askcolor(char*);
-ulong color2value(Color);
-
 struct Layer {
 	char *name;
 	char *label;
@@ -62,6 +54,7 @@
 	int mdirty;
 	int maxquality;
 	Point mousepos;
+	Mouse lastmouse;
 };
 
 void setmodedirty(int);
@@ -81,6 +74,7 @@
 struct Editor {
 	char *name;
 	void (*init)(void);
+	void (*initlayer)(Layer*);
 	Memimage *(*composite)(Layer*, Memimage*);
 	Memimage *(*raw)(Layer*);
 	Memimage *(*mask)(Layer*);
--- /dev/null
+++ b/eentercolor.c
@@ -1,0 +1,341 @@
+#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;
+}
--- a/mkfile
+++ b/mkfile
@@ -7,6 +7,7 @@
 	editor.$O\
 	layer.$O\
 	sample.$O\
+	eentercolor.$O\
 	util.$O\
 	p9image.$O\
 
--- a/p9image.c
+++ b/p9image.c
@@ -220,8 +220,6 @@
 p9composite(Layer *l, Memimage *img)
 {
 	Data *d;
-	
-	p9init(l);
 	d = (Data*)l->data;
 	
 	if (!img) {
@@ -238,7 +236,6 @@
 p9raw(Layer *l)
 {
 	Data *d;
-	p9init(l);
 	d = (Data*)l->data;
 	return d->img;
 }
@@ -247,7 +244,6 @@
 p9mask(Layer *l)
 {
 	Data *d;
-	p9init(l);
 	d = (Data*)l->data;
 	return d->mask;
 }
@@ -258,7 +254,6 @@
 	Data *data;
 	Memimage *mi;
 	
-	p9init(l);
 	data = (Data*)l->data;
 	
 	if (!i)
@@ -358,7 +353,6 @@
 static int
 p9savedata(Layer *l)
 {
-	p9init(l);
 	return 1;
 }
 
@@ -371,7 +365,6 @@
 	Brush *brush;
 	Memimage *color;
 	
-	p9init(l);
 	d = (Data*)l->data;
 	
 	if (!buttons)
@@ -413,8 +406,6 @@
 static Redrawwin
 p9drawinput(Layer *l, int e, Event ev)
 {
-	p9init(l);
-	
 	switch (e) {
 	case Ekeyboard:
 		break;
@@ -446,15 +437,14 @@
 static void
 setcolor(int num)
 {
-	Color c;
+	ulong col;
 	
 	if (num < 0 || num >= NUMCELLS)
 		return;
 	
-	c = askcolor("color");
-	if (c.r < 0)
+	if (!eentercolor("color", &col, &vstate.lastmouse))
 		return;
-	tstate.colordata[num] = color2value(c);
+	tstate.colordata[num] = col;
 }
 
 static void
@@ -482,7 +472,6 @@
 p9toolinput(Layer *l, int e, Event ev)
 {
 	Point xy;
-	p9init(l);
 	if (e != Emouse)
 		return Rnil;
 	
@@ -550,6 +539,7 @@
 Editor p9image = {
 	.name = "p9img",
 	.init = p9initialize,
+	.initlayer = p9init,
 	.raw = p9raw,
 	.mask = p9mask,
 	.overlay = p9overlay,
--- a/util.c
+++ b/util.c
@@ -59,81 +59,3 @@
 	xy.y = xy.y / vstate.zoom;
 	return xy;
 }
-
-ulong
-color2value(Color c)
-{
-	int r, g, b, a;
-	r = c.r * 256;
-	g = c.g * 256;
-	b = c.b * 256;
-	a = c.a * 256;
-	return r<<24 | g<<16 | b<<8 | a;
-}
-
-Image **askcolimage;
-char *askcolprompt;
-
-static void
-askcolorredraw(Image *i)
-{
-	border(i, i->r, 1, display->black, ZP);
-	string(i, addpt(i->r.min, Pt(10, 0)), display->black, ZP, font, askcolprompt);
-}
-
-Color
-askcolor(char *prompt)
-{
-	Event ev;
-	int e;
-	Color c;
-	Rectangle r;
-	Image *col = nil;
-	ulong val;
-	Point xy;
-	
-	r.min = vstate.mousepos;
-	r.max = addpt(r.min, Pt(100, 100+vdata.fontheight));
-	
-	askcolprompt = prompt;
-	askcolimage = reqwin(r, 0xccccccff, askcolorredraw);
-	r.min.y += vdata.fontheight;
-	
-	for (;;) {
-		e = event(&ev);
-		switch (e) {
-		case Ekeyboard:
-			if (ev.kbdc == Kesc || ev.kbdc == 'q')
-				goto Abort;
-			else if (ev.kbdc == '\n')
-				goto Accept;
-			break;
-		case Emouse:
-			if (!ev.mouse.buttons)
-				break;
-			if (!ptinrect(ev.mouse.xy, r))
-				break;
-			xy = subpt(ev.mouse.xy, (*askcolimage)->r.min);
-			xy.y -= vdata.fontheight;
-			c.r = (double)xy.x / 100;
-			c.g = (double)xy.y / 100;
-			c.b = 0.;
-			c.a = 1.;
-			val = color2value(c);
-			if (col)
-				freeimage(col);
-			col = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, val);
-			border(*askcolimage, (*askcolimage)->r, 2, col, ZP);
-//			fprint(2, "col: %f %f %f %f\n", c.r, c.g, c.b, c.a);
-			break;
-		}
-	}
-
-Abort:
-	c.r = -1.;
-Accept:
-	setmodedirty(1);
-	unreqwin(askcolimage);
-	askcolimage = nil;
-	return c;
-}