shithub: blie

Download patch

ref: 8626465772e8dd47dfc7e5f0319f6458d3be5335
parent: 807afae69b7551b681b638d64f1099af5c672fdd
author: sirjofri <sirjofri@sirjofri.de>
date: Tue Aug 20 04:27:47 EDT 2024

use embedded mask, use db for serializing tool data

--- a/blie.c
+++ b/blie.c
@@ -26,6 +26,17 @@
 	.keyzoom = 0.2,
 };
 
+static Memimage*
+memcol(int chan, int color)
+{
+	Memimage *i;
+	i = allocmemimage(Rect(0, 0, 1, 1), chan);
+	memfillcolor(i, color);
+	i->flags |= Fsimple|Frepl;
+	i->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
+	return i;
+}
+
 static void
 initvdata(void)
 {
@@ -583,10 +594,11 @@
 		sysfatal("memimageinit: %r");
 	
 	headless = outputonly;
-	if (!headless)
+	if (!headless) {
 		if (initdraw(nil, nil, "blie") < 0)
 			sysfatal("initdraw: %r");
-	initvdata();
+		initvdata();
+	}
 	loadeditors();
 	
 	if (!loadfile())
--- a/blie.h
+++ b/blie.h
@@ -38,6 +38,7 @@
 	int fontheight; /* height of font */
 	int keyoffset; /* offset on key input */
 	float keyzoom; /* zoom change */
+	
 	Image *gray;
 };
 
