shithub: blie

Download patch

ref: 2d1e449a474444fa3288c56937a033c4f5f019f7
parent: a3477b9d29227db6e78cede76fdd9d6b83f5ea76
author: sirjofri <sirjofri@sirjofri.de>
date: Wed Aug 14 12:05:53 EDT 2024

adds color selection (wip), adds brush and colors (p9image)

--- a/blie.c
+++ b/blie.c
@@ -50,6 +50,12 @@
 	vstate.dirty = d;
 }
 
+void
+setmodedirty(int d)
+{
+	vstate.mdirty = d;
+}
+
 typedef struct Estate Estate;
 struct Estate {
 	Editor *ed;
@@ -75,7 +81,26 @@
 };
 Panels panels;
 Screen *scr;
+#define NWIN 12
+Image *windows[NWIN];
+void (*winfunc[NWIN])(Image*);
 
+static Image**
+addwindow(Image *i, void (*f)(Image*))
+{
+	int n;
+	for (n = 0; n < NWIN; n++) {
+		if (!windows[n]) {
+			windows[n] = i;
+			winfunc[n] = f;
+			if (f)
+				f(windows[n]);
+			return &windows[n];
+		}
+	}
+	sysfatal("no more windows available!");
+}
+
 void
 changecursor(Cursor *c, Image *i, Point off)
 {
@@ -194,11 +219,21 @@
 static void
 redraw(int force)
 {
+	int i;
 	redrawlayers(panels.layers, estate.l);
 	if (force)
 		setdrawingdirty(Dpan);
 	redrawdrawing();
 	redrawtools();
+	
+	for (i = 0; i < NWIN; i++) {
+		if (windows[i]) {
+			topwindow(windows[i]);
+			if (winfunc[i])
+				winfunc[i](windows[i]);
+		}
+	}
+	flushimage(display, 1);
 }
 
 static void
@@ -234,7 +269,24 @@
 	flushimage(display, 1);
 }
 
+Image**
+reqwin(Rectangle r, ulong color, void (*f)(Image*))
+{
+	Image *i;
+	i = allocwindow(scr, r, 0, color);
+//	originwindow(i, Pt(0, 0), r.min);
+	return addwindow(i, f);
+}
+
 void
