shithub: blie

Download patch

ref: 0e8796746fad950ab61be075de5638fdcbd6fc32
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Aug 6 17:19:30 EDT 2024

adds first working prototype, CLI mode only

--- /dev/null
+++ b/blie.c
@@ -1,0 +1,115 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <event.h>
+#include <bio.h>
+#include "blie.h"
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-cd] dir\n", argv0);
+	exits(nil);
+}
+
+int bliedebug = 0;
+char Nlayers[] = "layers";
+char *root = nil;
+
+int
+loadfile(void)
+{
+	Biobuf *b;
+	int fd;
+	char *s;
+	
+	b = Bopen(Nlayers, OREAD);
+	if (!b) {
+		fd = create(Nlayers, OREAD, 0666);
+		close(fd);
+		b = Bopen(Nlayers, OREAD);
+	}
+	if (!b)
+		sysfatal("unable to open layers file: %r");
+	
+	while (s = Brdstr(b, '\n', 1))
+		addlayer(s);
+	
+	return 1;
+}
+
+static void
+docomp(Layer *l, int id, void *aux)
+{
+	int i;
+	Memimage **img = (Memimage**)aux;
+	Editor *ed = l->editor;
+	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]);
+	img[i] = nil;
+}
+
+void
+outputcomposite(void)
+{
+	Memimage *img[2]; /* swapchain */
+	int i;
+	
+	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]);
+}
+
+void
+main(int argc, char **argv)
+{
+	int outputonly = 0;
+	
+	ARGBEGIN{
+	case 'c':
+		outputonly++;
+		break;
+	case 'd':
+		bliedebug++;
+		break;
+	}ARGEND;
+	
+	root = "";
+	if (argc) {
+		root = argv[0];
+	}
+	
+	rfork(RFNAMEG);
+	if (chdir(root))
+		sysfatal("cannot chdir: %r");
+	
+	if (memimageinit())
+		sysfatal("memimageinit: %r");
+	
+	loadeditors();
+	
+	if (!loadfile())
+		sysfatal("cannot load data: %r");
+	
+	if (outputonly) {
+		outputcomposite();
+		exits(nil);
+	}
+	
+	fprint(2, "interactive mode is not implemented yet!\n");
+	exits(nil);
+}
--- /dev/null
+++ b/blie.h
@@ -1,0 +1,40 @@
+typedef struct Layer Layer;
+typedef struct Editor Editor;
+
+extern int bliedebug;
+
+struct Layer {
+	char *name;
+	char *label;
+	Drawop op;
+	Editor *editor;
+	void *data;
+};
+
+enum { UP, DOWN };
+
+void addlayer(char *name);
+void movelayer(Layer*, int);
+void savelayermeta(Layer*);
+int foreachlayer(void (*f)(Layer*, int, void*), void*);
+
+struct Editor {
+	char *name;
+	Memimage *(*composite)(Layer*, Memimage*);
+	Memimage *(*raw)(Layer*);
+	Memimage *(*mask)(Layer*);
+	Memimage *(*overlay)(Layer*);
+	void (*input)(Layer*, Event);
+	int (*savedata)(Layer*);
+};
+
+extern Editor p9image;
+
+void loadeditors(void);
+int addeditor(Editor*);
+Editor *geteditor(char*);
+
+Memimage* ecomposite(Layer*, Memimage*);
+Memimage* gencomposite(Memimage*, Memimage*, Memimage*, Drawop);
+Memimage* gencanvas(Memimage*);
+Memimage* dupmemimage(Memimage*);
--- /dev/null
+++ b/editor.c
@@ -1,0 +1,52 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <event.h>
+#include "blie.h"
+
+void
+loadeditors(void)
+{
+	addeditor(&p9image);
+}
+
+typedef struct Leditor Leditor;
+struct Leditor {
+	Editor *editor;
+	Leditor *next;
+};
+
+Leditor *firsteditor = nil;
+
+int
+addeditor(Editor *ed)
+{
+	Leditor *l;
+	if (geteditor(ed->name)) {
+		werrstr("editor already registered: %s", ed->name);
+		return 0;
+	}
+	if (!firsteditor) {
+		firsteditor = mallocz(sizeof(Leditor), 1);
+		firsteditor->editor = ed;
+		return 1;
+	}
+	for (l = firsteditor; l->next; l = l->next)
+		continue;
+	l->next = mallocz(sizeof(Leditor), 1);
+	l = l->next;
+	l->editor = ed;
+	return 1;
+}
+
+Editor*
+geteditor(char *name)
+{
+	Leditor *l;
+	for (l = firsteditor; l; l = l->next) {
+		if (cistrcmp(name, l->editor->name) == 0)
+			return l->editor;
+	}
+	return nil;
+}
--- /dev/null
+++ b/layer.c
@@ -1,0 +1,190 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <event.h>
+#include <bio.h>
+#include "blie.h"
+
+char Ebadfmt[] = "bad file format: ";
+
+typedef struct List List;
+struct List {
+	Layer *layer;
+	List *next;
+};
+
+List *firstlayer = nil;
+
+static Drawop
+str2op(char *s)
+{
+#define OP(n) if (!strcmp(s, "n")) { return n; }
+	OP(Clear);
+	OP(SinD);
+	OP(DinS);
+	OP(SoutD);
+	OP(DoutS);
+	OP(S);
+	OP(SoverD);
+	OP(SatopD);
+	OP(SxorD);
+	OP(D);
+	OP(DoverS);
+	OP(DatopS);
+	OP(DxorS);
+#undef OP
+	return SoverD;
+}
+
+static char*
+op2str(Drawop op)
+{
+#define OP(n) if (op == n) { return "n"; }
+	OP(Clear);
+	OP(SinD);
+	OP(DinS);
+	OP(SoutD);
+	OP(DoutS);
+	OP(S);
+	OP(SoverD);
+	OP(SatopD);
+	OP(SxorD);
+	OP(D);
+	OP(DoverS);
+	OP(DatopS);
+	OP(DxorS);
+#undef OP
+	return "SoverD";
+}
+
+static void
+addlayerl(Layer *l)
+{
+	List *f;
+	if (!firstlayer) {
+		firstlayer = mallocz(sizeof(List), 1);
+		firstlayer->layer = l;
+		return;
+	}
+	for (f = firstlayer; f->next; f = f->next)
+		continue;
+	f->next = mallocz(sizeof(List), 1);
+	f->next->layer = l;
+}
+
+void
+addlayer(char *name)
+{
+	Biobuf *b;
+	char *file;
+	char *ed, *lbl, *op;
+	Layer *l;
+	
+	if (!name || !name[0])
+		return;
+	
+	file = smprint("%s/meta", name);
+	b = Bopen(file, OREAD);
+	free(file);
+	if (!b)
+		sysfatal("error opening file %s/meta: %r", name);
+	
+	ed = Brdstr(b, '\n', 1);
+	if (!ed)
+		sysfatal("%s%s/meta: missing editor", Ebadfmt, name);
+	lbl = Brdstr(b, '\n', 1);
+	if (!lbl)
+		sysfatal("%s%s/meta: missing label", Ebadfmt, name);
+	op = Brdstr(b, '\n', 1);
+	if (!op)
+		sysfatal("%s%s/meta: missing drawop", Ebadfmt, name);
+	
+	l = mallocz(sizeof(Layer), 1);
+	l->name = strdup(name);
+	l->label = strdup(lbl);
+	l->editor = geteditor(ed);
+	l->op = str2op(op);
+	if (!l->editor)
+		sysfatal("editor %s not found for %s", ed, name);
+	addlayerl(l);
+	free(lbl);
+	free(ed);
+	free(op);
+	
+	Bterm(b);
+}
+
+void
+movelayer(Layer *layer, int dir)
+{
+	List *parent;
+	List *l;
+	
+	if (firstlayer->layer == layer) {
+		if (dir == DOWN) {
+			l = firstlayer->next;
+			firstlayer->next = l ? l->next : nil;
+			l->next = firstlayer;
+			firstlayer = l;
+			return;
+		}
+		if (dir == UP)
+			return; /* NOP */
+	}
+	
+	/* find parent */
+	for (l = firstlayer; l && l->next->layer != layer; l = l->next)
+		continue;
+	
+	if (!l)
+		return; /* not found */
+	parent = l;
+	
+	if (dir == DOWN)
+		goto Movedown;
+	if (dir != UP)
+		sysfatal("code error: direction != UP|DOWN");
+	
+	/* movelayer(layer, UP) == movelayer(parent, DOWN) */
+	movelayer(parent->layer, DOWN);
+	
+	return;
+Movedown:
+	l = parent->next;
+	if (!l)
+		return; /* NOP: last element */
+	parent->next = l->next;
+	l->next = l->next ? l->next->next : nil;
+	parent->next->next = l;
+}
+
+void
+savelayermeta(Layer *layer)
+{
+	Biobuf *b;
+	char *file;
+	
+	file = smprint("%s.meta", layer->name);
+	b = Bopen(file, OWRITE|OTRUNC);
+	free(file);
+	if (!b)
+		sysfatal("error opening file %s.meta for write: %r", layer->name);
+	
+	Bprint(b, "%s\n%s\n", layer->editor->name, layer->label);
+	Bterm(b);
+}
+
+int
+foreachlayer(void (*f)(Layer*, int, void*), void* aux)
+{
+	List *l;
+	int i;
+	
+	i = 0;
+	for (l = firstlayer; l; l = l->next, i++) {
+		f(l->layer, i, aux);
+	}
+	
+	return i;
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,14 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=blie
+OFILES=\
+	blie.$O\
+	editor.$O\
+	layer.$O\
+	util.$O\
+	p9image.$O\
+
+HFILES=blie.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/p9image.c
@@ -1,0 +1,126 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <event.h>
+#include "blie.h"
+
+typedef struct Data Data;
+struct Data {
+	Memimage *img;
+	Memimage *mask;
+	Drawop op;
+};
+
+static void
+p9init(Layer *l)
+{
+	int fd;
+	char *s;
+	Data *d;
+	
+	if (l->data)
+		return;
+	d = mallocz(sizeof(Data), 1);
+	l->data = d;
+	
+	/* image file */
+	s = smprint("%s/img", l->name);
+	fd = open(s, OREAD);
+	if (fd < 0) {
+		free(s);
+		return;
+	}
+	free(s);
+	
+	seek(fd, 0, 0);
+	d->img = creadmemimage(fd);
+	if (!d->img) {
+		seek(fd, 0, 0);
+		d->img = readmemimage(fd);
+	}
+	close(fd);
+	
+	/* mask file */
+	s = smprint("%s/mask", l->name);
+	fd = open(s, OREAD);
+	if (fd < 0) {
+		free(s);
+		return;
+	}
+	free(s);
+	
+	seek(fd, 0, 0);
+	d->mask = creadmemimage(fd);
+	if (!d->mask) {
+		seek(fd, 0, 0);
+		d->mask = readmemimage(fd);
+	}
+	close(fd);
+}
+
+/* just use ecompose, which uses raw() and mask() */
+static Memimage*
+p9composite(Layer *l, Memimage *img)
+{
+	Data *d;
+	
+	p9init(l);
+	d = (Data*)l->data;
+	
+	if (!img) {
+		fprint(2, "%s: return input image: %p\n", l->name, d->img);
+		return dupmemimage(d->img);
+	}
+	
+	fprint(2, "%s: return composite image: %p %p %p\n", l->name,
+		img, d->img, d->mask);
+	return gencomposite(img, d->img, d->mask, l->op);
+}
+
+static Memimage*
+p9raw(Layer *l)
+{
+	Data *d;
+	p9init(l);
+	d = (Data*)l->data;
+	return d->img;
+}
+
+static Memimage*
+p9mask(Layer *l)
+{
+	Data *d;
+	p9init(l);
+	d = (Data*)l->data;
+	return d->mask;
+}
+
+static Memimage*
+p9overlay(Layer *l)
+{
+	p9init(l);
+	return nil;
+}
+
+static int
+p9savedata(Layer *l)
+{
+	p9init(l);
+	return 1;
+}
+
+static void
+p9input(Layer *l, Event)
+{
+	p9init(l);
+}
+
+Editor p9image = {
+	.name = "p9img",
+	.raw = p9raw,
+	.mask = p9mask,
+	.overlay = p9overlay,
+	.savedata = p9savedata,
+	.input = p9input
+};
--- /dev/null
+++ b/util.c
@@ -1,0 +1,51 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <event.h>
+#include "blie.h"
+
+Memimage*
+dupmemimage(Memimage *i)
+{
+	Memimage *d;
+	d = gencanvas(i);
+	
+	memimagedraw(d, i->r, i, ZP, nil, ZP, SoverD);
+	return d;
+}
+
+Memimage*
+gencanvas(Memimage *i)
+{
+	return allocmemimage(i->r, i->chan);
+}
+
+Memimage*
+gencomposite(Memimage *A, Memimage *B, Memimage *M, Drawop op)
+{
+	Memimage *d;
+	
+	d = dupmemimage(A);
+	memimagedraw(d, B->r, B, ZP, M, ZP, op);
+	return d;
+}
+
+Memimage*
+ecomposite(Layer *l, Memimage *img)
+{
+	Memimage *s;
+	Memimage *m;
+	
+	s = l->editor->raw(l);
+	m = l->editor->mask(l);
+	
+	if (!img) {
+		if (bliedebug > 1)
+			fprint(2, "  c: raw image\n");
+		return dupmemimage(s);
+	}
+	if (bliedebug > 1)
+		fprint(2, "  c: combine images\n");
+	return gencomposite(img, s, m, l->op);
+}
--- /dev/null
+++ b/words
@@ -1,0 +1,53 @@
+blie - blended layers image editor
+
+FILE FORMAT
+
+The "file" is a folder.
+
+this folder needs at least a single file "layers", which lists all
+the layers in order.
+
+Each layer has its own subdirectory named as the layer, with at
+least a file named "meta". The meta files looks like this:
+
+	editor-type
+	Label
+	Drawop
+
+editor-type must be a valid editor module. At the moment, only
+p9 images are supported, which is "p9img".
+
+Label is a user label which should be displayed in the editor.
+
+Drawop is the drawing operation of that layer. It uses the names
+from draw(2), for example SatopD.
+
+All remaining lines are ignored.
+
+In addition to the "meta" file, the actual data of that layer should
+be stored in separate files. The p9 image editor "img" uses two
+additional files; both are p9 (rgb) images:
+
+- "img" is the image data of the layer,
+- "mask" is an optional mask which will be used for blending.
+
+
+In total, a sample structure could look like this:
+
+- /layers
+- /layerA/meta
+- /layerA/img
+- /layerB/meta
+- /layerB/img
+- /layerB/mask
+
+In this case, "layers" would look like this:
+
+	layerA
+	layerB
+
+and "layerB/meta" would look like this:
+
+	p9img
+	My fancy layer B
+	SatopD