xref: /plan9/sys/src/cmd/mug.c (revision 72061b92c86a7d2b16e3ab31edd3846f53c84345)
11052a86aSDavid du Colombier #include <u.h>
21052a86aSDavid du Colombier #include <libc.h>
31052a86aSDavid du Colombier #include <draw.h>
41052a86aSDavid du Colombier #include <event.h>
51052a86aSDavid du Colombier #include <cursor.h>
61052a86aSDavid du Colombier 
71052a86aSDavid du Colombier #define initstate muginitstate
81052a86aSDavid du Colombier 
91052a86aSDavid du Colombier typedef struct State State;
101052a86aSDavid du Colombier struct State {
111052a86aSDavid du Colombier 	double black;
121052a86aSDavid du Colombier 	double white;
131052a86aSDavid du Colombier 	double stretch;
141052a86aSDavid du Colombier 	double gamma;
151052a86aSDavid du Colombier 	int depth;
161052a86aSDavid du Colombier 	int gtab[1001];
171052a86aSDavid du Colombier 	Rectangle selr;
181052a86aSDavid du Colombier };
191052a86aSDavid du Colombier 
201052a86aSDavid du Colombier typedef struct Face Face;
211052a86aSDavid du Colombier struct Face {
221052a86aSDavid du Colombier 	Rectangle r;
231052a86aSDavid du Colombier 	State state;
241052a86aSDavid du Colombier 	Image *small;
251052a86aSDavid du Colombier };
261052a86aSDavid du Colombier 
271052a86aSDavid du Colombier double GAMMA = 1.0;		/* theory tells me this should be 2.2, but 1.0 sure looks better */
281052a86aSDavid du Colombier enum {
291052a86aSDavid du Colombier 	Left=0,
301052a86aSDavid du Colombier 	Right,
311052a86aSDavid du Colombier 	Top,
321052a86aSDavid du Colombier 	Bottom,
331052a86aSDavid du Colombier 
341052a86aSDavid du Colombier 	RTopLeft=0,
351052a86aSDavid du Colombier 	RTop,
361052a86aSDavid du Colombier 	RTopRight,
371052a86aSDavid du Colombier 	RLeft,
381052a86aSDavid du Colombier 	RMiddle,
391052a86aSDavid du Colombier 	RRight,
401052a86aSDavid du Colombier 	RBotLeft,
411052a86aSDavid du Colombier 	RBot,
421052a86aSDavid du Colombier 	RBotRight,
431052a86aSDavid du Colombier };
441052a86aSDavid du Colombier 
451052a86aSDavid du Colombier void*
emalloc(ulong sz)461052a86aSDavid du Colombier emalloc(ulong sz)
471052a86aSDavid du Colombier {
481052a86aSDavid du Colombier 	void *v;
491052a86aSDavid du Colombier 
501052a86aSDavid du Colombier 	v = malloc(sz);
511052a86aSDavid du Colombier 	if(v == nil)
5214cc0f53SDavid du Colombier 		sysfatal("malloc %lud fails", sz);
531052a86aSDavid du Colombier 	memset(v, 0, sz);
541052a86aSDavid du Colombier 	return v;
551052a86aSDavid du Colombier }
561052a86aSDavid du Colombier 
571052a86aSDavid du Colombier Face *face[8];
581052a86aSDavid du Colombier int nface;
591052a86aSDavid du Colombier uchar grey2cmap[256];
601052a86aSDavid du Colombier Image *bkgd;
611052a86aSDavid du Colombier Image *orig;
621052a86aSDavid du Colombier Image *ramp, *small, *osmall, *tmp8, *red, *green, *blue;
631052a86aSDavid du Colombier State state, ostate;
641052a86aSDavid du Colombier uchar val2cmap[256];
651052a86aSDavid du Colombier uchar clamp[3*256];
661052a86aSDavid du Colombier Rectangle rbig, rramp, rface[nelem(face)], rsmall;
671052a86aSDavid du Colombier double *rdata;
681052a86aSDavid du Colombier int sdy, sdx;
691052a86aSDavid du Colombier 
701052a86aSDavid du Colombier void
geometry(Rectangle r)711052a86aSDavid du Colombier geometry(Rectangle r)
721052a86aSDavid du Colombier {
731052a86aSDavid du Colombier 	int i;
741052a86aSDavid du Colombier 	Rectangle fr[9];
751052a86aSDavid du Colombier 
761052a86aSDavid du Colombier 	rramp.min = addpt(r.min, Pt(4,4));
771052a86aSDavid du Colombier 	rramp.max = addpt(rramp.min, Pt(256,256));
781052a86aSDavid du Colombier 
791052a86aSDavid du Colombier 	rbig.min = Pt(rramp.max.x+6, rramp.min.y);
801052a86aSDavid du Colombier 	rbig.max = addpt(rbig.min, Pt(Dx(orig->r), Dy(orig->r)));
811052a86aSDavid du Colombier 
821052a86aSDavid du Colombier 	for(i=0; i<9; i++)
831052a86aSDavid du Colombier 		fr[i] = rectaddpt(Rect(0,0,48,48), Pt(rramp.min.x+48+56*(i%3), rramp.max.y+6+56*(i/3)));
841052a86aSDavid du Colombier 
851052a86aSDavid du Colombier 	rsmall = fr[4];
861052a86aSDavid du Colombier 	for(i=0; i<4; i++)
871052a86aSDavid du Colombier 		rface[i] = fr[i];
881052a86aSDavid du Colombier 	for(i=4; i<8; i++)
891052a86aSDavid du Colombier 		rface[i] = fr[i+1];
901052a86aSDavid du Colombier }
911052a86aSDavid du Colombier 
921052a86aSDavid du Colombier double
y2gamma(int y)931052a86aSDavid du Colombier y2gamma(int y)
941052a86aSDavid du Colombier {
951052a86aSDavid du Colombier 	double g;
961052a86aSDavid du Colombier 
971052a86aSDavid du Colombier 	g = (double)y / 128.0;
981052a86aSDavid du Colombier 	return 0.5+g*g;		/* gamma from 0.5 to 4.5, with 1.0 near the middle */
991052a86aSDavid du Colombier }
1001052a86aSDavid du Colombier 
1011052a86aSDavid du Colombier int
gamma2y(double g)1021052a86aSDavid du Colombier gamma2y(double g)
1031052a86aSDavid du Colombier {
1041052a86aSDavid du Colombier 	g -= 0.5;
1051052a86aSDavid du Colombier 	return (int)(128.0*sqrt(g)+0.5);
1061052a86aSDavid du Colombier }
1071052a86aSDavid du Colombier 
1081052a86aSDavid du Colombier void
drawface(int i)1091052a86aSDavid du Colombier drawface(int i)
1101052a86aSDavid du Colombier {
1111052a86aSDavid du Colombier 	if(i==-1){
1121052a86aSDavid du Colombier 		border(screen, rsmall, -3, blue, ZP);
1131052a86aSDavid du Colombier 		draw(screen, rsmall, small, nil, ZP);
1141052a86aSDavid du Colombier 		return;
1151052a86aSDavid du Colombier 	}
1161052a86aSDavid du Colombier 	border(screen, rface[i], -1, display->black, ZP);
1171052a86aSDavid du Colombier 	if(face[i])
1181052a86aSDavid du Colombier 		draw(screen, rface[i], face[i]->small, nil, ZP);
1191052a86aSDavid du Colombier 	else
1201052a86aSDavid du Colombier 		draw(screen, rface[i], display->white, nil, ZP);
1211052a86aSDavid du Colombier }
1221052a86aSDavid du Colombier 
1231052a86aSDavid du Colombier void
drawrampbar(Image * color,State * s)1241052a86aSDavid du Colombier drawrampbar(Image *color, State *s)
1251052a86aSDavid du Colombier {
1261052a86aSDavid du Colombier 	Rectangle liner, r;
1271052a86aSDavid du Colombier 	static Rectangle br;
1281052a86aSDavid du Colombier 
1291052a86aSDavid du Colombier 	if(Dx(br))
1301052a86aSDavid du Colombier 		draw(screen, br, ramp, nil, subpt(br.min, rramp.min));
1311052a86aSDavid du Colombier 
1321052a86aSDavid du Colombier 	r = rramp;
1331052a86aSDavid du Colombier 	r.max.x = r.min.x + (int)(s->white*255.0);
1341052a86aSDavid du Colombier 	r.min.x += (int)(s->black*255.0);
1351052a86aSDavid du Colombier 	r.min.y += gamma2y(s->gamma);
1361052a86aSDavid du Colombier 	r.max.y = r.min.y+1;
1371052a86aSDavid du Colombier 	rectclip(&r, rramp);
1381052a86aSDavid du Colombier 	draw(screen, r, color, nil, ZP);
1391052a86aSDavid du Colombier 	br = r;
1401052a86aSDavid du Colombier 
1411052a86aSDavid du Colombier 	r.min.y -= 2;
1421052a86aSDavid du Colombier 	r.max.y += 2;
1431052a86aSDavid du Colombier 
1441052a86aSDavid du Colombier 	liner = r;
1451052a86aSDavid du Colombier 	r.min.x += Dx(liner)/3;
1461052a86aSDavid du Colombier 	r.max.x -= Dx(liner)/3;
1471052a86aSDavid du Colombier 	rectclip(&r, rramp);
1481052a86aSDavid du Colombier 	draw(screen, r, color, nil, ZP);
1491052a86aSDavid du Colombier 	combinerect(&br, r);
1501052a86aSDavid du Colombier 
1511052a86aSDavid du Colombier 	r = liner;
1521052a86aSDavid du Colombier 	r.max.x = r.min.x+3;
1531052a86aSDavid du Colombier 	rectclip(&r, rramp);
1541052a86aSDavid du Colombier 	draw(screen, r, color, nil, ZP);
1551052a86aSDavid du Colombier 	combinerect(&br, r);
1561052a86aSDavid du Colombier 
1571052a86aSDavid du Colombier 	r = liner;
1581052a86aSDavid du Colombier 	r.min.x = r.max.x-3;
1591052a86aSDavid du Colombier 	rectclip(&r, rramp);
1601052a86aSDavid du Colombier 	draw(screen, r, color, nil, ZP);
1611052a86aSDavid du Colombier 	combinerect(&br, r);
1621052a86aSDavid du Colombier }
1631052a86aSDavid du Colombier 
1641052a86aSDavid du Colombier void
drawscreen(int clear)1651052a86aSDavid du Colombier drawscreen(int clear)
1661052a86aSDavid du Colombier {
1671052a86aSDavid du Colombier 	int i;
1681052a86aSDavid du Colombier 
1691052a86aSDavid du Colombier 	if(clear){
1701052a86aSDavid du Colombier 		geometry(screen->r);
1711052a86aSDavid du Colombier 		draw(screen, screen->r, bkgd, nil, ZP);
1721052a86aSDavid du Colombier 	}
1731052a86aSDavid du Colombier 
1741052a86aSDavid du Colombier 	border(screen, rbig, -1, display->black, ZP);
1751052a86aSDavid du Colombier 	draw(screen, rbig, orig, nil, orig->r.min);
1761052a86aSDavid du Colombier 
1771052a86aSDavid du Colombier 	border(screen, rramp, -1, display->black, ZP);
1781052a86aSDavid du Colombier 	draw(screen, rramp, ramp, nil, ramp->r.min);
1791052a86aSDavid du Colombier 	drawrampbar(red, &state);
1801052a86aSDavid du Colombier 
1811052a86aSDavid du Colombier 	border(screen, rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), -2, red, ZP);
1821052a86aSDavid du Colombier 	if(clear){
1831052a86aSDavid du Colombier 		drawface(-1);
1841052a86aSDavid du Colombier 		for(i=0; i<nelem(face); i++)
1851052a86aSDavid du Colombier 			drawface(i);
1861052a86aSDavid du Colombier 	}
1871052a86aSDavid du Colombier }
1881052a86aSDavid du Colombier 
1891052a86aSDavid du Colombier void
moveframe(Rectangle old,Rectangle new)1901052a86aSDavid du Colombier moveframe(Rectangle old, Rectangle new)
1911052a86aSDavid du Colombier {
1921052a86aSDavid du Colombier 	border(screen, rectaddpt(old, subpt(rbig.min, orig->r.min)), -2, orig, old.min);
1931052a86aSDavid du Colombier 	border(screen, rectaddpt(new, subpt(rbig.min, orig->r.min)), -2, red, ZP);
1941052a86aSDavid du Colombier }
1951052a86aSDavid du Colombier 
1961052a86aSDavid du Colombier 
1971052a86aSDavid du Colombier /*
1981052a86aSDavid du Colombier  * Initialize gamma ramp; should dither for
1991052a86aSDavid du Colombier  * benefit of non-true-color displays.
2001052a86aSDavid du Colombier  */
2011052a86aSDavid du Colombier void
initramp(void)2021052a86aSDavid du Colombier initramp(void)
2031052a86aSDavid du Colombier {
2041052a86aSDavid du Colombier 	int k, x, y;
2051052a86aSDavid du Colombier 	uchar dat[256*256];
2061052a86aSDavid du Colombier 	double g;
2071052a86aSDavid du Colombier 
2081052a86aSDavid du Colombier 	k = 0;
2091052a86aSDavid du Colombier 	for(y=0; y<256; y++) {
2101052a86aSDavid du Colombier 		g = y2gamma(y);
2111052a86aSDavid du Colombier 		for(x=0; x<256; x++)
2121052a86aSDavid du Colombier 			dat[k++] = 255.0 * pow(x/255.0, g);
2131052a86aSDavid du Colombier 	}
2141052a86aSDavid du Colombier 	assert(k == sizeof dat);
2151052a86aSDavid du Colombier 
2161052a86aSDavid du Colombier 	ramp = allocimage(display, Rect(0,0,256,256), GREY8, 0, DNofill);
2171052a86aSDavid du Colombier 	if(ramp == nil)
2181052a86aSDavid du Colombier 		sysfatal("allocimage: %r");
2191052a86aSDavid du Colombier 
2201052a86aSDavid du Colombier 	if(loadimage(ramp, ramp->r, dat, sizeof dat) != sizeof dat)
2211052a86aSDavid du Colombier 		sysfatal("loadimage: %r");
2221052a86aSDavid du Colombier }
2231052a86aSDavid du Colombier 
2241052a86aSDavid du Colombier void
initclamp(void)2251052a86aSDavid du Colombier initclamp(void)
2261052a86aSDavid du Colombier {
2271052a86aSDavid du Colombier 	int i;
2281052a86aSDavid du Colombier 
2291052a86aSDavid du Colombier 	for(i=0; i<256; i++) {
2301052a86aSDavid du Colombier 		clamp[i] = 0;
2311052a86aSDavid du Colombier 		clamp[256+i] = i;
2321052a86aSDavid du Colombier 		clamp[512+i] = 255;
2331052a86aSDavid du Colombier 	}
2341052a86aSDavid du Colombier }
2351052a86aSDavid du Colombier 
2361052a86aSDavid du Colombier void
changestretch(double stretch)2371052a86aSDavid du Colombier changestretch(double stretch)
2381052a86aSDavid du Colombier {
2391052a86aSDavid du Colombier 	state.stretch = stretch;
2401052a86aSDavid du Colombier }
2411052a86aSDavid du Colombier 
2421052a86aSDavid du Colombier /*
2431052a86aSDavid du Colombier  * There is greyscale data for the rectangle datar in data;
2441052a86aSDavid du Colombier  * extract square r and write it into the 48x48 pixel image small.
2451052a86aSDavid du Colombier  */
2461052a86aSDavid du Colombier void
process(double * data,Rectangle datar,Rectangle r,Image * small)2471052a86aSDavid du Colombier process(double *data, Rectangle datar, Rectangle r, Image *small)
2481052a86aSDavid du Colombier {
2491052a86aSDavid du Colombier 	double black, center, delta, *k, shrink, sum, *tmp[48], *tt, w, white, x;
2501052a86aSDavid du Colombier 	int datadx, dp, dx, dy, error, i, ii, j, jj;
2511052a86aSDavid du Colombier 	int ksize, ksizeby2, sdata[48*48], sd, sh, sm, sv, u, uu, uuu, v, vv;
2521052a86aSDavid du Colombier 	uchar bdata[48*48];
2531052a86aSDavid du Colombier 
2541052a86aSDavid du Colombier 	datadx = Dx(datar);
2551052a86aSDavid du Colombier 	dx = Dx(r);
2561052a86aSDavid du Colombier 	dy = Dy(r);
2571052a86aSDavid du Colombier 	shrink = dx/48.0;
2581052a86aSDavid du Colombier 
2591052a86aSDavid du Colombier 	ksize = 1+2*(int)(shrink/2.0);
2601052a86aSDavid du Colombier 	if(ksize <= 2)
2611052a86aSDavid du Colombier 		return;
2621052a86aSDavid du Colombier 
2631052a86aSDavid du Colombier 	k = emalloc(ksize*sizeof(k[0]));
2641052a86aSDavid du Colombier 
2651052a86aSDavid du Colombier 	/* center of box */
2661052a86aSDavid du Colombier 	for(i=1; i<ksize-1; i++)
2671052a86aSDavid du Colombier 		k[i] = 1.0;
2681052a86aSDavid du Colombier 
2691052a86aSDavid du Colombier 	/* edges */
2701052a86aSDavid du Colombier 	x = shrink - floor(shrink);
2711052a86aSDavid du Colombier 	k[0] = x;
2721052a86aSDavid du Colombier 	k[ksize-1] = x;
2731052a86aSDavid du Colombier 
2741052a86aSDavid du Colombier 	sum = 0.0;
2751052a86aSDavid du Colombier 	for(i=0; i<ksize; i++)
2761052a86aSDavid du Colombier 		sum += k[i];
2771052a86aSDavid du Colombier 
2781052a86aSDavid du Colombier 	for(i=0; i<ksize; i++)
2791052a86aSDavid du Colombier 		k[i] /= sum;
2801052a86aSDavid du Colombier 
2811052a86aSDavid du Colombier 	ksizeby2 = ksize/2;
2821052a86aSDavid du Colombier 
2831052a86aSDavid du Colombier 	for(i=0; i<48; i++)
2841052a86aSDavid du Colombier 		tmp[i] = emalloc(datadx*sizeof(tmp[i][0]));
2851052a86aSDavid du Colombier 
2861052a86aSDavid du Colombier 	/* squeeze vertically */
2871052a86aSDavid du Colombier 	for(i=0; i<48; i++) {
2881052a86aSDavid du Colombier 		ii = r.min.y+i*dy/48;
2891052a86aSDavid du Colombier 		tt = tmp[i];
2901052a86aSDavid du Colombier 		uu = ii - ksizeby2;
2911052a86aSDavid du Colombier 		for(j=r.min.x-ksize; j<r.max.x+ksize; j++) {
2921052a86aSDavid du Colombier 			if(j<datar.min.x || j>=datar.max.x)
2931052a86aSDavid du Colombier 				continue;
2941052a86aSDavid du Colombier 			w = 0.0;
2951052a86aSDavid du Colombier 
2961052a86aSDavid du Colombier 			uuu = uu*datadx+j;
2971052a86aSDavid du Colombier 			if(uu>=datar.min.y && uu+ksize<datar.max.y)
2981052a86aSDavid du Colombier 				for(u=0; u<ksize; u++){
2991052a86aSDavid du Colombier 					w += k[u]*data[uuu];
3001052a86aSDavid du Colombier 					uuu += datadx;
3011052a86aSDavid du Colombier 				}
3021052a86aSDavid du Colombier 			else
3031052a86aSDavid du Colombier 				for(u=0; u<ksize; u++){
3041052a86aSDavid du Colombier 					if(uu+u>=datar.min.y && uu+u<datar.max.y)
3051052a86aSDavid du Colombier 						w += k[u]*data[uuu];
3061052a86aSDavid du Colombier 					uuu+=datadx;
3071052a86aSDavid du Colombier 				}
3081052a86aSDavid du Colombier 			tt[j-datar.min.x] = w;
3091052a86aSDavid du Colombier 		}
3101052a86aSDavid du Colombier 	}
3111052a86aSDavid du Colombier 
3121052a86aSDavid du Colombier 	/* stretch value scale */
3131052a86aSDavid du Colombier 	center = (state.black+state.white)/2;
3141052a86aSDavid du Colombier 	delta = state.stretch*(state.white-state.black)/2;
3151052a86aSDavid du Colombier 	black = center - delta;
3161052a86aSDavid du Colombier 	white = center + delta;
3171052a86aSDavid du Colombier 
3181052a86aSDavid du Colombier 	/* squeeze horizontally */
3191052a86aSDavid du Colombier 	for(i=0; i<48; i++) {
3201052a86aSDavid du Colombier 		tt = tmp[i];
3211052a86aSDavid du Colombier 		for(j=0; j<48; j++) {
3221052a86aSDavid du Colombier 			jj = r.min.x+j*dx/48;
3231052a86aSDavid du Colombier 			w = 0.0;
3241052a86aSDavid du Colombier 			for(v=0; v<ksize; v++) {
3251052a86aSDavid du Colombier 				vv = jj - ksizeby2 + v;
3261052a86aSDavid du Colombier 				if(vv<datar.min.x || vv>=datar.max.x) {
3271052a86aSDavid du Colombier 					w += k[v];		/* assume white surround */
3281052a86aSDavid du Colombier 					continue;
3291052a86aSDavid du Colombier 				}
3301052a86aSDavid du Colombier 				w += k[v]*tt[vv-datar.min.x];
3311052a86aSDavid du Colombier 			}
3321052a86aSDavid du Colombier 			if(w < black || black==white)
3331052a86aSDavid du Colombier 				w = 0.0;
3341052a86aSDavid du Colombier 			else if(w > white)
3351052a86aSDavid du Colombier 				w = 1.0;
3361052a86aSDavid du Colombier 			else
3371052a86aSDavid du Colombier 				w = (w-black)/(white-black);
3381052a86aSDavid du Colombier 			sdata[i*48+j] = state.gtab[(int)(1000.0*w)];
3391052a86aSDavid du Colombier 		}
3401052a86aSDavid du Colombier 	}
3411052a86aSDavid du Colombier 
3421052a86aSDavid du Colombier 	/* dither to lower depth before copying into GREY8 version */
3431052a86aSDavid du Colombier 	if(small->chan != GREY8) {
3441052a86aSDavid du Colombier 		u = 0;
3451052a86aSDavid du Colombier 		dp = small->depth;
3461052a86aSDavid du Colombier 		for(i=0; i<48; i++) {
3471052a86aSDavid du Colombier 			sm = 0xFF ^ (0xFF>>dp);
3481052a86aSDavid du Colombier 			sh = 0;
3491052a86aSDavid du Colombier 			v = 0;
3501052a86aSDavid du Colombier 			for(j=0; j<48; j++) {
3511052a86aSDavid du Colombier 				ii = 48*i+j;
3521052a86aSDavid du Colombier 				sd = clamp[sdata[ii]+256];
3531052a86aSDavid du Colombier 				sv = sd&sm;
3541052a86aSDavid du Colombier 				v |= sv>>sh;
3551052a86aSDavid du Colombier 				sh += dp;
3561052a86aSDavid du Colombier 				if(sh == 8) {
3571052a86aSDavid du Colombier 					bdata[u++] = v;
3581052a86aSDavid du Colombier 					v = 0;
3591052a86aSDavid du Colombier 					sh = 0;
3601052a86aSDavid du Colombier 				}
3611052a86aSDavid du Colombier 
3621052a86aSDavid du Colombier 				/* propagate error, with decay (sum errors < 1) */
3631052a86aSDavid du Colombier 				error = sd - sv;
3641052a86aSDavid du Colombier 				if(ii+49 < 48*48) {	/* one test is enough, really */
3651052a86aSDavid du Colombier 					sdata[ii+1] = sdata[ii+1]+((3*error)>>4);
3661052a86aSDavid du Colombier 					sdata[ii+48] = sdata[ii+48]+((3*error)>>4);
3671052a86aSDavid du Colombier 					sdata[ii+49] = sdata[ii+49]+((3*error)>>3);
3681052a86aSDavid du Colombier 				}
3691052a86aSDavid du Colombier 
3701052a86aSDavid du Colombier 				/* produce correct color map value by copying bits */
3711052a86aSDavid du Colombier 				switch(dp){
3721052a86aSDavid du Colombier 				case 1:
3731052a86aSDavid du Colombier 					sv |= sv>>1;
3741052a86aSDavid du Colombier 				case 2:
3751052a86aSDavid du Colombier 					sv |= sv>>2;
3761052a86aSDavid du Colombier 				case 4:
3771052a86aSDavid du Colombier 					sv |= sv>>4;
3781052a86aSDavid du Colombier 				}
3791052a86aSDavid du Colombier 				sdata[ii] = sv;
3801052a86aSDavid du Colombier 			}
3811052a86aSDavid du Colombier 		}
3821052a86aSDavid du Colombier 		for(i=0; i<nelem(bdata); i++)
3831052a86aSDavid du Colombier 			bdata[i] = sdata[i];
3841052a86aSDavid du Colombier 		if(loadimage(tmp8, tmp8->r, bdata, sizeof bdata) != sizeof bdata)
3851052a86aSDavid du Colombier 			sysfatal("loadimage: %r");
3861052a86aSDavid du Colombier 		draw(small, small->r, tmp8, nil, tmp8->r.min);
3871052a86aSDavid du Colombier 	} else {
3881052a86aSDavid du Colombier 		for(i=0; i<nelem(bdata); i++)
3891052a86aSDavid du Colombier 			bdata[i] = sdata[i];
3901052a86aSDavid du Colombier 		if(loadimage(small, small->r, bdata, sizeof bdata) != sizeof bdata)
3911052a86aSDavid du Colombier 			sysfatal("loadimage: %r");
3921052a86aSDavid du Colombier 	}
3931052a86aSDavid du Colombier 
3941052a86aSDavid du Colombier 	free(k);
3951052a86aSDavid du Colombier 	for(i=0; i<48; i++)
3961052a86aSDavid du Colombier 		free(tmp[i]);
3971052a86aSDavid du Colombier }
3981052a86aSDavid du Colombier 
3991052a86aSDavid du Colombier void
initval2cmap(void)4001052a86aSDavid du Colombier initval2cmap(void)
4011052a86aSDavid du Colombier {
4021052a86aSDavid du Colombier 	int i;
4031052a86aSDavid du Colombier 
4041052a86aSDavid du Colombier 	for(i=0; i<256; i++)
4051052a86aSDavid du Colombier 		val2cmap[i] = rgb2cmap(i, i, i);
4061052a86aSDavid du Colombier }
4071052a86aSDavid du Colombier 
4081052a86aSDavid du Colombier void
setgtab(State * s)4091052a86aSDavid du Colombier setgtab(State *s)
4101052a86aSDavid du Colombier {
4111052a86aSDavid du Colombier 	int i;
4121052a86aSDavid du Colombier 
4131052a86aSDavid du Colombier 	for(i=0; i<=1000; i++)
4141052a86aSDavid du Colombier 		s->gtab[i] = val2cmap[(int)(255.0*pow((i/1000.0), 1.0/s->gamma))];
4151052a86aSDavid du Colombier }
4161052a86aSDavid du Colombier 
4171052a86aSDavid du Colombier int
section(int x)4181052a86aSDavid du Colombier section(int x)
4191052a86aSDavid du Colombier {
4201052a86aSDavid du Colombier 	int ib, iw;
4211052a86aSDavid du Colombier 
4221052a86aSDavid du Colombier 	ib = state.black * 255.0;
4231052a86aSDavid du Colombier 	iw = state.white * 255.0;
4241052a86aSDavid du Colombier 
4251052a86aSDavid du Colombier 	if(x<ib-5 || iw+5<x)
4261052a86aSDavid du Colombier 		return -1;
4271052a86aSDavid du Colombier 
4281052a86aSDavid du Colombier 	iw -= ib;
4291052a86aSDavid du Colombier 	x -= ib;
4301052a86aSDavid du Colombier 	if(x < iw/3)
4311052a86aSDavid du Colombier 		return 0;
4321052a86aSDavid du Colombier 	if(x < 2*iw/3)
4331052a86aSDavid du Colombier 		return 1;
4341052a86aSDavid du Colombier 	return 2;
4351052a86aSDavid du Colombier }
4361052a86aSDavid du Colombier 
4371052a86aSDavid du Colombier Image*
copyimage(Image * i)4381052a86aSDavid du Colombier copyimage(Image *i)
4391052a86aSDavid du Colombier {
4401052a86aSDavid du Colombier 	Image *n;
4411052a86aSDavid du Colombier 
4421052a86aSDavid du Colombier 	if(i == nil)
4431052a86aSDavid du Colombier 		return nil;
4441052a86aSDavid du Colombier 
4451052a86aSDavid du Colombier 	n = allocimage(display, i->r, i->chan, 0, DNofill);
4461052a86aSDavid du Colombier 	if(n == nil)
4471052a86aSDavid du Colombier 		sysfatal("allocimage: %r");
4481052a86aSDavid du Colombier 
4491052a86aSDavid du Colombier 	draw(n, n->r, i, nil, i->r.min);
4501052a86aSDavid du Colombier 	return n;
4511052a86aSDavid du Colombier }
4521052a86aSDavid du Colombier 
4531052a86aSDavid du Colombier Image*
grey8image(Image * i)4541052a86aSDavid du Colombier grey8image(Image *i)
4551052a86aSDavid du Colombier {
4561052a86aSDavid du Colombier 	Image *n;
4571052a86aSDavid du Colombier 
4581052a86aSDavid du Colombier 	if(i->chan == GREY8)
4591052a86aSDavid du Colombier 		return i;
4601052a86aSDavid du Colombier 
4611052a86aSDavid du Colombier 	n = allocimage(display, i->r, GREY8, 0, DNofill);
4621052a86aSDavid du Colombier 	if(n == nil)
4631052a86aSDavid du Colombier 		sysfatal("allocimage: %r");
4641052a86aSDavid du Colombier 
4651052a86aSDavid du Colombier 	draw(n, n->r, i, nil, i->r.min);
4661052a86aSDavid du Colombier 	freeimage(i);
4671052a86aSDavid du Colombier 	return n;
4681052a86aSDavid du Colombier }
4691052a86aSDavid du Colombier 
4701052a86aSDavid du Colombier 
4711052a86aSDavid du Colombier void
mark(void)4721052a86aSDavid du Colombier mark(void)
4731052a86aSDavid du Colombier {
4741052a86aSDavid du Colombier 	if(osmall != small){
4751052a86aSDavid du Colombier 		freeimage(osmall);
4761052a86aSDavid du Colombier 		osmall = small;
4771052a86aSDavid du Colombier 	}
4781052a86aSDavid du Colombier 	ostate = state;
4791052a86aSDavid du Colombier }
4801052a86aSDavid du Colombier 
4811052a86aSDavid du Colombier void
undo(void)4821052a86aSDavid du Colombier undo(void)
4831052a86aSDavid du Colombier {
4841052a86aSDavid du Colombier 	if(small != osmall){
4851052a86aSDavid du Colombier 		freeimage(small);
4861052a86aSDavid du Colombier 		small = osmall;
4871052a86aSDavid du Colombier 	}
4881052a86aSDavid du Colombier 	state = ostate;
4891052a86aSDavid du Colombier 	process(rdata, orig->r, state.selr, small);
4901052a86aSDavid du Colombier 	drawface(-1);
4911052a86aSDavid du Colombier 	drawscreen(0);
4921052a86aSDavid du Colombier }
4931052a86aSDavid du Colombier 
4941052a86aSDavid du Colombier void
saveface(Face * f,int slot)4951052a86aSDavid du Colombier saveface(Face *f, int slot)
4961052a86aSDavid du Colombier {
4971052a86aSDavid du Colombier 	if(slot == -1){
4981052a86aSDavid du Colombier 		mark();
4991052a86aSDavid du Colombier 		state = f->state;
5001052a86aSDavid du Colombier 		small = copyimage(f->small);
5011052a86aSDavid du Colombier 		drawface(-1);
5021052a86aSDavid du Colombier 		drawscreen(0);
5031052a86aSDavid du Colombier 		return;
5041052a86aSDavid du Colombier 	}
5051052a86aSDavid du Colombier 
5061052a86aSDavid du Colombier 	if(face[slot]==nil)
5071052a86aSDavid du Colombier 		face[slot] = emalloc(sizeof(*face[slot]));
5081052a86aSDavid du Colombier 	else{
5091052a86aSDavid du Colombier 		freeimage(face[slot]->small);
5101052a86aSDavid du Colombier 		face[slot]->small = nil;
5111052a86aSDavid du Colombier 	}
5121052a86aSDavid du Colombier 
5131052a86aSDavid du Colombier 	if(f == nil){
5141052a86aSDavid du Colombier 		face[slot]->small = copyimage(small);
5151052a86aSDavid du Colombier 		face[slot]->state = state;
5161052a86aSDavid du Colombier 	}else{
5171052a86aSDavid du Colombier 		face[slot]->small = copyimage(f->small);
5181052a86aSDavid du Colombier 		face[slot]->state = f->state;
5191052a86aSDavid du Colombier 	}
5201052a86aSDavid du Colombier 	drawface(slot);
5211052a86aSDavid du Colombier }
5221052a86aSDavid du Colombier 
5231052a86aSDavid du Colombier int
writeface(char * outfile,Image * image)5241052a86aSDavid du Colombier writeface(char *outfile, Image *image)
5251052a86aSDavid du Colombier {
5261052a86aSDavid du Colombier 	int i, fd, rv, y;
5271052a86aSDavid du Colombier 	uchar data[48*48/2];
5281052a86aSDavid du Colombier 
5291052a86aSDavid du Colombier 	if(outfile == nil)
5301052a86aSDavid du Colombier 		fd = 1;
5311052a86aSDavid du Colombier 	else{
5321052a86aSDavid du Colombier 		if((fd = create(outfile, OWRITE, 0666)) < 0)
5331052a86aSDavid du Colombier 			return -1;
5341052a86aSDavid du Colombier 	}
5351052a86aSDavid du Colombier 
5361052a86aSDavid du Colombier 	switch(image->chan) {
5371052a86aSDavid du Colombier 	default:
5381052a86aSDavid du Colombier 		rv = -1;
5391052a86aSDavid du Colombier 		break;
5401052a86aSDavid du Colombier 
5411052a86aSDavid du Colombier 	case GREY1:
5421052a86aSDavid du Colombier 		if(unloadimage(image, image->r, data, 48*48/8) != 48*48/8)
5431052a86aSDavid du Colombier 			sysfatal("unloadimage: %r");
5441052a86aSDavid du Colombier 		for(y=0; y<48; y++) {
5451052a86aSDavid du Colombier 			for(i=0; i<3; i++)
5461052a86aSDavid du Colombier 				fprint(fd, "0x%.2x%.2x,", data[y*6+i*2+0], data[y*6+i*2+1]);
5471052a86aSDavid du Colombier 			fprint(fd, "\n");
5481052a86aSDavid du Colombier 		}
5491052a86aSDavid du Colombier 		rv = 0;
5501052a86aSDavid du Colombier 		break;
5511052a86aSDavid du Colombier 
5521052a86aSDavid du Colombier 	case GREY2:
5531052a86aSDavid du Colombier 		if(unloadimage(image, image->r, data, 48*48/4) != 48*48/4)
5541052a86aSDavid du Colombier 			sysfatal("unloadimage: %r");
5551052a86aSDavid du Colombier 		for(y=0; y<48; y++) {
5561052a86aSDavid du Colombier 			for(i=0; i<3; i++)
5571052a86aSDavid du Colombier 				fprint(fd, "0x%.2x%.2x,%.2x%.2x,",
5581052a86aSDavid du Colombier 					data[y*12+i*4+0], data[y*12+i*4+1],
5591052a86aSDavid du Colombier 					data[y*12+i*4+2], data[y*12+i*4+3]);
5601052a86aSDavid du Colombier 			fprint(fd, "\n");
5611052a86aSDavid du Colombier 		}
5621052a86aSDavid du Colombier 		rv = 0;
5631052a86aSDavid du Colombier 		break;
5641052a86aSDavid du Colombier 
5651052a86aSDavid du Colombier 	case GREY4:
5661052a86aSDavid du Colombier 	case GREY8:
5671052a86aSDavid du Colombier 		rv = writeimage(fd, image, 0);	/* dolock? */
5681052a86aSDavid du Colombier 		break;
5691052a86aSDavid du Colombier 	}
5701052a86aSDavid du Colombier 
5711052a86aSDavid du Colombier 	if(outfile)
5721052a86aSDavid du Colombier 		close(fd);
5731052a86aSDavid du Colombier 	return rv;
5741052a86aSDavid du Colombier }
5751052a86aSDavid du Colombier 
5761052a86aSDavid du Colombier void
room(Rectangle out,Rectangle in,int * a)5771052a86aSDavid du Colombier room(Rectangle out, Rectangle in, int *a)
5781052a86aSDavid du Colombier {
5791052a86aSDavid du Colombier 	a[Left] = out.min.x - in.min.x;
5801052a86aSDavid du Colombier 	a[Right] = out.max.x - in.max.x;
5811052a86aSDavid du Colombier 	a[Top] = out.min.y - in.min.y;
5821052a86aSDavid du Colombier 	a[Bottom] = out.max.y - in.max.y;
5831052a86aSDavid du Colombier }
5841052a86aSDavid du Colombier 
5851052a86aSDavid du Colombier int
min(int a,int b)5861052a86aSDavid du Colombier min(int a, int b)
5871052a86aSDavid du Colombier {
5881052a86aSDavid du Colombier 	if(a < b)
5891052a86aSDavid du Colombier 		return a;
5901052a86aSDavid du Colombier 	return b;
5911052a86aSDavid du Colombier }
5921052a86aSDavid du Colombier 
5931052a86aSDavid du Colombier int
max(int a,int b)5941052a86aSDavid du Colombier max(int a, int b)
5951052a86aSDavid du Colombier {
5961052a86aSDavid du Colombier 	if(a > b)
5971052a86aSDavid du Colombier 		return a;
5981052a86aSDavid du Colombier 	return b;
5991052a86aSDavid du Colombier }
6001052a86aSDavid du Colombier 
6011052a86aSDavid du Colombier int
move(Rectangle r,Rectangle picr,Point d,int k,Rectangle * rp)6021052a86aSDavid du Colombier move(Rectangle r, Rectangle picr, Point d, int k, Rectangle *rp)
6031052a86aSDavid du Colombier {
6041052a86aSDavid du Colombier 	int a[4], i;
6051052a86aSDavid du Colombier 	Rectangle oldr;
6061052a86aSDavid du Colombier 	static int toggle;
6071052a86aSDavid du Colombier 
6081052a86aSDavid du Colombier 	oldr = r;
6091052a86aSDavid du Colombier 	room(picr, r, a);
6101052a86aSDavid du Colombier 	switch(k){
6111052a86aSDavid du Colombier 	case RTopLeft:
6121052a86aSDavid du Colombier 		i = (d.x+d.y)/2;
6131052a86aSDavid du Colombier 		if(i>=Dx(r) || i>=Dy(r))
6141052a86aSDavid du Colombier 			break;
6151052a86aSDavid du Colombier 		i = max(i, a[Left]);
6161052a86aSDavid du Colombier 		i = max(i, a[Top]);
6171052a86aSDavid du Colombier 		r.min.x += i;
6181052a86aSDavid du Colombier 		r.min.y += i;
6191052a86aSDavid du Colombier 		break;
6201052a86aSDavid du Colombier 	case RTop:
6211052a86aSDavid du Colombier 		i = d.y;
6221052a86aSDavid du Colombier 		if(i < 0){
6231052a86aSDavid du Colombier 			/*
6241052a86aSDavid du Colombier 			 * should really check i/2, but this is safe and feedback
6251052a86aSDavid du Colombier 			 * makes the control feel right
6261052a86aSDavid du Colombier 			 */
6271052a86aSDavid du Colombier 			i = -min(-i, a[Right]);
6281052a86aSDavid du Colombier 			i = max(i, a[Left]);
6291052a86aSDavid du Colombier 		}
6301052a86aSDavid du Colombier 		i = max(i, a[Top]);
6311052a86aSDavid du Colombier 		if(i >= Dy(r))
6321052a86aSDavid du Colombier 			break;
6331052a86aSDavid du Colombier 		r.min.y += i;
6341052a86aSDavid du Colombier 		/* divide the half bit equally */
6351052a86aSDavid du Colombier 		toggle = 1-toggle;
6361052a86aSDavid du Colombier 		if(toggle){
6371052a86aSDavid du Colombier 			r.min.x += i/2;
6381052a86aSDavid du Colombier 			r.max.x = r.min.x+Dy(r);
6391052a86aSDavid du Colombier 		}else{
6401052a86aSDavid du Colombier 			r.max.x -= i/2;
6411052a86aSDavid du Colombier 			r.min.x = r.max.x-Dy(r);
6421052a86aSDavid du Colombier 		}
6431052a86aSDavid du Colombier 		break;
6441052a86aSDavid du Colombier 	case RTopRight:
6451052a86aSDavid du Colombier 		i = (-d.x+d.y)/2;
6461052a86aSDavid du Colombier 		if(i>=Dx(r) || i>=Dy(r))
6471052a86aSDavid du Colombier 			break;
6481052a86aSDavid du Colombier 		i = -min(-i, a[Right]);
6491052a86aSDavid du Colombier 		i = max(i, a[Top]);
6501052a86aSDavid du Colombier 		r.max.x -= i;
6511052a86aSDavid du Colombier 		r.min.y += i;
6521052a86aSDavid du Colombier 		break;
6531052a86aSDavid du Colombier 	case RLeft:
6541052a86aSDavid du Colombier 		i = d.x;
6551052a86aSDavid du Colombier 		if(i < 0){
6561052a86aSDavid du Colombier 			i = -min(-i, a[Bottom]);
6571052a86aSDavid du Colombier 			i = max(i, a[Top]);
6581052a86aSDavid du Colombier 		}
6591052a86aSDavid du Colombier 		i = max(i, a[Left]);
6601052a86aSDavid du Colombier 		if(i >= Dx(r))
6611052a86aSDavid du Colombier 			break;
6621052a86aSDavid du Colombier 		r.min.x += i;
6631052a86aSDavid du Colombier 		/* divide the half bit equally */
6641052a86aSDavid du Colombier 		toggle = 1-toggle;
6651052a86aSDavid du Colombier 		if(toggle){
6661052a86aSDavid du Colombier 			r.min.y += i/2;
6671052a86aSDavid du Colombier 			r.max.y = r.min.y+Dx(r);
6681052a86aSDavid du Colombier 		}else{
6691052a86aSDavid du Colombier 			r.max.y -= i/2;
6701052a86aSDavid du Colombier 			r.min.y = r.max.y-Dx(r);
6711052a86aSDavid du Colombier 		}
6721052a86aSDavid du Colombier 		break;
6731052a86aSDavid du Colombier 	case RMiddle:
6741052a86aSDavid du Colombier 		if(d.x >= 0)
6751052a86aSDavid du Colombier 			d.x = min(d.x, a[Right]);
6761052a86aSDavid du Colombier 		else
6771052a86aSDavid du Colombier 			d.x = max(d.x, a[Left]);
6781052a86aSDavid du Colombier 		if(d.y >= 0)
6791052a86aSDavid du Colombier 			d.y = min(d.y, a[Bottom]);
6801052a86aSDavid du Colombier 		else
6811052a86aSDavid du Colombier 			d.y = max(d.y, a[Top]);
6821052a86aSDavid du Colombier 		r = rectaddpt(r, d);
6831052a86aSDavid du Colombier 		break;
6841052a86aSDavid du Colombier 	case RRight:
6851052a86aSDavid du Colombier 		i = d.x;
6861052a86aSDavid du Colombier 		if(i > 0){
6871052a86aSDavid du Colombier 			i = min(i, a[Bottom]);
6881052a86aSDavid du Colombier 			i = -max(-i, a[Top]);
6891052a86aSDavid du Colombier 		}
6901052a86aSDavid du Colombier 		i = min(i, a[Right]);
6911052a86aSDavid du Colombier 		if(-i >= Dx(r))
6921052a86aSDavid du Colombier 			break;
6931052a86aSDavid du Colombier 		r.max.x += i;
6941052a86aSDavid du Colombier 		/* divide the half bit equally */
6951052a86aSDavid du Colombier 		toggle = 1-toggle;
6961052a86aSDavid du Colombier 		if(toggle){
6971052a86aSDavid du Colombier 			r.min.y -= i/2;
6981052a86aSDavid du Colombier 			r.max.y = r.min.y+Dx(r);
6991052a86aSDavid du Colombier 		}else{
7001052a86aSDavid du Colombier 			r.max.y += i/2;
7011052a86aSDavid du Colombier 			r.min.y = r.max.y-Dx(r);
7021052a86aSDavid du Colombier 		}
7031052a86aSDavid du Colombier 		break;
7041052a86aSDavid du Colombier 	case RBotLeft:
7051052a86aSDavid du Colombier 		i = (d.x+-d.y)/2;
7061052a86aSDavid du Colombier 		if(i>=Dx(r) || i>=Dy(r))
7071052a86aSDavid du Colombier 			break;
7081052a86aSDavid du Colombier 		i = max(i, a[Left]);
7091052a86aSDavid du Colombier 		i = -min(-i, a[Bottom]);
7101052a86aSDavid du Colombier 		r.min.x += i;
7111052a86aSDavid du Colombier 		r.max.y -= i;
7121052a86aSDavid du Colombier 		break;
7131052a86aSDavid du Colombier 	case RBot:
7141052a86aSDavid du Colombier 		i = d.y;
7151052a86aSDavid du Colombier 		if(i > 0){
7161052a86aSDavid du Colombier 			i = min(i, a[Right]);
7171052a86aSDavid du Colombier 			i = -max(-i, a[Left]);
7181052a86aSDavid du Colombier 		}
7191052a86aSDavid du Colombier 		i = min(i, a[Bottom]);
7201052a86aSDavid du Colombier 		if(i >= Dy(r))
7211052a86aSDavid du Colombier 			break;
7221052a86aSDavid du Colombier 		r.max.y += i;
7231052a86aSDavid du Colombier 		/* divide the half bit equally */
7241052a86aSDavid du Colombier 		toggle = 1-toggle;
7251052a86aSDavid du Colombier 		if(toggle){
7261052a86aSDavid du Colombier 			r.min.x -= i/2;
7271052a86aSDavid du Colombier 			r.max.x = r.min.x+Dy(r);
7281052a86aSDavid du Colombier 		}else{
7291052a86aSDavid du Colombier 			r.max.x += i/2;
7301052a86aSDavid du Colombier 			r.min.x = r.max.x-Dy(r);
7311052a86aSDavid du Colombier 		}
7321052a86aSDavid du Colombier 		break;
7331052a86aSDavid du Colombier 	case RBotRight:
7341052a86aSDavid du Colombier 		i = (-d.x+-d.y)/2;
7351052a86aSDavid du Colombier 		if(i>=Dx(r) || i>=Dy(r))
7361052a86aSDavid du Colombier 			break;
7371052a86aSDavid du Colombier 		i = -min(-i, a[Right]);
7381052a86aSDavid du Colombier 		i = -min(-i, a[Bottom]);
7391052a86aSDavid du Colombier 		r.max.x -= i;
7401052a86aSDavid du Colombier 		r.max.y -= i;
7411052a86aSDavid du Colombier 		break;
7421052a86aSDavid du Colombier 	}
7431052a86aSDavid du Colombier 	if(Dx(r)<3 || Dy(r)<3){
7441052a86aSDavid du Colombier 		*rp = oldr;
7451052a86aSDavid du Colombier 		return 0;
7461052a86aSDavid du Colombier 	}
7471052a86aSDavid du Colombier 	*rp = r;
7481052a86aSDavid du Colombier 	return !eqrect(r, oldr);
7491052a86aSDavid du Colombier }
7501052a86aSDavid du Colombier 
7511052a86aSDavid du Colombier void
rlist(Rectangle r,Rectangle * ra)7521052a86aSDavid du Colombier rlist(Rectangle r, Rectangle *ra)
7531052a86aSDavid du Colombier {
7541052a86aSDavid du Colombier 	Rectangle tr;
7551052a86aSDavid du Colombier 
7561052a86aSDavid du Colombier 	tr = r;
7571052a86aSDavid du Colombier 	tr.max.y = r.min.y+Dy(r)/4;
7581052a86aSDavid du Colombier 	ra[0] = tr;
7591052a86aSDavid du Colombier 	ra[0].max.x = tr.min.x+Dx(tr)/4;
7601052a86aSDavid du Colombier 	ra[1] = tr;
7611052a86aSDavid du Colombier 	ra[1].min.x = ra[0].max.x;
7621052a86aSDavid du Colombier 	ra[1].max.x = tr.max.x-Dx(tr)/4;
7631052a86aSDavid du Colombier 	ra[2] = tr;
7641052a86aSDavid du Colombier 	ra[2].min.x = ra[1].max.x;
7651052a86aSDavid du Colombier 
7661052a86aSDavid du Colombier 	tr.min.y = tr.max.y;
7671052a86aSDavid du Colombier 	tr.max.y = r.max.y-Dy(r)/4;
7681052a86aSDavid du Colombier 	ra[3] = tr;
7691052a86aSDavid du Colombier 	ra[3].max.x = tr.min.x+Dx(tr)/4;
7701052a86aSDavid du Colombier 	ra[4] = tr;
7711052a86aSDavid du Colombier 	ra[4].min.x = ra[3].max.x;
7721052a86aSDavid du Colombier 	ra[4].max.x = tr.max.x-Dx(tr)/4;
7731052a86aSDavid du Colombier 	ra[5] = tr;
7741052a86aSDavid du Colombier 	ra[5].min.x = ra[4].max.x;
7751052a86aSDavid du Colombier 
7761052a86aSDavid du Colombier 	tr.min.y = tr.max.y;
7771052a86aSDavid du Colombier 	tr.max.y = r.max.y;
7781052a86aSDavid du Colombier 	ra[6] = tr;
7791052a86aSDavid du Colombier 	ra[6].max.x = tr.min.x+Dx(tr)/4;
7801052a86aSDavid du Colombier 	ra[7] = tr;
7811052a86aSDavid du Colombier 	ra[7].min.x = ra[6].max.x;
7821052a86aSDavid du Colombier 	ra[7].max.x = tr.max.x-Dx(tr)/4;
7831052a86aSDavid du Colombier 	ra[8] = tr;
7841052a86aSDavid du Colombier 	ra[8].min.x = ra[7].max.x;
7851052a86aSDavid du Colombier }
7861052a86aSDavid du Colombier 
7871052a86aSDavid du Colombier int
abs(int a)7881052a86aSDavid du Colombier abs(int a)
7891052a86aSDavid du Colombier {
7901052a86aSDavid du Colombier 	if(a < 0)
7911052a86aSDavid du Colombier 		return -a;
7921052a86aSDavid du Colombier 	return a;
7931052a86aSDavid du Colombier }
7941052a86aSDavid du Colombier 
7951052a86aSDavid du Colombier void
usage(void)7961052a86aSDavid du Colombier usage(void)
7971052a86aSDavid du Colombier {
7981052a86aSDavid du Colombier 	fprint(2, "usage: mug [file.bit]\n");
7991052a86aSDavid du Colombier 	exits("usage");
8001052a86aSDavid du Colombier }
8011052a86aSDavid du Colombier 
8021052a86aSDavid du Colombier void
eresized(int new)8031052a86aSDavid du Colombier eresized(int new)
8041052a86aSDavid du Colombier {
8051052a86aSDavid du Colombier 	if(new && getwindow(display, Refmesg) < 0)
8061052a86aSDavid du Colombier 		fprint(2,"can't reattach to window");
8071052a86aSDavid du Colombier 	drawscreen(1);
8081052a86aSDavid du Colombier 
8091052a86aSDavid du Colombier }
8101052a86aSDavid du Colombier 
8111052a86aSDavid du Colombier /*
8121052a86aSDavid du Colombier interface notes
8131052a86aSDavid du Colombier 
8141052a86aSDavid du Colombier cursor changes while in rbig to indicate region.
8151052a86aSDavid du Colombier only button 1 works for resizing region
8161052a86aSDavid du Colombier only button 1 works for moving thingy in ramp
8171052a86aSDavid du Colombier 
8181052a86aSDavid du Colombier button-3 menu: Reset, Depth, Undo, Save, Write
8191052a86aSDavid du Colombier */
8201052a86aSDavid du Colombier 
8211052a86aSDavid du Colombier Cursor tl = {
8221052a86aSDavid du Colombier 	{-4, -4},
8231052a86aSDavid du Colombier 	{0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff,
8241052a86aSDavid du Colombier 	 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff,
8251052a86aSDavid du Colombier 	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
8261052a86aSDavid du Colombier 	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, },
8271052a86aSDavid du Colombier 	{0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00,
8281052a86aSDavid du Colombier 	 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00,
8291052a86aSDavid du Colombier 	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
8301052a86aSDavid du Colombier 	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, }
8311052a86aSDavid du Colombier };
8321052a86aSDavid du Colombier 
8331052a86aSDavid du Colombier Cursor t = {
8341052a86aSDavid du Colombier 	{-7, -8},
8351052a86aSDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0,
8361052a86aSDavid du Colombier 	 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f,
8371052a86aSDavid du Colombier 	 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff,
8381052a86aSDavid du Colombier 	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
8391052a86aSDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
8401052a86aSDavid du Colombier 	 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80,
8411052a86aSDavid du Colombier 	 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00,
8421052a86aSDavid du Colombier 	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
8431052a86aSDavid du Colombier };
8441052a86aSDavid du Colombier 
8451052a86aSDavid du Colombier Cursor tr = {
8461052a86aSDavid du Colombier 	{-11, -4},
8471052a86aSDavid du Colombier 	{0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1,
8481052a86aSDavid du Colombier 	 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88,
8491052a86aSDavid du Colombier 	 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88,
8501052a86aSDavid du Colombier 	 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, },
8511052a86aSDavid du Colombier 	{0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e,
8521052a86aSDavid du Colombier 	 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70,
8531052a86aSDavid du Colombier 	 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
8541052a86aSDavid du Colombier 	 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, }
8551052a86aSDavid du Colombier };
8561052a86aSDavid du Colombier 
8571052a86aSDavid du Colombier Cursor r = {
8581052a86aSDavid du Colombier 	{-8, -7},
8591052a86aSDavid du Colombier 	{0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58,
8601052a86aSDavid du Colombier 	 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02,
8611052a86aSDavid du Colombier 	 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58,
8621052a86aSDavid du Colombier 	 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
8631052a86aSDavid du Colombier 	{0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
8641052a86aSDavid du Colombier 	 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc,
8651052a86aSDavid du Colombier 	 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80,
8661052a86aSDavid du Colombier 	 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
8671052a86aSDavid du Colombier };
8681052a86aSDavid du Colombier 
8691052a86aSDavid du Colombier Cursor br = {
8701052a86aSDavid du Colombier 	{-11, -11},
8711052a86aSDavid du Colombier 	{0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
8721052a86aSDavid du Colombier 	 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
8731052a86aSDavid du Colombier 	 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05,
8741052a86aSDavid du Colombier 	 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, },
8751052a86aSDavid du Colombier 	{0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
8761052a86aSDavid du Colombier 	 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
8771052a86aSDavid du Colombier 	 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa,
8781052a86aSDavid du Colombier 	 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, }
8791052a86aSDavid du Colombier };
8801052a86aSDavid du Colombier 
8811052a86aSDavid du Colombier Cursor b = {
8821052a86aSDavid du Colombier 	{-7, -7},
8831052a86aSDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8841052a86aSDavid du Colombier 	 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
8851052a86aSDavid du Colombier 	 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70,
8861052a86aSDavid du Colombier 	 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, },
8871052a86aSDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8881052a86aSDavid du Colombier 	 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe,
8891052a86aSDavid du Colombier 	 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80,
8901052a86aSDavid du Colombier 	 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
8911052a86aSDavid du Colombier };
8921052a86aSDavid du Colombier 
8931052a86aSDavid du Colombier Cursor bl = {
8941052a86aSDavid du Colombier 	{-4, -11},
8951052a86aSDavid du Colombier 	{0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
8961052a86aSDavid du Colombier 	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
8971052a86aSDavid du Colombier 	 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01,
8981052a86aSDavid du Colombier 	 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, },
8991052a86aSDavid du Colombier 	{0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
9001052a86aSDavid du Colombier 	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
9011052a86aSDavid du Colombier 	 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe,
9021052a86aSDavid du Colombier 	 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, }
9031052a86aSDavid du Colombier };
9041052a86aSDavid du Colombier 
9051052a86aSDavid du Colombier Cursor l = {
9061052a86aSDavid du Colombier 	{-7, -7},
9071052a86aSDavid du Colombier 	{0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20,
9081052a86aSDavid du Colombier 	 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20,
9091052a86aSDavid du Colombier 	 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20,
9101052a86aSDavid du Colombier 	 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
9111052a86aSDavid du Colombier 	{0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
9121052a86aSDavid du Colombier 	 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0,
9131052a86aSDavid du Colombier 	 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0,
9141052a86aSDavid du Colombier 	 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
9151052a86aSDavid du Colombier };
9161052a86aSDavid du Colombier 
9171052a86aSDavid du Colombier Cursor boxcursor = {
9181052a86aSDavid du Colombier 	{-7, -7},
9191052a86aSDavid du Colombier 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
9201052a86aSDavid du Colombier 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
9211052a86aSDavid du Colombier 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
9221052a86aSDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, },
9231052a86aSDavid du Colombier 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
9241052a86aSDavid du Colombier 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
9251052a86aSDavid du Colombier 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
9261052a86aSDavid du Colombier 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, }
9271052a86aSDavid du Colombier };
9281052a86aSDavid du Colombier 
9291052a86aSDavid du Colombier Cursor clearcursor;
9301052a86aSDavid du Colombier 
9311052a86aSDavid du Colombier Cursor *corners[10] = {
9321052a86aSDavid du Colombier 	&tl,	&t,	&tr,
9331052a86aSDavid du Colombier 	&l,	&boxcursor,	&r,
9341052a86aSDavid du Colombier 	&bl,	&b,	&br,
9351052a86aSDavid du Colombier 	nil,	/* default arrow */
9361052a86aSDavid du Colombier };
9371052a86aSDavid du Colombier 
9381052a86aSDavid du Colombier char *item[] = {
9391052a86aSDavid du Colombier 	"Reset",
9401052a86aSDavid du Colombier 	"Depth",
9411052a86aSDavid du Colombier 	"Undo",
9421052a86aSDavid du Colombier 	"Write",
9431052a86aSDavid du Colombier 	"Exit",
9441052a86aSDavid du Colombier 	nil
9451052a86aSDavid du Colombier };
9461052a86aSDavid du Colombier 
9471052a86aSDavid du Colombier Menu menu = {
9481052a86aSDavid du Colombier 	item,
9491052a86aSDavid du Colombier 	nil,
9501052a86aSDavid du Colombier 	2
9511052a86aSDavid du Colombier };
9521052a86aSDavid du Colombier 
9531052a86aSDavid du Colombier /*BUG make less flashy */
9541052a86aSDavid du Colombier void
moveface(Image * back,Point lastp,Image * face,Point p,Point d)9551052a86aSDavid du Colombier moveface(Image *back, Point lastp, Image *face, Point p, Point d)
9561052a86aSDavid du Colombier {
9571052a86aSDavid du Colombier 	draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min);
9581052a86aSDavid du Colombier 	draw(back, back->r, screen, nil, addpt(back->r.min, subpt(p, d)));
9591052a86aSDavid du Colombier 	border(screen, rectaddpt(face->r, subpt(p, d)),
9601052a86aSDavid du Colombier 		 -1, display->black, ZP);
9611052a86aSDavid du Colombier 	draw(screen, rectaddpt(face->r, subpt(p, d)),
9621052a86aSDavid du Colombier 		face, nil, face->r.min);
9631052a86aSDavid du Colombier }
9641052a86aSDavid du Colombier 
9651052a86aSDavid du Colombier int
dragface(Mouse * m,Image * im,Point d,int x)9661052a86aSDavid du Colombier dragface(Mouse *m, Image *im, Point d, int x)
9671052a86aSDavid du Colombier {
9681052a86aSDavid du Colombier 	int i;
9691052a86aSDavid du Colombier 	Point lastp;
9701052a86aSDavid du Colombier 	static Image *back;
9711052a86aSDavid du Colombier 
9721052a86aSDavid du Colombier 	if(back == nil){
9731052a86aSDavid du Colombier 		back = allocimage(display, Rect(-1,-1,49,49), display->image->chan, 0, DNofill);
9741052a86aSDavid du Colombier 		if(back == nil)
9751052a86aSDavid du Colombier 			sysfatal("dragface backing store: %r");
9761052a86aSDavid du Colombier 	}
9771052a86aSDavid du Colombier 
9781052a86aSDavid du Colombier 	lastp = m->xy;
9791052a86aSDavid du Colombier 	draw(back, back->r, screen, nil, addpt(back->r.min, subpt(lastp, d)));
9801052a86aSDavid du Colombier 	esetcursor(&clearcursor);
9811052a86aSDavid du Colombier 	do{
9821052a86aSDavid du Colombier 		moveface(back, lastp, im, m->xy, d);
9831052a86aSDavid du Colombier 		lastp = m->xy;
9841052a86aSDavid du Colombier 	}while(*m=emouse(), m->buttons==1);
9851052a86aSDavid du Colombier 
9861052a86aSDavid du Colombier 	draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min);
9871052a86aSDavid du Colombier 	esetcursor(nil);
9881052a86aSDavid du Colombier 	if(m->buttons==0){
9891052a86aSDavid du Colombier 		for(i=0; i<nelem(face); i++)
9901052a86aSDavid du Colombier 			if(ptinrect(m->xy, rface[i]))
9911052a86aSDavid du Colombier 				return i;
9921052a86aSDavid du Colombier 		if(ptinrect(m->xy, rsmall))
9931052a86aSDavid du Colombier 			return -1;
9941052a86aSDavid du Colombier 		return x;
9951052a86aSDavid du Colombier 	}
9961052a86aSDavid du Colombier 	while(*m=emouse(), m->buttons)
9971052a86aSDavid du Colombier 		;
9981052a86aSDavid du Colombier 	return x;
9991052a86aSDavid du Colombier }
10001052a86aSDavid du Colombier 
10011052a86aSDavid du Colombier void
initstate(void)10021052a86aSDavid du Colombier initstate(void)
10031052a86aSDavid du Colombier {
10041052a86aSDavid du Colombier 	state.black = 0.0;
10051052a86aSDavid du Colombier 	state.white = 1.0;
10061052a86aSDavid du Colombier 	state.stretch = 1.0;
10071052a86aSDavid du Colombier 	state.depth = 4;
10081052a86aSDavid du Colombier 	state.gamma = 1.0;
10091052a86aSDavid du Colombier 	setgtab(&state);
10101052a86aSDavid du Colombier 	state.selr = insetrect(orig->r, 5);
10111052a86aSDavid du Colombier 	sdx = Dx(state.selr);
10121052a86aSDavid du Colombier 	sdy = Dy(state.selr);
10131052a86aSDavid du Colombier 	if(sdx > sdy)
10141052a86aSDavid du Colombier 		state.selr.max.x = state.selr.min.x+sdy;
10151052a86aSDavid du Colombier 	else
10161052a86aSDavid du Colombier 		state.selr.max.y = state.selr.min.y+sdx;
10171052a86aSDavid du Colombier }
10181052a86aSDavid du Colombier 
10191052a86aSDavid du Colombier void
main(int argc,char ** argv)10201052a86aSDavid du Colombier main(int argc, char **argv)
10211052a86aSDavid du Colombier {
10221052a86aSDavid du Colombier 	int ccursor, i, fd, k, n, y;
10231052a86aSDavid du Colombier 	uchar *data;
10241052a86aSDavid du Colombier 	double gammatab[256];
10251052a86aSDavid du Colombier 	Event e;
10261052a86aSDavid du Colombier 	Mouse m;
10271052a86aSDavid du Colombier 	Point lastp, p;
10281052a86aSDavid du Colombier 	Rectangle nselr, rbig9[9];
10291052a86aSDavid du Colombier 
10301052a86aSDavid du Colombier 	ARGBEGIN{
10311052a86aSDavid du Colombier 	default:
10321052a86aSDavid du Colombier 		usage();
10331052a86aSDavid du Colombier 	}ARGEND
10341052a86aSDavid du Colombier 
10351052a86aSDavid du Colombier 	if(argc > 1)
10361052a86aSDavid du Colombier 		usage();
10371052a86aSDavid du Colombier 	if(argc == 1){
10381052a86aSDavid du Colombier 		if((fd = open(argv[0], OREAD)) < 0)
10391052a86aSDavid du Colombier 			sysfatal("open %s: %r", argv[0]);
10401052a86aSDavid du Colombier 	}else
10411052a86aSDavid du Colombier 		fd = 0;
10421052a86aSDavid du Colombier 
1043*72061b92SDavid du Colombier 	if (initdraw(0, 0, "mug") < 0)
1044*72061b92SDavid du Colombier 		sysfatal("initdraw failed");
10451052a86aSDavid du Colombier 
10461052a86aSDavid du Colombier 	if((orig = readimage(display, fd, 0)) == nil)
10471052a86aSDavid du Colombier 		sysfatal("readimage: %r");
10481052a86aSDavid du Colombier 
10491052a86aSDavid du Colombier 	orig = grey8image(orig);
10501052a86aSDavid du Colombier 
10511052a86aSDavid du Colombier 	initramp();
10521052a86aSDavid du Colombier 	initclamp();
10531052a86aSDavid du Colombier 	initval2cmap();
10541052a86aSDavid du Colombier 	bkgd = allocimagemix(display, DPaleyellow, DWhite);
10551052a86aSDavid du Colombier 	small = allocimage(display, Rect(0,0,48,48), GREY4, 0, DWhite);
10561052a86aSDavid du Colombier 	tmp8 = allocimage(display, Rect(0,0,48,48), GREY8, 0, DWhite);
10571052a86aSDavid du Colombier 	red = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DRed);
10581052a86aSDavid du Colombier 	green = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DGreen);
10591052a86aSDavid du Colombier 	blue = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DBlue);
10601052a86aSDavid du Colombier 	if(bkgd==nil || small==nil || tmp8==nil || red==nil || green==nil || blue==nil)
10611052a86aSDavid du Colombier 		sysfatal("allocimage: %r");
10621052a86aSDavid du Colombier 
10631052a86aSDavid du Colombier 	n = Dx(orig->r)*Dy(orig->r);
10641052a86aSDavid du Colombier 	data = emalloc(n*sizeof data[0]);
10651052a86aSDavid du Colombier 	rdata = emalloc(n*sizeof rdata[0]);
10661052a86aSDavid du Colombier 
10671052a86aSDavid du Colombier 	if(unloadimage(orig, orig->r, data, n) != n)
10681052a86aSDavid du Colombier 		sysfatal("unloadimage: %r");
10691052a86aSDavid du Colombier 
10701052a86aSDavid du Colombier 	for(i=0; i<256; i++)
10711052a86aSDavid du Colombier 		gammatab[i] = pow((255-i)/(double)255.0, GAMMA);
10721052a86aSDavid du Colombier 
10731052a86aSDavid du Colombier 	for(i=0; i<n; i++)
10741052a86aSDavid du Colombier 		rdata[i] = gammatab[255-data[i]];
10751052a86aSDavid du Colombier 
10761052a86aSDavid du Colombier 	initstate();
10771052a86aSDavid du Colombier 	process(rdata, orig->r, state.selr, small);
10781052a86aSDavid du Colombier 	drawscreen(1);
10791052a86aSDavid du Colombier 	flushimage(display, 1);
10801052a86aSDavid du Colombier 	einit(Emouse|Ekeyboard);
10811052a86aSDavid du Colombier 	ccursor = 9;
10821052a86aSDavid du Colombier 	for(;;){
10831052a86aSDavid du Colombier 		if((n=eread(Emouse|Ekeyboard, &e))==Ekeyboard)
10841052a86aSDavid du Colombier 			continue;
10851052a86aSDavid du Colombier 		if(n != Emouse)
10861052a86aSDavid du Colombier 			break;
10871052a86aSDavid du Colombier 
10881052a86aSDavid du Colombier 		m = e.mouse;
10891052a86aSDavid du Colombier 		if(m.buttons&4){
10901052a86aSDavid du Colombier 			ccursor = 9;
10911052a86aSDavid du Colombier 			esetcursor(corners[ccursor]);
10921052a86aSDavid du Colombier 			switch(emenuhit(3, &m, &menu)){
10931052a86aSDavid du Colombier 			case -1:
10941052a86aSDavid du Colombier 				continue;
10951052a86aSDavid du Colombier 			case 0:	/* Reset */
10961052a86aSDavid du Colombier 				mark();
10971052a86aSDavid du Colombier 				initstate();
10981052a86aSDavid du Colombier 				small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite);
10991052a86aSDavid du Colombier 				if(small == nil)
11001052a86aSDavid du Colombier 					sysfatal("allocimage: %r");
11011052a86aSDavid du Colombier 				process(rdata, orig->r, state.selr, small);
11021052a86aSDavid du Colombier 				drawface(-1);
11031052a86aSDavid du Colombier 				drawscreen(0);
11041052a86aSDavid du Colombier 				break;
11051052a86aSDavid du Colombier 			case 1:	/* Depth */
11061052a86aSDavid du Colombier 				mark();
11071052a86aSDavid du Colombier 				/* osmall = small, so no freeimage */
11081052a86aSDavid du Colombier 				state.depth /= 2;
11091052a86aSDavid du Colombier 				if(state.depth == 0)
11101052a86aSDavid du Colombier 					state.depth = 8;
11111052a86aSDavid du Colombier 				small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite);
11121052a86aSDavid du Colombier 				if(small == nil)
11131052a86aSDavid du Colombier 					sysfatal("allocimage: %r");
11141052a86aSDavid du Colombier 				process(rdata, orig->r, state.selr, small);
11151052a86aSDavid du Colombier 				drawface(-1);
11161052a86aSDavid du Colombier 				break;
11171052a86aSDavid du Colombier 			case 2:	/* Undo */
11181052a86aSDavid du Colombier 				undo();
11191052a86aSDavid du Colombier 				break;
11201052a86aSDavid du Colombier 			case 3:	/* Write */
11211052a86aSDavid du Colombier 				writeface(nil, small);
11221052a86aSDavid du Colombier 				break;
11231052a86aSDavid du Colombier 			case 4:	/* Exit */
11241052a86aSDavid du Colombier 				exits(nil);
11251052a86aSDavid du Colombier 				break;
11261052a86aSDavid du Colombier 			}
11271052a86aSDavid du Colombier 		}
11281052a86aSDavid du Colombier 
11291052a86aSDavid du Colombier 		if(ptinrect(m.xy, rbig)){
11301052a86aSDavid du Colombier 			rlist(rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), rbig9);
11311052a86aSDavid du Colombier 			for(i=0; i<9; i++)
11321052a86aSDavid du Colombier 				if(ptinrect(m.xy, rbig9[i]))
11331052a86aSDavid du Colombier 					break;
11341052a86aSDavid du Colombier 			if(i != ccursor){
11351052a86aSDavid du Colombier 				ccursor = i;
11361052a86aSDavid du Colombier 				esetcursor(corners[ccursor]);
11371052a86aSDavid du Colombier 			}
11381052a86aSDavid du Colombier 			if(i==9)
11391052a86aSDavid du Colombier 				continue;
11401052a86aSDavid du Colombier 
11411052a86aSDavid du Colombier 			if(m.buttons & 1){
11421052a86aSDavid du Colombier 				mark();
11431052a86aSDavid du Colombier 				lastp = m.xy;
11441052a86aSDavid du Colombier 				while(m=emouse(), m.buttons&1){
11451052a86aSDavid du Colombier 					if(move(state.selr, orig->r, subpt(m.xy, lastp), i, &nselr)){
11461052a86aSDavid du Colombier 						moveframe(state.selr, nselr);
11471052a86aSDavid du Colombier 						state.selr = nselr;
11481052a86aSDavid du Colombier 						lastp = m.xy;
11491052a86aSDavid du Colombier 						process(rdata, orig->r, state.selr, small);
11501052a86aSDavid du Colombier 						drawface(-1);
11511052a86aSDavid du Colombier 					}
11521052a86aSDavid du Colombier 				}
11531052a86aSDavid du Colombier 			}
11541052a86aSDavid du Colombier 			continue;
11551052a86aSDavid du Colombier 		}
11561052a86aSDavid du Colombier 
11571052a86aSDavid du Colombier 		if(ccursor != 9){	/* default cursor */
11581052a86aSDavid du Colombier 			ccursor = 9;
11591052a86aSDavid du Colombier 			esetcursor(corners[ccursor]);
11601052a86aSDavid du Colombier 		}
11611052a86aSDavid du Colombier 
11621052a86aSDavid du Colombier 		if(ptinrect(m.xy, rramp)){
11631052a86aSDavid du Colombier 			if(m.buttons != 1)
11641052a86aSDavid du Colombier 				continue;
11651052a86aSDavid du Colombier 			mark();
11661052a86aSDavid du Colombier 			y = gamma2y(state.gamma);
11671052a86aSDavid du Colombier 			if(abs(y-(m.xy.y-rramp.min.y)) > 5)
11681052a86aSDavid du Colombier 				continue;
11691052a86aSDavid du Colombier 			k = section(m.xy.x-rramp.min.x);
11701052a86aSDavid du Colombier 			drawrampbar(green, &state);
11711052a86aSDavid du Colombier 			lastp = m.xy;
11721052a86aSDavid du Colombier 			while(m=emouse(), m.buttons&1){
11731052a86aSDavid du Colombier 				if(!ptinrect(m.xy, rramp))
11741052a86aSDavid du Colombier 					continue;
11751052a86aSDavid du Colombier 				switch(k){
11761052a86aSDavid du Colombier 				case -1:
11771052a86aSDavid du Colombier 					continue;
11781052a86aSDavid du Colombier 				case 0:
11791052a86aSDavid du Colombier 					if((m.xy.x-rramp.min.x)/255.0 < state.white){
11801052a86aSDavid du Colombier 						state.black = (m.xy.x-rramp.min.x)/255.0;
11811052a86aSDavid du Colombier 						break;
11821052a86aSDavid du Colombier 					}
11831052a86aSDavid du Colombier 					continue;
11841052a86aSDavid du Colombier 				case 1:
11851052a86aSDavid du Colombier 					state.gamma = y2gamma(m.xy.y-rramp.min.y);
11861052a86aSDavid du Colombier 					setgtab(&state);
11871052a86aSDavid du Colombier 					break;
11881052a86aSDavid du Colombier 				case 2:
11891052a86aSDavid du Colombier 					if((m.xy.x-rramp.min.x)/255.0 > state.black){
11901052a86aSDavid du Colombier 						state.white = (m.xy.x-rramp.min.x)/255.0;
11911052a86aSDavid du Colombier 						break;
11921052a86aSDavid du Colombier 					}
11931052a86aSDavid du Colombier 					continue;
11941052a86aSDavid du Colombier 				case 10:
11951052a86aSDavid du Colombier 					state.black += (m.xy.x-lastp.x)/255.0;
11961052a86aSDavid du Colombier 					state.white += (m.xy.x-lastp.x)/255.0;
11971052a86aSDavid du Colombier 					state.gamma = y2gamma(p.y);
11981052a86aSDavid du Colombier 					break;
11991052a86aSDavid du Colombier 				}
12001052a86aSDavid du Colombier 				process(rdata, orig->r, state.selr, small);
12011052a86aSDavid du Colombier 				drawface(-1);
12021052a86aSDavid du Colombier 				drawrampbar(green, &state);
12031052a86aSDavid du Colombier 			}
12041052a86aSDavid du Colombier 			if(m.buttons == 0){
12051052a86aSDavid du Colombier 				process(rdata, orig->r, state.selr, small);
12061052a86aSDavid du Colombier 				drawface(-1);
12071052a86aSDavid du Colombier 				drawrampbar(red, &state);
12081052a86aSDavid du Colombier 			}else
12091052a86aSDavid du Colombier 				undo();
12101052a86aSDavid du Colombier 			continue;
12111052a86aSDavid du Colombier 		}
12121052a86aSDavid du Colombier 
12131052a86aSDavid du Colombier 		if(ptinrect(m.xy, rsmall)){
12141052a86aSDavid du Colombier 			if(m.buttons != 1)
12151052a86aSDavid du Colombier 				continue;
12161052a86aSDavid du Colombier 			n=dragface(&m, small, subpt(m.xy, rsmall.min), -1);
12171052a86aSDavid du Colombier 			if(n == -1)
12181052a86aSDavid du Colombier 				continue;
12191052a86aSDavid du Colombier 			saveface(nil, n);
12201052a86aSDavid du Colombier 		}
12211052a86aSDavid du Colombier 
12221052a86aSDavid du Colombier 		for(i=0; i<nelem(face); i++)
12231052a86aSDavid du Colombier 			if(ptinrect(m.xy, rface[i]))
12241052a86aSDavid du Colombier 				break;
12251052a86aSDavid du Colombier 		if(i<nelem(face) && face[i] != nil){
12261052a86aSDavid du Colombier 			if(m.buttons != 1)
12271052a86aSDavid du Colombier 				continue;
12281052a86aSDavid du Colombier 			n=dragface(&m, face[i]->small, subpt(m.xy, rface[i].min), i);
12291052a86aSDavid du Colombier 			if(n == i)
12301052a86aSDavid du Colombier 				continue;
12311052a86aSDavid du Colombier 			saveface(face[i], n);
12321052a86aSDavid du Colombier 			continue;
12331052a86aSDavid du Colombier 		}
12341052a86aSDavid du Colombier 
12351052a86aSDavid du Colombier 		do
12361052a86aSDavid du Colombier 			m = emouse();
12371052a86aSDavid du Colombier 		while(m.buttons==1);
12381052a86aSDavid du Colombier 	}
12391052a86aSDavid du Colombier 	exits(nil);
12401052a86aSDavid du Colombier }
1241