+unreqwin(Image **i)
+{
+	freeimage(*i);
+	*i = nil;
+	winfunc[i-windows] = nil;
+}
+
+void
 eresized(int new)
 {
 	if (new && getwindow(display, Refnone) < 0)
@@ -280,6 +332,11 @@
 static void
 condredraw(Redrawwin w)
 {
+	if (vstate.mdirty) {
+		redraw(1);
+		vstate.mdirty = 0;
+		return;
+	}
 	if (w & Rdrawing)
 		redrawdrawing();
 	if (w & Rtools)
@@ -293,6 +350,7 @@
 	Point xy;
 	Mouse *m = &ev.mouse;
 	xy = m->xy;
+	vstate.mousepos = xy;
 	n = Rnil;
 	if (ptinrect(m->xy, panels.layers->r)) {
 		esetcursor(nil);
--- a/blie.h
+++ b/blie.h
@@ -2,6 +2,7 @@
 typedef struct Editor Editor;
 typedef struct Vdata Vdata;
 typedef struct Vstate Vstate;
+typedef struct Color Color;
 
 extern Vdata vdata;
 extern Vstate vstate;
@@ -8,6 +9,16 @@
 extern int bliedebug;
 extern int headless;
 
+struct Color {
+	float r;
+	float g;
+	float b;
+	float a;
+};
+
+Color askcolor(char*);
+ulong color2value(Color);
+
 struct Layer {
 	char *name;
 	char *label;
@@ -48,10 +59,15 @@
 	Point offset;
 	float zoom;
 	int dirty;
+	int mdirty;
 	int maxquality;
+	Point mousepos;
 };
 
+void setmodedirty(int);
 void setdrawingdirty(int);
+Image **reqwin(Rectangle, ulong, void (*f)(Image*));
+void unreqwin(Image**);
 
 /* writes memimage to drawing image, considering pan and zoom using dirty flags */
 void sampleview(Image*, Memimage*, int quality);
--- a/p9image.c
+++ b/p9image.c
@@ -19,7 +19,10 @@
 };
 
 Point toolcell;
+#define NUMCELLS (5)
 
+Image *tmpcol;
+
 typedef struct Data Data;
 struct Data {
 	Memimage *img;
@@ -38,6 +41,12 @@
 	DTmask,
 };
 
+typedef struct Brush Brush;
+struct Brush {
+	Memimage *i;
+	int r;
+};
+
 typedef struct Tstate Tstate;
 struct Tstate {
 	Mode mode;
@@ -46,9 +55,48 @@
 	Memimage *circlebrush;
 	Memimage *colorbrush;
 	int brushrad;
+	
+	int cbrad;
+	double cbexp;
+	double cbmult;
+	
+	int curbrush;
+	int curcolor;
+	
+	Brush brushes[NUMCELLS];
+	ulong colordata[NUMCELLS];
 };
 Tstate tstate;
 
+static Brush*
+getcurrentbrush(void)
+{
+	if (tstate.curbrush < 4) /* three cells reserved */
+		return &tstate.brushes[3];
+	if (tstate.curbrush >= NUMCELLS)
+		return nil;
+	return &tstate.brushes[tstate.curbrush];
+}
+
+static void
+setcolorbrush(ulong color)
+{
+	if (tstate.colorbrush)
+		freememimage(tstate.colorbrush);
+	tstate.colorbrush = allocmemimage(Rect(0, 0, 1, 1), RGB24);
+	tstate.colorbrush->flags |= Frepl|Fsimple|Fbytes;
+	memfillcolor(tstate.colorbrush, color);
+}
+
+static Memimage*
+getcurrentcolor(void)
+{
+	if (tstate.curcolor < 0 || tstate.curcolor >= NUMCELLS)
+		return nil;
+	setcolorbrush(tstate.colordata[tstate.curcolor]);
+	return tstate.colorbrush;
+}
+
 static double
 distance(Point p, Point q)
 {
@@ -67,11 +115,15 @@
 }
 
 static void
-setcirclebrush(int r, double exp)
+setcirclebrush(int r, double exp, double mult)
 {
 	int x, y, d, n;
 	double dist;
 	
+	tstate.cbrad = r;
+	tstate.cbexp = exp;
+	tstate.cbmult = mult;
+	
 	d = r*2 + 1;
 	if (tstate.circlebrush)
 		freememimage(tstate.circlebrush);
@@ -81,24 +133,21 @@
 	for (y = 0; y < d; y++)
 		for (x = 0; x < d; x++) {
 			dist = distance(Pt(x, y), Pt(r, r))/r;
-			dist = pow(1. - dist, exp);
+			if (dist > 1.) {
+				*byteaddr(tstate.circlebrush, Pt(x, y)) = 0x0;
+				continue;
+			}
+			dist = pow(1. - dist, exp) * mult;
 			
 			n = (int)(dist * 256.);
 			*byteaddr(tstate.circlebrush, Pt(x, y)) = isaturate(n);
 		}
+	
+	tstate.brushes[3].i = tstate.circlebrush;
+	tstate.brushes[3].r = tstate.brushrad;
 }
 
 static void
-setcolorbrush(ulong color)
-{
-	if (tstate.colorbrush)
-		freememimage(tstate.colorbrush);
-	tstate.colorbrush = allocmemimage(Rect(0, 0, 1, 1), RGB24);
-	tstate.colorbrush->flags |= Frepl|Fsimple|Fbytes;
-	memfillcolor(tstate.colorbrush, color);
-}
-
-static void
 p9initialize()
 {
 	tstate.mode = Composite;
@@ -113,8 +162,10 @@
 	ellipse(tstate.circle, Pt(20, 20), 19, 19, 0, display->white, ZP);
 	ellipse(tstate.circle, Pt(20, 20), 20, 20, 0, display->black, ZP);
 	
-	setcirclebrush(20, 1.);
+	setcirclebrush(20, 1., 1.);
 	setcolorbrush(DRed);
+	tstate.curbrush = 3;
+	tstate.curcolor = -1;
 }
 
 static void
@@ -256,8 +307,30 @@
 }
 
 static void
+drcols(Image *i, Point p, int n, int hl)
+{
+	Rectangle r;
+	r.min = p;
+	r.max = addpt(p, toolcell);
+	tmpcol = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, tstate.colordata[n]);
+	draw(i, r, tmpcol, nil, ZP);
+	freeimage(tmpcol);
+	border(i, r, 1, hl ? display->black : vdata.gray, ZP);
+}
+
+static void
+drbrush(Image *i, Point p, int n, int hl)
+{
+	Rectangle r;
+	r.min = p;
+	r.max = addpt(p, toolcell);
+	border(i, r, 1, hl ? display->black : vdata.gray, ZP);
+}
+
+static void
 p9drawtools(Layer *l, Image *i)
 {
+	int n;
 	Point p;
 	p = i->r.min;
 	draw(i, i->r, display->white, nil, ZP);
@@ -267,6 +340,19 @@
 	drcells(i, p, "S", tstate.mode == Img);
 	p.x += toolcell.x;
 	drcells(i, p, "M", tstate.mode == Mask);
+	p.x += toolcell.x;
+	
+	for (n = 3; n < NUMCELLS; n++) {
+		drbrush(i, p, n, tstate.curbrush == n);
+		p.x += toolcell.x;
+	}
+	
+	p.y += toolcell.y;
+	p.x = i->r.min.x;
+	for (n = 0; n < NUMCELLS; n++) {
+		drcols(i, p, n, tstate.curcolor == n);
+		p.x += toolcell.x;
+	}
 }
 
 static int
@@ -282,6 +368,8 @@
 	Memimage *tgt;
 	Data *d;
 	Rectangle r;
