shithub: blie

Download patch

ref: 6772237407110a85e52893d91caba41969dad707
parent: f7ce9af61db9b0d072dad403cb98d8bad4224dda
author: sirjofri <sirjofri@sirjofri.de>
date: Fri Aug 9 08:50:28 EDT 2024

improve performance, fixes cursor issues, general code improvement

--- a/blie.c
+++ b/blie.c
@@ -25,11 +25,6 @@
 	.keyzoom = 0.2,
 };
 
-Vstate vstate = {
-	.zoom = 1.0,
-	.offset = { 0, 0 },
-};
-
 static void
 initvdata(void)
 {
@@ -41,6 +36,18 @@
 	vdata.gray = allocimagemix(display, DBlack, DWhite);
 }
 
+Vstate vstate = {
+	.zoom = 1.0,
+	.offset = { 0, 0 },
+	.dirty = 3,
+};
+
+void
+setdrawingdirty(int d)
+{
+	vstate.dirty = d;
+}
+
 typedef struct Estate Estate;
 struct Estate {
 	Editor *ed;
@@ -98,21 +105,23 @@
 }
 
 static void
-docomp(Layer *l, int id, void *aux)
+docomp(Layer *l, int id, int, void *aux)
 {
 	int i;
 	Memimage **img = (Memimage**)aux;
 	Editor *ed = l->editor;
+	Memimage *(*c)(Layer*, Memimage*);
+	
 	i = (id + 1)%2;
 	
 	if (bliedebug)
 		fprint(2, "composite: %s\n", l->name);
 	
-	if (ed->composite)
-		img[id%2] = ed->composite(l, img[i]);
-	else
-		img[id%2] = ecomposite(l, img[i]);
-	freememimage(img[i]);
+	c = ed->composite ? ed->composite : ecomposite;
+	
+	if (!l->composited)
+		l->composited = c(l, img[i]);
+	img[id%2] = l->composited;
 	img[i] = nil;
 }
 
@@ -124,12 +133,8 @@
 	
 	img[0] = nil;
 	img[1] = nil;
-	i = foreachlayer(docomp, img);
-	
-	if (img[i%2])
-		writememimage(1, img[i%2]);
-	else
-		writememimage(1, img[(i+1)%2]);
+	i = (foreachlayer(docomp, img) + 1) % 2;
+	writememimage(1, img[i]);
 }
 
 static void
@@ -138,6 +143,12 @@
 	Memimage *img[2]; /* swapchain */
 	int i;
 	
+	if (estate.ed && estate.ed->overlay) {
+		if (estate.ed->overlay(estate.l, nil)) {
+			estate.ed->overlay(estate.l, panels.drawing);
+			goto Cursorfix;
+		}
+	}
 	img[0] = nil;
 	img[1] = nil;
 	i = (foreachlayer(docomp, img) + 1) % 2;
@@ -144,10 +155,15 @@
 
 	sampleview(panels.drawing, img[i]);
 	
-	if (!(estate.ed && estate.ed->overlay))
-		return;
+	if (estate.ed && estate.ed->overlay)
+		estate.ed->overlay(estate.l, panels.drawing);
 	
-	estate.ed->overlay(estate.l, panels.drawing);
+	/* fix cursor */
+Cursorfix:
+	if (dstate.cursorblit) {
+		freeimage(dstate.cursorblit);
+		dstate.cursorblit = nil;
+	}
 }
 
 static void
@@ -158,22 +174,20 @@
 	} else {
 		draw(panels.tools, panels.tools->r, display->white, nil, ZP);
 	}
+	line(panels.tools,
+		Pt(panels.tools->r.min.x, panels.tools->r.max.y-1),
+		subpt(panels.tools->r.max, Pt(0, 1)),
+		Endsquare, Endsquare, 0, display->black, ZP);
 }
 
 static void
-redraw(void)
+redraw(int force)
 {
 	redrawlayers(panels.layers, estate.l);
-	line(panels.layers, panels.layers->r.min,
-		Pt(panels.layers->r.min.x, panels.layers->r.max.y),
-		Endsquare, Endsquare, 0, display->black, ZP);
+	if (force)
+		setdrawingdirty(Dpan);
 	redrawdrawing();
-	
 	redrawtools();
-	line(panels.tools,
-		Pt(panels.tools->r.min.x, panels.tools->r.max.y-1),
-		subpt(panels.tools->r.max, Pt(0, 1)),
-		Endsquare, Endsquare, 0, display->black, ZP);
 }
 
 static void
@@ -215,7 +229,7 @@
 	if (new && getwindow(display, Refnone) < 0)
 		sysfatal("resize failed: %r");
 	screeninit();
