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