--- /dev/null
+++ b/db.c
@@ -1,0 +1,490 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include "db.h"
+
+
+#define DEBUG
+#undef DEBUG
+#define LOG 1
+#define VERBOSE 2
+#define VERYVERBOSE 3
+static int verbosity = LOG;
+static int consfd = -1;   /* 2 for stderr, -1 for cons */
+
+static void
+clog(int v, char *fmt, ...)
+{
+#ifdef DEBUG
+	va_list args;
+	
+	if (v > verbosity)
+		return;
+	
+	if (consfd < 0) {
+		consfd = open("#c/cons", OWRITE|OCEXEC);
+		if (consfd < 0)
+			return;
+	}
+	
+	fprint(consfd, "bliedb: ");
+	va_start(args, fmt);
+	vfprint(consfd, fmt, args);
+	va_end(args);
+	fprint(consfd, "\n");
+#else
+	USED(v, fmt);
+#endif
+}
+
+static char*
+estrdup(char *s)
+{
+	char *t;
+	t = strdup(s);
+	if (!t)
+		sysfatal("out of memory: %r");
+	return t;
+}
+
+static char*
+unesc(char *s)
+{
+	char *t;
+	int n;
+	n = strlen(s);
+	
+	while (t = strstr(s, "''")) {
+		memmove(t, t+1, n - (t-s));
+		n--;
+	}
+	clog(VERYVERBOSE, "parse: unesc: %s", s);
+	return s;
+}
+
+enum {
+	OUT = 0,
+	QUOT = 1,
+};
+
+static Dpack*
+pgetdv(Db *db, char *i)
+{
+	clog(VERBOSE, "parse: dpack %s", i);
+	return getdpack(db, i);
+}
+
+static void
+paddkv(Dpack *dv, char *k, char *v)
+{
+	clog(VERBOSE, "parse:   %s = %s", k, v);
+	setdval(dv, k, unesc(v));
+}
+
+char *allowed = ".(){}!#$%&*+,-/:;<>?@[]^\_`|~\"";
+
+static int
+iswc(int c)
+{
+	return isalnum(c) || strchr(allowed, c);
+}
+
+static void
+parsedb(Db *db, char *s)
+{
+	Dpack *dv;
+	char *k, *v;
+	int state = 0;
+	char *t;
+	int doquit;
+	
+	clog(LOG, "parse: begin parsing: %s", s);
+	
+	dv = nil;
+	t = nil;
+	k = nil;
+	v = nil;
+	doquit = 0;
+	while (1) {
+		if (doquit)
+			break;
+		clog(VERYVERBOSE, "parse: state: %d, char '%c'", state, *s);
+		if (state == QUOT && *s == 0) {
+			clog(LOG, "parse: bad syntax EOL");
+			break;
+		}
+		if (state == OUT && (*s == ' ' || *s == '\t' || *s == 0)) {
+			if (*s == 0) {
+				clog(VERBOSE, "parse: regular EOL");
+				doquit++;
+			}
+			if (!t) {
+				s++;
+				continue;
+			}
+			if (!dv) {
+				*s = 0;
+				dv = pgetdv(db, t);
+				t = nil;
+				s++;
+				continue;
+			}
+			if (!k) {
+				*s = 0;
+				k = t;
+				clog(VERBOSE, "parse:   key %s", k);
+				t = nil;
+				s++;
+				continue;
+			}
+			if (!v) {
+				*s = 0;
+				v = t;
+				paddkv(dv, k, v);
+				k = nil;
+				v = nil;
+				t = nil;
+				s++;
+				continue;
+			}
+			goto Bad;
+		}
+		
+		if (state == QUOT && (*s == ' ' || *s == '\t' || *s == '=')) {
+			goto Alnum;
+		}
+		
+		if (state == OUT && *s == '\'') {
+			state = QUOT;
+			clog(VERYVERBOSE, "parse: {quote");
+			s++;
+			t = s;
+			continue;
+		}
+		if (state == QUOT && *s == '\'') {
+			if (s[1] == '\'') {
+				s++; /* skip escaped quote */
+				clog(VERYVERBOSE, "parse: esc:           >%c<", *s);
+				goto Alnum;
+			}
+			state = OUT;
+			clog(VERYVERBOSE, "parse: }quote");
+			*s = 0;
+			s++;
+			continue;
+		}
+		if (iswc(*s)) {   /* both states */
+Alnum:
+			if (!t) {
+				clog(VERYVERBOSE, "parse: encountered new word");
+				t = s;
+			}
+			s++;
+			continue;
+		}
+		if (state == OUT && *s == '=') {
+			*s = ' ';
+			continue;
+		}
+Bad:
+		clog(LOG, "parse: bad state: got '%c'", *s);
+		break;
+	}
+	
+	clog(LOG, "parse: finished parsing line");
+}
+
+Db*
+opendb(char *file)
+{
+	Biobuf *bin;
+	int fd;
+	char *s;
+	Db *db;
+	
+	/* try to install "%q" quote fmt, will silently
+	 * fail if it's already installed.
+	 */
+	quotefmtinstall();
+	
+	if (!file) {
+		db = mallocz(sizeof(Db), 1);
+		return db;
+	}
+	
+	fd = open(file, OREAD);
+	if (fd < 0) {
+		db = mallocz(sizeof(Db), 1);
+		db->file = estrdup(file);
+		return db;
+	}
+	bin = Bfdopen(fd, OREAD);
+	if (!bin)
+		sysfatal("%r");
+	
+	db = mallocz(sizeof(Db), 1);
+	db->file = estrdup(file);
+	
+	while (s = Brdstr(bin, '\n', 1)) {
+		parsedb(db, s);
+		free(s);
+	}
+	
+	Bterm(bin);
+	return db;
+}
+
+static void
+freedpack(Dpack *dv)
+{
+	Dtuple *tp, *ttp;
+	
+	for (tp = dv->tuple; tp;) {
+		free(tp->key);
+		if (tp->value)
+			free(tp->value);
+		ttp = tp->next;
+		free(tp);
+		tp = ttp;
+	}
+	free(dv->id);
+}
+
+void
+freedb(Db *db)
+{
+	Dpack *dv, *tdv;
+	
+	for (dv = db->dpack; dv;) {
+		tdv = dv->next;
+		freedpack(dv);
+		dv = tdv;
+	}
+	
+	if (db->file)
+		free(db->file);
+	free(db);
+}
+
+int
+writedb(Db *db, char *file)
+{
+	int fd;
+	Biobuf *bout;
+	Dpack *dv;
+	Dtuple *tp;
+	
+	if (!file)
+		file = db->file;
+	if (!file) {
+		werrstr("no file");
+		return 0;
+	}
+	
+	fd = create(file, OWRITE|OTRUNC, 0666);
+	if (fd < 0) {
+		werrstr("create: %r");
+		return 0;
+	}
+	bout = Bfdopen(fd, OWRITE);
+	if (!bout)
+		sysfatal("%r");
+	
+	// TODO: implement esc sequence
+	for (dv = db->dpack; dv; dv = dv->next) {
+		Bprint(bout, "%s", dv->id);
+		for (tp = dv->tuple; tp; tp = tp->next) {
+			if (tp->value) {
+				Bprint(bout, " %s=%q", tp->key, tp->value);
+			} else {
+				Bprint(bout, " %s", tp->key);
+			}
+		}
+		Bprint(bout, "\n");
+	}
+	
+	Bterm(bout);
+	return 1;
+}
+
+Dpack*
+getdpack(Db *db, char *id)
+{
+	Dpack *dv, *pdv;
+	
+	if (!db->dpack) {
+		db->dpack = mallocz(sizeof(Dpack), 1);
+		db->dpack->id = estrdup(id);
+		return db->dpack;
+	}
+	
+	pdv = nil; /* shut up compiler */
+	for (dv = db->dpack; dv; dv = dv->next) {
+		if (strcmp(dv->id, id) == 0)
+			return dv;
+		pdv = dv;
+	}
+	pdv->next = mallocz(sizeof(Dpack), 1);
+	dv = pdv->next;
+	dv->id = estrdup(id);
+	return dv;
+}
+
+static Dtuple*
+getdtuple(Dpack *dv, char *key)
+{
+	Dtuple *tp, *ptp;
+	
+	if (!dv->tuple) {
+		dv->tuple = mallocz(sizeof(Dtuple), 1);
+		dv->tuple->key = estrdup(key);
+		return dv->tuple;
+	}
+	
+	ptp = nil; /* shut up compiler */
+	for (tp = dv->tuple; tp; tp = tp->next) {
+		if (strcmp(tp->key, key) == 0)
+			return tp;
+		ptp = tp;
+	}
+	ptp->next = mallocz(sizeof(Dtuple), 1);
+	tp = ptp->next;
+	tp->key = estrdup(key);
+	return tp;
+}
+
+void
+setdval(Dpack *dv, char *key, char *value)
+{
+	Dtuple *dt;
+	
+	dt = getdtuple(dv, key);
+	if (dt->value)
+		free(dt->value);
+	dt->value = value ? estrdup(value) : nil;
+}
+
+char*
+getdval(Dpack *dv, char *key, char *def)
+{
+	Dtuple *tp;
+	
+	for (tp = dv->tuple; tp; tp = tp->next) {
+		if (strcmp(tp->key, key) == 0)
+			return tp->value;
+	}
+	return def;
+}
+
+void
+deldtuple(Dpack *dv, char *key)
+{
+	USED(dv, key);
+	sysfatal("deldtuple not implemented yet!");
+}
+
+void
+deldpack(Db *db, Dpack *dv)
+{
+	USED(db, dv);
+	sysfatal("deldpack not implemented yet!");
+}
+
+
+int
+typelen(Stype t)
+{
+	switch (t) {
+	case INT:
+		return sizeof(int);
+	case FLOAT:
+		return sizeof(double);
+	case STRING:
+		return sizeof(char*);
+	}
+	return 0;
+}
+
+void
+serialize(Dpack *dv, void *data, Sdata *desc)
+{
+	char buf[32], *str;
+	Sdata *s;
+	uchar *p;
+	int *ip;
+	double *fp;
+	char **sp;
+	
+	p = (uchar*)data;
+	for (s = desc; s->type; s++) {
+		str = nil;
+		switch (s->type) {
+		case NIL:
+			return;
+		case INT:
+			ip = (int*)p;
+			snprint(buf, sizeof buf, "%d", *ip);
+			str = buf;
+			p += typelen(INT);
+			break;
+		case STRING:
+			sp = (char**)p;
+			str = *sp;
+			p += typelen(STRING);
+			break;
+		case FLOAT:
+			fp = (double*)p;
+			snprint(buf, sizeof buf, "%f", *fp);
+			str = buf;
+			p += typelen(FLOAT);
+			break;
+		}
+		setdval(dv, s->name, str);
+	}
+}
+
+void
+deserialize(Dpack *dv, void *data, Sdata *desc)
+{
+	char *str;
+	Sdata *s;
+	uchar *p;
+	int *ip;
+	double *fp;
+	char **cp;
+	
+	p = (uchar*)data;
+	for (s = desc; s->type; s++) {
+		clog(VERBOSE, "des: '%s' (%d)", s->name, s->type);
+		str = getdval(dv, s->name, s->def);
+		if (!str) {
+			clog(LOG, "des: %s not found", s->name);
+			clog(VERYVERBOSE, "des: ptr: %p", p);
+			p += typelen(s->type);
+			continue;
+		}
+		switch (s->type) {
+		case NIL:
+			return;
+		case INT:
+			ip = (int*)p;
+			*ip = atoi(str);
+			clog(VERYVERBOSE, "des: int: %d", *ip);
+			p += typelen(INT);
+			continue;
+		case STRING:
+			cp = (char**)p;
+			*cp = str;
+			clog(VERYVERBOSE, "des: str: %s", *cp);
+			p += typelen(STRING);
+			continue;
+		case FLOAT:
+			fp = (double*)p;
+			*fp = atof(str);
+			clog(VERYVERBOSE, "des: float: %f", *fp);
+			p += typelen(FLOAT);
+			continue;
+		}
+	}
+}
--- /dev/null
+++ b/db.h
@@ -1,0 +1,73 @@
+typedef struct Db Db;
+typedef struct Dpack Dpack;
+typedef struct Dtuple Dtuple;
+
+/* format:
+<i1> <k1>=<v1> <k2>=<v2>
+<i2> <k1>=<v1> <k2>=<v2>
+*/
+
+struct Db {
+	Dpack *dpack;
+	char *file;
+};
+
+struct Dpack {
+	Dpack *next;
+	char *id;
+	Dtuple *tuple;
+};
+
+struct Dtuple {
+	Dtuple *next;
+	char *key;
+	char *value;
+};
+
+Db *opendb(char *file);
+void freedb(Db*);
+
+int writedb(Db*, char *file);
+
+Dpack *getdpack(Db*, char *id);
+void setdval(Dpack*, char *key, char *value);
+char *getdval(Dpack*, char *key, char *def);
+void deldtuple(Dpack*, char *key);
+void deldpack(Db*, Dpack*);
+
+
+/* serializable structure:
+
+struct mystruct {
+	int a;
+	char *b;
+	int c;
+};
+Sdata data[] = {
+	{ INT, "a", "5" },
+	{ STRING, "b", "hello" },
+	{ INT, "c", nil },
+	{ NIL, nil, nil },
+};
+*/
+
+#define STRUCT(name, type, dtype, dval) dtype name;
+#define SDATA(name, type, dtype, dval) { type, "name", dval },
+#define ENDSDATA { NIL, nil, nil },
+
+typedef enum {
+	NIL = 0,
+	INT,
+	STRING,
+	FLOAT,
+} Stype;
+
+typedef struct Sdata Sdata;
+struct Sdata {
+	Stype type;
+	char *name;
+	char *def;
+};
+
+void serialize(Dpack *dv, void *data, Sdata *desc);
+void deserialize(Dpack *dv, void *data, Sdata *desc);
--- a/eentercolor.c
+++ b/eentercolor.c
@@ -82,8 +82,8 @@
 	return r;
 }
 