-	redraw();
+	redraw(1);
 }
 
 static void
@@ -224,7 +238,7 @@
 	estate.ed = l->editor;
 	estate.l = l;
 	screeninit();
-	redraw();
+	redraw(1);
 }
 
 static void
@@ -255,11 +269,11 @@
 static void
 handlemouse(Event ev)
 {
-	int n;
+	Redrawwin n;
 	Point xy;
 	Mouse *m = &ev.mouse;
 	xy = m->xy;
-	n = 0;
+	n = Rnil;
 	if (ptinrect(m->xy, panels.layers->r)) {
 		esetcursor(nil);
 		drawcursor(m->xy, 1);
@@ -276,14 +290,15 @@
 		if (estate.ed->toolinput)
 			n = estate.ed->toolinput(estate.l, Emouse, ev);
 		switch (n) {
-		case 1:
+		case Rdrawing:
+			setdrawingdirty(Dcontent);
 			redrawdrawing();
 			break;
-		case 2:
+		case Rtools:
 			redrawtools();
 			break;
 		case 3:
-			redraw();
+			redraw(0);
 			break;
 		}
 		return;
@@ -293,14 +308,15 @@
 		if (estate.ed->drawinput)
 			n = estate.ed->drawinput(estate.l, Emouse, ev);
 		switch (n) {
-		case 1:
+		case Rdrawing:
+			setdrawingdirty(Dcontent);
 			redrawdrawing();
 			break;
-		case 2:
+		case Rtools:
 			redrawtools();
 			break;
 		case 3:
-			redraw();
+			redraw(0);
 			break;
 		}
 		esetcursor(dstate.cursor);
@@ -316,25 +332,28 @@
 	switch (kbdc) {
 	case Kup:
 		vstate.offset.y += vdata.keyoffset;
-		goto Redraw;
+		goto Dirtypan;
 	case Kdown:
 		vstate.offset.y -= vdata.keyoffset;
-		goto Redraw;
+		goto Dirtypan;
 	case Kleft:
 		vstate.offset.x += vdata.keyoffset;
-		goto Redraw;
+		goto Dirtypan;
 	case Kright:
 		vstate.offset.x -= vdata.keyoffset;
-		goto Redraw;
+		goto Dirtypan;
 	case '.':
 		vstate.zoom += vdata.keyzoom;
-		goto Redraw;
+		goto Dirtyzoom;
 	case ',':
 		vstate.zoom -= vdata.keyzoom;
-		goto Redraw;
+		goto Dirtyzoom;
 	}
 	return 0;
-Redraw:
+Dirtyzoom:
+	vstate.dirty |= Dzoom;
+Dirtypan:
+	vstate.dirty |= Dpan;
 	redrawdrawing();
 	return 1;
 }
@@ -427,7 +446,7 @@
 	estate.ed = nil;
 	estate.l = nil;
 	screeninit();
-	redraw();
+	redraw(1);
 	
 	for (;;) {
 		e = event(&ev);
--- a/blie.h
+++ b/blie.h
@@ -12,6 +12,8 @@
 	char *label;
 	Drawop op;
 	Editor *editor;
+	Memimage *composited;
+	int num;
 	void *data;
 };
 
@@ -20,9 +22,10 @@
 void addlayer(char *name);
 void movelayer(Layer*, int);
 void savelayermeta(Layer*);
+void dirtylayer(Layer*);
 void redrawlayers(Image*, Layer*);
 void clicklayer(Mouse, void (*f)(Layer*));
-int foreachlayer(void (*f)(Layer*, int, void*), void*);
+int foreachlayer(void (*f)(Layer*, int, int, void*), void*);
 
 void changecursor(Cursor*, Image*, Point);
 
@@ -34,24 +37,39 @@
 	Image *gray;
 };
 
+enum {
+	Dpan = 1,
+	Dzoom = 2,
+	Dcontent = Dpan|Dzoom,
+};
+
 struct Vstate {	
 	Point offset;
 	float zoom;
+	int dirty;
 };
 
-/* writes memimage to drawing image, considering pan and zoom */
+void setdrawingdirty(int);
+
+/* writes memimage to drawing image, considering pan and zoom using dirty flags */
 void sampleview(Image*, Memimage*);
 
+typedef enum {
+	Rnil = 0,
+	Rdrawing = 1,
+	Rtools = 2,
+} Redrawwin;
+
 struct Editor {
 	char *name;
 	Memimage *(*composite)(Layer*, Memimage*);
 	Memimage *(*raw)(Layer*);
 	Memimage *(*mask)(Layer*);
-	void (*overlay)(Layer*, Image*);
+	int (*overlay)(Layer*, Image*);
 	Rectangle (*toolrect)(Layer*);
 	void (*drawtools)(Layer*, Image*);
-	int (*drawinput)(Layer*, int, Event);
-	int (*toolinput)(Layer*, int, Event);
+	Redrawwin (*drawinput)(Layer*, int, Event);
+	Redrawwin (*toolinput)(Layer*, int, Event);
 	int (*savedata)(Layer*);
 };
 