+	Brush *brush;
+	Memimage *color;
 	
 	p9init(l);
 	d = (Data*)l->data;
@@ -289,6 +377,12 @@
 	if (!buttons)
 		return Rnil;
 	
+	brush = getcurrentbrush();
+	color = getcurrentcolor();
+	
+	if (!(brush && brush->i && color))
+		return Rnil;
+	
 	tgt = nil;
 	if (tstate.drawtarget == DTimg)
 		tgt = d->img;
@@ -298,19 +392,19 @@
 	if (!tgt)
 		return Rnil;
 	
-	r = insetrect(tgt->r, -tstate.brushrad);
+	r = insetrect(tgt->r, -brush->r);
 	if (!ptinrect(xy, r))
 		return Rnil;
 	
-	r = rectaddpt(tstate.circlebrush->r,
+	r = rectaddpt(brush->i->r,
 		subpt(
 			addpt(tgt->r.min, xy),
-			Pt(tstate.brushrad, tstate.brushrad)
+			Pt(brush->r, brush->r)
 		)
 	);
 	
-	tstate.colorbrush->clipr = tstate.circlebrush->r;
-	memimagedraw(tgt, r, tstate.colorbrush, ZP, tstate.circlebrush, ZP, SoverD);
+	color->clipr = brush->i->r;
+	memimagedraw(tgt, r, color, ZP, brush->i, ZP, SoverD);
 	setdrawingdirty(Dcontent);
 	dirtylayer(l);
 	return Rdrawing;
@@ -331,9 +425,63 @@
 	return Rnil;
 }
 
+static void
+selectbrush(int num)
+{
+}
+
+static void
+selectcolor(int num)
+{
+	if (num < 0 || num >= NUMCELLS)
+		return;
+	tstate.curcolor = num;
+}
+
+static void
+setbrush(int num)
+{
+}
+
+static void
+setcolor(int num)
+{
+	Color c;
+	
+	if (num < 0 || num >= NUMCELLS)
+		return;
+	
+	c = askcolor("color");
+	if (c.r < 0)
+		return;
+	tstate.colordata[num] = color2value(c);
+}
+
+static void
+configcirclebrush(Mouse *m)
+{
+	char buf[256];
+	char *args[3];
+	int r;
+	double e, mu;
+	
+	snprint(buf, sizeof buf, "%d %f %f", tstate.cbrad, tstate.cbexp, tstate.cbmult);
+	if (eenter("circle (rad, exp, mult)", buf, sizeof buf, m) < 0)
+		return;
+	
+	if (tokenize(buf, args, 3) != 3)
+		return;
+	
+	r = atoi(args[0]);
+	e = atof(args[1]);
+	mu = atof(args[2]);
+	setcirclebrush(r, e, mu);
+}
+
 static Redrawwin
 p9toolinput(Layer *l, int e, Event ev)
 {
+	Point xy;
 	p9init(l);
 	if (e != Emouse)
 		return Rnil;
@@ -356,6 +504,42 @@
 			tstate.drawtarget = DTmask;
 			goto Out;
 		}
+	}
+	xy.x = ev.mouse.xy.x / toolcell.x;
+	xy.y = ev.mouse.xy.y / toolcell.y;
+	
+	if (ev.mouse.buttons & 1) {
+		/* left mouse button */
+		switch (xy.y) {
+		case 0:
+			if (xy.x < 3)
+				return Rnil;
+			selectbrush(xy.x);
+			return Rtools;
+		case 1:
+			selectcolor(xy.x);
+			return Rtools;
+		}
+		return Rnil;
+	}
+	if (ev.mouse.buttons & 4) {
+		/* right mouse button */
+		switch (xy.y) {
+		case 0:
+			if (xy.x < 3)
+				return Rnil;
+			if (xy.x == 3) {
+				/* special case for circle brush */
+				configcirclebrush(&ev.mouse);
+				return Rnil;
+			}
+			setbrush(xy.x);
+			return Rtools;
+		case 1:
+			setcolor(xy.x);
+			return Rtools;
+		}
+		return Rnil;
 	}
 	return Rnil;
 Out:
--- a/util.c
+++ b/util.c
@@ -3,6 +3,7 @@
 #include <draw.h>
 #include <memdraw.h>
 #include <event.h>
+#include <keyboard.h>
 #include "blie.h"
 
 Memimage*
@@ -57,4 +58,82 @@
 	xy.x = xy.x / vstate.zoom;
 	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;
 }
--- a/words
+++ b/words
@@ -85,3 +85,22 @@
 Selecting a layer also changes the "editor". The editor is a
 module that takes over part of the control, depending on the
 type (see first section about file format).
+
+
+EDITOR p9image
+
+The tools panel shows buttons in a grid.
+
+First row:
+- C: show composite image
+- S: show only the image of this layer ("Solo")
+- M: show only mask
+- the others: brushes (first brush is a configurable circle brush)
+
+Second row:
+- color palette
+
+For the color palette and configurable brushes:
+- left click to select
+- right click to configure
+- middle mouse to preview brush (not implemented yet)