-Image *colors = nil;
-uchar *data;
+static Image *colors = nil;
+static uchar *data;
 
 static void
 initimage(double hue)
@@ -119,7 +119,7 @@
 	loadimage(colors, colors->r, data, n);
 }
 
-Image *huegrad = nil;
+static Image *huegrad = nil;
 
 static void
 inithuegrad(void)
@@ -193,6 +193,8 @@
 	return rgb;
 }
 
+static double hue = 0.;
+
 int
 eentercolor(char *ask, ulong *out, Mouse *m)
 {
@@ -203,7 +205,6 @@
 	Point xy;
 	uchar *pixel;
 	int fy;
-	double hue;
 	Rgb rgb;
 	Hsv hsv;
 	Image *blitimg;
@@ -210,8 +211,6 @@
 	Image *selcol;
 	char buf[1 + 3 + 1 + 3 + 1 + 3 + 1 + 1]; /* (XXX,XXX,XXX) */
 	
-	hue = 0.;
-	
 	if (!ask)
 		ask = "color:";
 	
@@ -220,8 +219,7 @@
 	
 	if (!huegrad)
 		inithuegrad();
-	if (!colors)
-		initimage(hue);
+	initimage(hue);
 	
 	pixel = (uchar*)out;
 	
@@ -296,10 +294,12 @@
 				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;
+Setcolor:
+				hsv.h = hue;
 				rgb = hsv2rgb(hsv);
+Setpixel:
 				pixel[3] = rgb.r * 256;
 				pixel[2] = rgb.g * 256;
 				pixel[1] = rgb.b * 256;
@@ -311,18 +311,13 @@
 				xy.x = ev.mouse.xy.x - rh.min.x;
 				hue = ((double)xy.x / 100);
 				initimage(hue);
-				break;
+				goto Setcolor;
 			}
 			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;