--- /dev/null
+++ b/editor.txt
@@ -1,0 +1,97 @@
+Editor functions and properties (blie.h:/^struct Editor)
+
+PROPERTY name
+
+short name of the editor. Works as an identifier, must be unique!
+
+
+FUNCTION composite (*)
+
+param:  Layer*     Layer to work with
+param:  Memimage*  Memimage to composite over
+return: Memimage*  composited result
+
+
+FUNCTION raw (tbd)
+
+param:  Layer*     Layer to work with
+return: Memimage*  raw image (no duplicate)
+
+
+FUNCTION mask (tbd)
+
+param:  Layer*     Layer to work with
+return: Memimage*  raw mask (no duplicate)
+
+
+FUNCTION overlay (*)
+
+param:  Layer*     Layer to work with
+param:  Image*     editor image to draw on
+return: int        whether overlay fills the full drawing
+
+This function is called in two situations:
+
+1. To determine if we need to draw the image underneath
+2. To actually draw the overlay
+
+Case 1 is when the editor image is nil. In this case, only the
+return value is relevant.
+
+Case 2 is when the editor image is set. In this case, the return
+value is ignored.
+
+Case 1 is important to improve performance. There's no need to
+composite and draw the drawing when we will overdraw it with
+a full overlay image anyway.
+
+
+FUNCTION toolrect
+
+param:  Layer*     Layer to work with
+return: Rectangle  wanted size of the tool window
+
+For the given layer, return the needed size of the tool window.
+In general, the tool window should be pretty small in height.
+
+
+FUNCTION drawtools
+
+param:  Layer*     Layer to work with
+param:  Image*     Image to draw the tools on
+
+For the given layer, draw the tools handles into the supplied
+image.
+
+
+FUNCTION drawinput
+
+param:  Layer*     Layer to work with
+param:  int        Event type (Emouse, Ekeyboard)
+param:  Event      Event structure
+return: Redrawwin  Redraw parameters
+
+The mouse xy inside the event structure is normalized to the
+(0,0) position of the drawing window.
+
+Use the return parameter to specify which window should be
+redrawn.
+
+
+FUNCTION toolinput
+
+param:  Layer*     Layer to work with
+param:  int        Event type (Emouse, Ekeyboard)
+param:  Event      Event structure
+return: Redrawwin  Redraw parameters
+
+This function is analogous to drawinput, just for the tools
+window.
+
+
+FUNCTION savedata
+
+param:  Layer*     Layer to work with
+return: int        1 for success, 0 for fail
+
+If failed, set errstr.
--- a/layer.c
+++ b/layer.c
@@ -59,6 +59,18 @@
 }
 
 static void
+_renum(Layer *l, int id, int, void*)
+{
+	l->num = id;
+}
+
+static void
+renumlayers(void)
+{
+	foreachlayer(_renum, nil);
+}
+
+static void
 addlayerl(Layer *l)
 {
 	List *f;
@@ -65,6 +77,7 @@
 	if (!firstlayer) {
 		firstlayer = mallocz(sizeof(List), 1);
 		firstlayer->layer = l;
+		l->num = 0;
 		return;
 	}
 	for (f = firstlayer; f->next; f = f->next)
@@ -71,6 +84,7 @@
 		continue;
 	f->next = mallocz(sizeof(List), 1);
 	f->next->layer = l;
+	l->num = f->layer->num + 1;
 }
 
 void
@@ -157,9 +171,22 @@
 	parent->next = l->next;
 	l->next = l->next ? l->next->next : nil;
 	parent->next->next = l;
+	
+	// TODO: verify!
+	l->layer->num--;
+	if (l->next)
+		l->next->layer->num++;
 }
 
 void
+dirtylayer(Layer *layer)
+{
+	if (layer->composited)
+		freememimage(layer->composited);
+	layer->composited = nil;
+}
+
+void
 savelayermeta(Layer *layer)
 {
 	Biobuf *b;
@@ -176,7 +203,7 @@
 }
 
 int
-foreachlayer(void (*f)(Layer*, int, void*), void* aux)
+foreachlayer(void (*f)(Layer*, int, int, void*), void* aux)
 {
 	List *l;
 	int i;
@@ -183,7 +210,7 @@
 	
 	i = 0;
 	for (l = firstlayer; l; l = l->next, i++) {
-		f(l->layer, i, aux);
+		f(l->layer, i, !l->next, aux);
 	}
 	
 	return i;
@@ -192,7 +219,7 @@
 int layerheight;
 
 static void
-redrawlayer(Layer *l, int n, void *aux)
+redrawlayer(Layer *l, int n, int, void *aux)
 {
 	void **data;
 	Image *img;
@@ -225,10 +252,12 @@
 	layerheight = vdata.fontheight + 10;
 	draw(img, img->r, display->white, nil, ZP);
 	foreachlayer(redrawlayer, data);
+	line(img, img->r.min, Pt(img->r.min.x, img->r.max.y),
+		Endsquare, Endsquare, 0, display->black, ZP);
 }
 
 static void
-checkclick(Layer *l, int n, void *aux)
+checkclick(Layer *l, int n, int, void *aux)
 {
 	void **a;
 	void (*factivate)(Layer*);
--- a/p9image.c
+++ b/p9image.c
@@ -122,7 +122,7 @@
 	return d->mask;
 }
 
-static void
+static int
 p9overlay(Layer *l, Image *i)
 {
 	Data *data;
@@ -131,6 +131,9 @@
 	p9init(l);
 	data = (Data*)l->data;
 	
+	if (!i)
+		return tstate.mode != Composite;
+	
 	switch (tstate.mode) {
 	case Composite:
 		break;
@@ -142,7 +145,7 @@
 		goto Mout;
 	}
 	changecursor(nil, nil, ZP);
-	return;
+	return 0;
 Mout:
 	if (!tstate.circle) {
 		tstate.circle = allocimage(display, Rect(0, 0, 41, 41), RGBA32, 0, DTransparent);
@@ -151,9 +154,11 @@
 	}
 	changecursor(&ccircle, tstate.circle, Pt(-20, -20));
 	if (!mi)
-		return;
+		return 0;
 	
+	setdrawingdirty(Dcontent);
 	sampleview(i, mi);
+	return 0;
 }
 
 static Rectangle
@@ -175,7 +180,7 @@
 	return 1;
 }
 
-static int
+static Redrawwin
 p9drawinput(Layer *l, int e, Event)
 {
 	p9init(l);
@@ -185,18 +190,18 @@
 	case Emouse:
 		break;
 	}
-	return 0;
+	return Rnil;
 }
 
-static int
+static Redrawwin
 p9toolinput(Layer *l, int e, Event ev)
 {
 	p9init(l);
 	if (e != Emouse)
-		return 0;
+		return Rnil;
 	
 	if (!ev.mouse.buttons)
-		return 0;
+		return Rnil;
 	
 	switch (ev.mouse.xy.x / 50) {
 	case 0:
@@ -209,9 +214,9 @@
 		tstate.mode = Mask;
 		break;
 	default:
-		return 0;
+		return Rnil;
 	}
-	return 1;
+	return Rdrawing;
 }
 
 Editor p9image = {
--- a/sample.c
+++ b/sample.c
@@ -113,29 +113,39 @@
 	return nil;
 }
 
+Memimage *lastsampled = nil;
+
 void
 sampleview(Image *img, Memimage *src)
 {
 	Memimage *tmi;
-	Memimage *tsi;
 	Rectangle r;
 	int nw;
 	
+	if (!vstate.dirty)
+		return;
+	
 	r.min = ZP;
 	r.max = Pt(Dx(img->r), Dy(img->r));
 	
-	tsi = resample(src, Dx(src->r)*vstate.zoom, Dy(src->r)*vstate.zoom);
-	if (!tsi)
-		return;
+	if (vstate.dirty & Dzoom) {
+		freememimage(lastsampled);
+		lastsampled = nil;
+	}
+	if (!lastsampled) {
+		lastsampled = resample(src, Dx(src->r)*vstate.zoom, Dy(src->r)*vstate.zoom);
+		if (!lastsampled)
+			return;
+	}
 	
 	tmi = allocmemimage(r, img->chan);
 	memfillcolor(tmi, DBlack);
-	memimagedraw(tmi, tmi->r, tsi, addpt(tmi->r.min, vstate.offset), nil, ZP, S);
+	memimagedraw(tmi, tmi->r, lastsampled, addpt(tmi->r.min, vstate.offset), nil, ZP, S);
 	
 	nw = tmi->width * tmi->r.max.y * sizeof(ulong); // tmi->r.max.y == Dy(tmi->r)
 	draw(img, img->r, display->black, nil, ZP);
 	loadimage(img, img->r, tmi->data->bdata, nw);
 	
-	freememimage(tsi);
 	freememimage(tmi);
+	vstate.dirty = 0;
 }