+				goto Setpixel;
 			}
 		}
 	}
--- a/mkfile
+++ b/mkfile
@@ -8,6 +8,7 @@
 	layer.$O\
 	sample.$O\
 	eentercolor.$O\
+	db.$O\
 	util.$O\
 	p9image.$O\
 
--- a/p9image.c
+++ b/p9image.c
@@ -6,7 +6,29 @@
 #include <event.h>
 #include <cursor.h>
 #include "blie.h"
+#include "db.h"
 
+#define DEBUG
+static int consfd = -1;
+static void
+clog(char *fmt, ...)
+{
+#ifdef DEBUG
+	va_list args;
+	
+	if (consfd < 0) {
+		consfd = open("#c/cons", OWRITE|OCEXEC);
+		if (consfd < 0)
+			return;
+	}
+	fprint(consfd, "blie-p9image: ");
+	va_start(args, fmt);
+	vfprint(consfd, fmt, args);
+	va_end(args);
+	fprint(consfd, "\n");
+#endif
+}
+
 Cursor ccircle = {
 	{-7, -7},
 	{0xFF, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07,
@@ -24,11 +46,27 @@
 
 Image *tmpcol;
 
+#define SLIST(DO) \
+	DO(value, INT, int, nil)
+
+typedef struct Maskdata Maskdata;
+struct Maskdata {
+	SLIST(STRUCT)
+};
+Sdata smaskdata[] = {
+	SLIST(SDATA)
+	ENDSDATA
+};
+
 typedef struct Data Data;
 struct Data {
 	Memimage *img;
 	Memimage *mask;
-	Drawop op;
+	Memimage *imask;
+	
+	/* serializable values */
+	Db *db;
+	Maskdata *maskdata;
 };
 
 typedef enum {
@@ -151,59 +189,58 @@
 static void
 readcolors(void)
 {
-	int fd, i;
-	Biobuf *bin;
-	char *s;
-	char *args[2];
+	int i;
+	Db *db;
 	ulong val;
+	Dpack *dv;
+	char *tv;
 	
-	fd = open("p9image/colors", OREAD);
-	if (fd < 0)
-		return;
+	db = opendb("p9image/colors");
+	if (!db)
+		sysfatal("cannot read colors: %r");
 	
-	bin = Bfdopen(fd, OREAD);
-	if (!bin)
-		sysfatal("%r");
-	
-	while (s = Brdstr(bin, '\n', 1)) {
-		if (tokenize(s, args, 2) == 2) {
-			i = atoi(args[0]);
-			val = strtoul(args[1], nil, 16);
-			tstate.colordata[i] = val;
-		} else
-			fprint(2, "ignoring line: %s\n", args[0]);
-		free(s);
+	for (dv = db->dpack; dv; dv = dv->next) {
+		tv = getdval(dv, "color", nil);
+		if (!tv)
+			continue;
+		i = atoi(dv->id);
+		val = strtoul(tv, nil, 16);
+		tstate.colordata[i] = val;
 	}
+	freedb(db);
 }
 
 static int
 writecolors(void)
 {
-	int fd, i;
+	int i;
 	ulong c;
+	Dpack *dv;
+	Db *db;
+	char buf[9];
+	
 	if (access("p9image", AEXIST)) {
-		fd = create("p9image", OREAD, DMDIR|0555);
-		if (fd < 0) {
+		i = create("p9image", OREAD, DMDIR|0555);
+		if (i < 0) {
 			werrstr("p9image: %r");
 			return 0;
 		}
-		close(fd);
+		close(i);
 	}
-	fd = open("p9image/colors", OWRITE|OTRUNC);
-	if (fd < 0)
-		fd = create("p9image/colors", OWRITE|OTRUNC, 0666);
-	if (fd < 0) {
-		werrstr("p9image: colors: %r");
-		return 0;
-	}
 	
+	db = opendb(nil);
+	
 	for (i = 0; i < NUMCELLS; i++) {
 		c = tstate.colordata[i];
-		fprint(fd, "%d\t%ulx\n", i, c&0xff ? c : 0);
+		snprint(buf, sizeof(buf), "%d", i);
+		dv = getdpack(db, buf);
+		snprint(buf, sizeof(buf), "%ulx", c&0xff ? c : 0);
+		setdval(dv, "color", buf);
 	}
 	
-	close(fd);
-	return 1;
+	i = writedb(db, "p9image/colors");
+	freedb(db);
+	return i;
 }
 
 static void
@@ -212,11 +249,11 @@
 	tstate.mode = Composite;
 	tstate.drawtarget = DTimg;
 	
-	toolcell = Pt(15, vdata.fontheight + 4);
-	
 	if (headless)
 		return;
 	
+	toolcell = Pt(15, vdata.fontheight + 4);
+	
 	tstate.circle = allocimage(display, Rect(0, 0, 41, 41), RGBA32, 0, DTransparent);
 	ellipse(tstate.circle, Pt(20, 20), 19, 19, 0, display->white, ZP);
 	ellipse(tstate.circle, Pt(20, 20), 20, 20, 0, display->black, ZP);
@@ -289,7 +326,75 @@
 	return 1;
 }
 
+static int
+p9writedata(Layer *l, Data *d)
+{
+	Db *db;
+	Dpack *dv;
+	
+	char buf[128];
+	db = d->db;
+	
+	dv = getdpack(db, "mask");
+	if (dv) {
+		snprint(buf, sizeof buf, "%d", d->maskdata->value);
+		setdval(dv, "value", buf);
+	}
+	
+	return writedb(db, nil);
+}
+
 static void
+updateimask(Data *d)
+{
+	int x, y, dx, dy;
+	double m;
+	uchar *p;
+	
+	if (d->maskdata->value == 255) {
+		d->imask = allocmemimage(d->img->r, GREY8);
+		memfillcolor(d->imask, 0);
+		memimagedraw(d->imask, d->imask->r, memwhite, ZP, d->img, ZP, SoverD);
+		return;
+	}
+	
+	m = (double)d->maskdata->value / 256;
+	dx = Dx(d->img->r);
+	dy = Dy(d->img->r);
+	d->imask = allocmemimage(d->img->r, GREY8);
+	memfillcolor(d->imask, 0);
+	memimagedraw(d->imask, d->imask->r, memwhite, ZP, d->img, ZP, SoverD);
+	
+	for (y = 0; y < dy; y++) {
+		for (x = 0; x < dx; x++) {
+			p = byteaddr(d->imask, Pt(x, y));
+			*p = *p * m;
+		}
+	}
+}
+
+static void
+p9readdata(Layer *l, Data *d)
+{
+	Db *db;
+	Dpack *dv;
+	char *s;
+	int i;
+	
+	clog("readdata: %s", l->name);
+	
+	s = smprint("l/%s/data", l->name);
+	db = opendb(s);
+	free(s);
+	if (!db)
+		return;
+	d->db = db;
+	
+	dv = getdpack(db, "mask");
+	deserialize(dv, d->maskdata, smaskdata);
+}
+
+static void
 p9init(Layer *l)
 {
 	int fd;
@@ -300,7 +405,10 @@
 		return;
 	d = mallocz(sizeof(Data), 1);
 	l->data = d;
+	d->maskdata = mallocz(sizeof(Maskdata), 1);
 	
+	p9readdata(l, d);
+	
 	/* image file */
 	s = smprint("l/%s/img", l->name);
 	fd = open(s, OREAD);
@@ -323,6 +431,7 @@
 	fd = open(s, OREAD);
 	if (fd < 0) {
 		free(s);
+		updateimask(d);
 		return;
 	}
 	free(s);
@@ -334,6 +443,7 @@
 		d->mask = readmemimage(fd);
 	}
 	close(fd);
+	updateimask(d);
 }
 
 /* just use ecompose, which uses raw() and mask() */
@@ -366,6 +476,8 @@
 {
 	Data *d;
 	d = (Data*)l->data;
+	if (d->imask)
+		return d->imask;
 	return d->mask;
 }
 
@@ -387,7 +499,11 @@
 		mi = data->img;
 		goto Mout;
 	case Mask:
-		mi = data->mask;
+		if (data->imask) {
+			mi = data->imask;
+			goto Mout;
+		}
+		mi = nil;
 		goto Mout;
 	}
 	changecursor(nil, nil, ZP);
@@ -474,7 +590,9 @@
 static int
 p9savedata(Layer *l)
 {
-	return writelayer(l);
+	Data *d;
+	d = (Data*)l->data;
+	return writelayer(l) && p9writedata(l, d);
 }
 
 static int
@@ -529,6 +647,7 @@
 	
 	color->clipr = brush->i->r;
 	memimagedraw(tgt, r, color, ZP, brush->i, ZP, SoverD);
+	updateimask(d);
 	setdrawingdirty(Dcontent);
 	dirtylayer(l);
 	return Rdrawing;
binary files a/test/img/l/layerB/mask /dev/null differ