xref: /plan9/sys/src/cmd/mug.c (revision 72061b92c86a7d2b16e3ab31edd3846f53c84345)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5 #include <cursor.h>
6 
7 #define initstate muginitstate
8 
9 typedef struct State State;
10 struct State {
11 	double black;
12 	double white;
13 	double stretch;
14 	double gamma;
15 	int depth;
16 	int gtab[1001];
17 	Rectangle selr;
18 };
19 
20 typedef struct Face Face;
21 struct Face {
22 	Rectangle r;
23 	State state;
24 	Image *small;
25 };
26 
27 double GAMMA = 1.0;		/* theory tells me this should be 2.2, but 1.0 sure looks better */
28 enum {
29 	Left=0,
30 	Right,
31 	Top,
32 	Bottom,
33 
34 	RTopLeft=0,
35 	RTop,
36 	RTopRight,
37 	RLeft,
38 	RMiddle,
39 	RRight,
40 	RBotLeft,
41 	RBot,
42 	RBotRight,
43 };
44 
45 void*
emalloc(ulong sz)46 emalloc(ulong sz)
47 {
48 	void *v;
49 
50 	v = malloc(sz);
51 	if(v == nil)
52 		sysfatal("malloc %lud fails", sz);
53 	memset(v, 0, sz);
54 	return v;
55 }
56 
57 Face *face[8];
58 int nface;
59 uchar grey2cmap[256];
60 Image *bkgd;
61 Image *orig;
62 Image *ramp, *small, *osmall, *tmp8, *red, *green, *blue;
63 State state, ostate;
64 uchar val2cmap[256];
65 uchar clamp[3*256];
66 Rectangle rbig, rramp, rface[nelem(face)], rsmall;
67 double *rdata;
68 int sdy, sdx;
69 
70 void
geometry(Rectangle r)71 geometry(Rectangle r)
72 {
73 	int i;
74 	Rectangle fr[9];
75 
76 	rramp.min = addpt(r.min, Pt(4,4));
77 	rramp.max = addpt(rramp.min, Pt(256,256));
78 
79 	rbig.min = Pt(rramp.max.x+6, rramp.min.y);
80 	rbig.max = addpt(rbig.min, Pt(Dx(orig->r), Dy(orig->r)));
81 
82 	for(i=0; i<9; i++)
83 		fr[i] = rectaddpt(Rect(0,0,48,48), Pt(rramp.min.x+48+56*(i%3), rramp.max.y+6+56*(i/3)));
84 
85 	rsmall = fr[4];
86 	for(i=0; i<4; i++)
87 		rface[i] = fr[i];
88 	for(i=4; i<8; i++)
89 		rface[i] = fr[i+1];
90 }
91 
92 double
y2gamma(int y)93 y2gamma(int y)
94 {
95 	double g;
96 
97 	g = (double)y / 128.0;
98 	return 0.5+g*g;		/* gamma from 0.5 to 4.5, with 1.0 near the middle */
99 }
100 
101 int
gamma2y(double g)102 gamma2y(double g)
103 {
104 	g -= 0.5;
105 	return (int)(128.0*sqrt(g)+0.5);
106 }
107 
108 void
drawface(int i)109 drawface(int i)
110 {
111 	if(i==-1){
112 		border(screen, rsmall, -3, blue, ZP);
113 		draw(screen, rsmall, small, nil, ZP);
114 		return;
115 	}
116 	border(screen, rface[i], -1, display->black, ZP);
117 	if(face[i])
118 		draw(screen, rface[i], face[i]->small, nil, ZP);
119 	else
120 		draw(screen, rface[i], display->white, nil, ZP);
121 }
122 
123 void
drawrampbar(Image * color,State * s)124 drawrampbar(Image *color, State *s)
125 {
126 	Rectangle liner, r;
127 	static Rectangle br;
128 
129 	if(Dx(br))
130 		draw(screen, br, ramp, nil, subpt(br.min, rramp.min));
131 
132 	r = rramp;
133 	r.max.x = r.min.x + (int)(s->white*255.0);
134 	r.min.x += (int)(s->black*255.0);
135 	r.min.y += gamma2y(s->gamma);
136 	r.max.y = r.min.y+1;
137 	rectclip(&r, rramp);
138 	draw(screen, r, color, nil, ZP);
139 	br = r;
140 
141 	r.min.y -= 2;
142 	r.max.y += 2;
143 
144 	liner = r;
145 	r.min.x += Dx(liner)/3;
146 	r.max.x -= Dx(liner)/3;
147 	rectclip(&r, rramp);
148 	draw(screen, r, color, nil, ZP);
149 	combinerect(&br, r);
150 
151 	r = liner;
152 	r.max.x = r.min.x+3;
153 	rectclip(&r, rramp);
154 	draw(screen, r, color, nil, ZP);
155 	combinerect(&br, r);
156 
157 	r = liner;
158 	r.min.x = r.max.x-3;
159 	rectclip(&r, rramp);
160 	draw(screen, r, color, nil, ZP);
161 	combinerect(&br, r);
162 }
163 
164 void
drawscreen(int clear)165 drawscreen(int clear)
166 {
167 	int i;
168 
169 	if(clear){
170 		geometry(screen->r);
171 		draw(screen, screen->r, bkgd, nil, ZP);
172 	}
173 
174 	border(screen, rbig, -1, display->black, ZP);
175 	draw(screen, rbig, orig, nil, orig->r.min);
176 
177 	border(screen, rramp, -1, display->black, ZP);
178 	draw(screen, rramp, ramp, nil, ramp->r.min);
179 	drawrampbar(red, &state);
180 
181 	border(screen, rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), -2, red, ZP);
182 	if(clear){
183 		drawface(-1);
184 		for(i=0; i<nelem(face); i++)
185 			drawface(i);
186 	}
187 }
188 
189 void
moveframe(Rectangle old,Rectangle new)190 moveframe(Rectangle old, Rectangle new)
191 {
192 	border(screen, rectaddpt(old, subpt(rbig.min, orig->r.min)), -2, orig, old.min);
193 	border(screen, rectaddpt(new, subpt(rbig.min, orig->r.min)), -2, red, ZP);
194 }
195 
196 
197 /*
198  * Initialize gamma ramp; should dither for
199  * benefit of non-true-color displays.
200  */
201 void
initramp(void)202 initramp(void)
203 {
204 	int k, x, y;
205 	uchar dat[256*256];
206 	double g;
207 
208 	k = 0;
209 	for(y=0; y<256; y++) {
210 		g = y2gamma(y);
211 		for(x=0; x<256; x++)
212 			dat[k++] = 255.0 * pow(x/255.0, g);
213 	}
214 	assert(k == sizeof dat);
215 
216 	ramp = allocimage(display, Rect(0,0,256,256), GREY8, 0, DNofill);
217 	if(ramp == nil)
218 		sysfatal("allocimage: %r");
219 
220 	if(loadimage(ramp, ramp->r, dat, sizeof dat) != sizeof dat)
221 		sysfatal("loadimage: %r");
222 }
223 
224 void
initclamp(void)225 initclamp(void)
226 {
227 	int i;
228 
229 	for(i=0; i<256; i++) {
230 		clamp[i] = 0;
231 		clamp[256+i] = i;
232 		clamp[512+i] = 255;
233 	}
234 }
235 
236 void
changestretch(double stretch)237 changestretch(double stretch)
238 {
239 	state.stretch = stretch;
240 }
241 
242 /*
243  * There is greyscale data for the rectangle datar in data;
244  * extract square r and write it into the 48x48 pixel image small.
245  */
246 void
process(double * data,Rectangle datar,Rectangle r,Image * small)247 process(double *data, Rectangle datar, Rectangle r, Image *small)
248 {
249 	double black, center, delta, *k, shrink, sum, *tmp[48], *tt, w, white, x;
250 	int datadx, dp, dx, dy, error, i, ii, j, jj;
251 	int ksize, ksizeby2, sdata[48*48], sd, sh, sm, sv, u, uu, uuu, v, vv;
252 	uchar bdata[48*48];
253 
254 	datadx = Dx(datar);
255 	dx = Dx(r);
256 	dy = Dy(r);
257 	shrink = dx/48.0;
258 
259 	ksize = 1+2*(int)(shrink/2.0);
260 	if(ksize <= 2)
261 		return;
262 
263 	k = emalloc(ksize*sizeof(k[0]));
264 
265 	/* center of box */
266 	for(i=1; i<ksize-1; i++)
267 		k[i] = 1.0;
268 
269 	/* edges */
270 	x = shrink - floor(shrink);
271 	k[0] = x;
272 	k[ksize-1] = x;
273 
274 	sum = 0.0;
275 	for(i=0; i<ksize; i++)
276 		sum += k[i];
277 
278 	for(i=0; i<ksize; i++)
279 		k[i] /= sum;
280 
281 	ksizeby2 = ksize/2;
282 
283 	for(i=0; i<48; i++)
284 		tmp[i] = emalloc(datadx*sizeof(tmp[i][0]));
285 
286 	/* squeeze vertically */
287 	for(i=0; i<48; i++) {
288 		ii = r.min.y+i*dy/48;
289 		tt = tmp[i];
290 		uu = ii - ksizeby2;
291 		for(j=r.min.x-ksize; j<r.max.x+ksize; j++) {
292 			if(j<datar.min.x || j>=datar.max.x)
293 				continue;
294 			w = 0.0;
295 
296 			uuu = uu*datadx+j;
297 			if(uu>=datar.min.y && uu+ksize<datar.max.y)
298 				for(u=0; u<ksize; u++){
299 					w += k[u]*data[uuu];
300 					uuu += datadx;
301 				}
302 			else
303 				for(u=0; u<ksize; u++){
304 					if(uu+u>=datar.min.y && uu+u<datar.max.y)
305 						w += k[u]*data[uuu];
306 					uuu+=datadx;
307 				}
308 			tt[j-datar.min.x] = w;
309 		}
310 	}
311 
312 	/* stretch value scale */
313 	center = (state.black+state.white)/2;
314 	delta = state.stretch*(state.white-state.black)/2;
315 	black = center - delta;
316 	white = center + delta;
317 
318 	/* squeeze horizontally */
319 	for(i=0; i<48; i++) {
320 		tt = tmp[i];
321 		for(j=0; j<48; j++) {
322 			jj = r.min.x+j*dx/48;
323 			w = 0.0;
324 			for(v=0; v<ksize; v++) {
325 				vv = jj - ksizeby2 + v;
326 				if(vv<datar.min.x || vv>=datar.max.x) {
327 					w += k[v];		/* assume white surround */
328 					continue;
329 				}
330 				w += k[v]*tt[vv-datar.min.x];
331 			}
332 			if(w < black || black==white)
333 				w = 0.0;
334 			else if(w > white)
335 				w = 1.0;
336 			else
337 				w = (w-black)/(white-black);
338 			sdata[i*48+j] = state.gtab[(int)(1000.0*w)];
339 		}
340 	}
341 
342 	/* dither to lower depth before copying into GREY8 version */
343 	if(small->chan != GREY8) {
344 		u = 0;
345 		dp = small->depth;
346 		for(i=0; i<48; i++) {
347 			sm = 0xFF ^ (0xFF>>dp);
348 			sh = 0;
349 			v = 0;
350 			for(j=0; j<48; j++) {
351 				ii = 48*i+j;
352 				sd = clamp[sdata[ii]+256];
353 				sv = sd&sm;
354 				v |= sv>>sh;
355 				sh += dp;
356 				if(sh == 8) {
357 					bdata[u++] = v;
358 					v = 0;
359 					sh = 0;
360 				}
361 
362 				/* propagate error, with decay (sum errors < 1) */
363 				error = sd - sv;
364 				if(ii+49 < 48*48) {	/* one test is enough, really */
365 					sdata[ii+1] = sdata[ii+1]+((3*error)>>4);
366 					sdata[ii+48] = sdata[ii+48]+((3*error)>>4);
367 					sdata[ii+49] = sdata[ii+49]+((3*error)>>3);
368 				}
369 
370 				/* produce correct color map value by copying bits */
371 				switch(dp){
372 				case 1:
373 					sv |= sv>>1;
374 				case 2:
375 					sv |= sv>>2;
376 				case 4:
377 					sv |= sv>>4;
378 				}
379 				sdata[ii] = sv;
380 			}
381 		}
382 		for(i=0; i<nelem(bdata); i++)
383 			bdata[i] = sdata[i];
384 		if(loadimage(tmp8, tmp8->r, bdata, sizeof bdata) != sizeof bdata)
385 			sysfatal("loadimage: %r");
386 		draw(small, small->r, tmp8, nil, tmp8->r.min);
387 	} else {
388 		for(i=0; i<nelem(bdata); i++)
389 			bdata[i] = sdata[i];
390 		if(loadimage(small, small->r, bdata, sizeof bdata) != sizeof bdata)
391 			sysfatal("loadimage: %r");
392 	}
393 
394 	free(k);
395 	for(i=0; i<48; i++)
396 		free(tmp[i]);
397 }
398 
399 void
initval2cmap(void)400 initval2cmap(void)
401 {
402 	int i;
403 
404 	for(i=0; i<256; i++)
405 		val2cmap[i] = rgb2cmap(i, i, i);
406 }
407 
408 void
setgtab(State * s)409 setgtab(State *s)
410 {
411 	int i;
412 
413 	for(i=0; i<=1000; i++)
414 		s->gtab[i] = val2cmap[(int)(255.0*pow((i/1000.0), 1.0/s->gamma))];
415 }
416 
417 int
section(int x)418 section(int x)
419 {
420 	int ib, iw;
421 
422 	ib = state.black * 255.0;
423 	iw = state.white * 255.0;
424 
425 	if(x<ib-5 || iw+5<x)
426 		return -1;
427 
428 	iw -= ib;
429 	x -= ib;
430 	if(x < iw/3)
431 		return 0;
432 	if(x < 2*iw/3)
433 		return 1;
434 	return 2;
435 }
436 
437 Image*
copyimage(Image * i)438 copyimage(Image *i)
439 {
440 	Image *n;
441 
442 	if(i == nil)
443 		return nil;
444 
445 	n = allocimage(display, i->r, i->chan, 0, DNofill);
446 	if(n == nil)
447 		sysfatal("allocimage: %r");
448 
449 	draw(n, n->r, i, nil, i->r.min);
450 	return n;
451 }
452 
453 Image*
grey8image(Image * i)454 grey8image(Image *i)
455 {
456 	Image *n;
457 
458 	if(i->chan == GREY8)
459 		return i;
460 
461 	n = allocimage(display, i->r, GREY8, 0, DNofill);
462 	if(n == nil)
463 		sysfatal("allocimage: %r");
464 
465 	draw(n, n->r, i, nil, i->r.min);
466 	freeimage(i);
467 	return n;
468 }
469 
470 
471 void
mark(void)472 mark(void)
473 {
474 	if(osmall != small){
475 		freeimage(osmall);
476 		osmall = small;
477 	}
478 	ostate = state;
479 }
480 
481 void
undo(void)482 undo(void)
483 {
484 	if(small != osmall){
485 		freeimage(small);
486 		small = osmall;
487 	}
488 	state = ostate;
489 	process(rdata, orig->r, state.selr, small);
490 	drawface(-1);
491 	drawscreen(0);
492 }
493 
494 void
saveface(Face * f,int slot)495 saveface(Face *f, int slot)
496 {
497 	if(slot == -1){
498 		mark();
499 		state = f->state;
500 		small = copyimage(f->small);
501 		drawface(-1);
502 		drawscreen(0);
503 		return;
504 	}
505 
506 	if(face[slot]==nil)
507 		face[slot] = emalloc(sizeof(*face[slot]));
508 	else{
509 		freeimage(face[slot]->small);
510 		face[slot]->small = nil;
511 	}
512 
513 	if(f == nil){
514 		face[slot]->small = copyimage(small);
515 		face[slot]->state = state;
516 	}else{
517 		face[slot]->small = copyimage(f->small);
518 		face[slot]->state = f->state;
519 	}
520 	drawface(slot);
521 }
522 
523 int
writeface(char * outfile,Image * image)524 writeface(char *outfile, Image *image)
525 {
526 	int i, fd, rv, y;
527 	uchar data[48*48/2];
528 
529 	if(outfile == nil)
530 		fd = 1;
531 	else{
532 		if((fd = create(outfile, OWRITE, 0666)) < 0)
533 			return -1;
534 	}
535 
536 	switch(image->chan) {
537 	default:
538 		rv = -1;
539 		break;
540 
541 	case GREY1:
542 		if(unloadimage(image, image->r, data, 48*48/8) != 48*48/8)
543 			sysfatal("unloadimage: %r");
544 		for(y=0; y<48; y++) {
545 			for(i=0; i<3; i++)
546 				fprint(fd, "0x%.2x%.2x,", data[y*6+i*2+0], data[y*6+i*2+1]);
547 			fprint(fd, "\n");
548 		}
549 		rv = 0;
550 		break;
551 
552 	case GREY2:
553 		if(unloadimage(image, image->r, data, 48*48/4) != 48*48/4)
554 			sysfatal("unloadimage: %r");
555 		for(y=0; y<48; y++) {
556 			for(i=0; i<3; i++)
557 				fprint(fd, "0x%.2x%.2x,%.2x%.2x,",
558 					data[y*12+i*4+0], data[y*12+i*4+1],
559 					data[y*12+i*4+2], data[y*12+i*4+3]);
560 			fprint(fd, "\n");
561 		}
562 		rv = 0;
563 		break;
564 
565 	case GREY4:
566 	case GREY8:
567 		rv = writeimage(fd, image, 0);	/* dolock? */
568 		break;
569 	}
570 
571 	if(outfile)
572 		close(fd);
573 	return rv;
574 }
575 
576 void
room(Rectangle out,Rectangle in,int * a)577 room(Rectangle out, Rectangle in, int *a)
578 {
579 	a[Left] = out.min.x - in.min.x;
580 	a[Right] = out.max.x - in.max.x;
581 	a[Top] = out.min.y - in.min.y;
582 	a[Bottom] = out.max.y - in.max.y;
583 }
584 
585 int
min(int a,int b)586 min(int a, int b)
587 {
588 	if(a < b)
589 		return a;
590 	return b;
591 }
592 
593 int
max(int a,int b)594 max(int a, int b)
595 {
596 	if(a > b)
597 		return a;
598 	return b;
599 }
600 
601 int
move(Rectangle r,Rectangle picr,Point d,int k,Rectangle * rp)602 move(Rectangle r, Rectangle picr, Point d, int k, Rectangle *rp)
603 {
604 	int a[4], i;
605 	Rectangle oldr;
606 	static int toggle;
607 
608 	oldr = r;
609 	room(picr, r, a);
610 	switch(k){
611 	case RTopLeft:
612 		i = (d.x+d.y)/2;
613 		if(i>=Dx(r) || i>=Dy(r))
614 			break;
615 		i = max(i, a[Left]);
616 		i = max(i, a[Top]);
617 		r.min.x += i;
618 		r.min.y += i;
619 		break;
620 	case RTop:
621 		i = d.y;
622 		if(i < 0){
623 			/*
624 			 * should really check i/2, but this is safe and feedback
625 			 * makes the control feel right
626 			 */
627 			i = -min(-i, a[Right]);
628 			i = max(i, a[Left]);
629 		}
630 		i = max(i, a[Top]);
631 		if(i >= Dy(r))
632 			break;
633 		r.min.y += i;
634 		/* divide the half bit equally */
635 		toggle = 1-toggle;
636 		if(toggle){
637 			r.min.x += i/2;
638 			r.max.x = r.min.x+Dy(r);
639 		}else{
640 			r.max.x -= i/2;
641 			r.min.x = r.max.x-Dy(r);
642 		}
643 		break;
644 	case RTopRight:
645 		i = (-d.x+d.y)/2;
646 		if(i>=Dx(r) || i>=Dy(r))
647 			break;
648 		i = -min(-i, a[Right]);
649 		i = max(i, a[Top]);
650 		r.max.x -= i;
651 		r.min.y += i;
652 		break;
653 	case RLeft:
654 		i = d.x;
655 		if(i < 0){
656 			i = -min(-i, a[Bottom]);
657 			i = max(i, a[Top]);
658 		}
659 		i = max(i, a[Left]);
660 		if(i >= Dx(r))
661 			break;
662 		r.min.x += i;
663 		/* divide the half bit equally */
664 		toggle = 1-toggle;
665 		if(toggle){
666 			r.min.y += i/2;
667 			r.max.y = r.min.y+Dx(r);
668 		}else{
669 			r.max.y -= i/2;
670 			r.min.y = r.max.y-Dx(r);
671 		}
672 		break;
673 	case RMiddle:
674 		if(d.x >= 0)
675 			d.x = min(d.x, a[Right]);
676 		else
677 			d.x = max(d.x, a[Left]);
678 		if(d.y >= 0)
679 			d.y = min(d.y, a[Bottom]);
680 		else
681 			d.y = max(d.y, a[Top]);
682 		r = rectaddpt(r, d);
683 		break;
684 	case RRight:
685 		i = d.x;
686 		if(i > 0){
687 			i = min(i, a[Bottom]);
688 			i = -max(-i, a[Top]);
689 		}
690 		i = min(i, a[Right]);
691 		if(-i >= Dx(r))
692 			break;
693 		r.max.x += i;
694 		/* divide the half bit equally */
695 		toggle = 1-toggle;
696 		if(toggle){
697 			r.min.y -= i/2;
698 			r.max.y = r.min.y+Dx(r);
699 		}else{
700 			r.max.y += i/2;
701 			r.min.y = r.max.y-Dx(r);
702 		}
703 		break;
704 	case RBotLeft:
705 		i = (d.x+-d.y)/2;
706 		if(i>=Dx(r) || i>=Dy(r))
707 			break;
708 		i = max(i, a[Left]);
709 		i = -min(-i, a[Bottom]);
710 		r.min.x += i;
711 		r.max.y -= i;
712 		break;
713 	case RBot:
714 		i = d.y;
715 		if(i > 0){
716 			i = min(i, a[Right]);
717 			i = -max(-i, a[Left]);
718 		}
719 		i = min(i, a[Bottom]);
720 		if(i >= Dy(r))
721 			break;
722 		r.max.y += i;
723 		/* divide the half bit equally */
724 		toggle = 1-toggle;
725 		if(toggle){
726 			r.min.x -= i/2;
727 			r.max.x = r.min.x+Dy(r);
728 		}else{
729 			r.max.x += i/2;
730 			r.min.x = r.max.x-Dy(r);
731 		}
732 		break;
733 	case RBotRight:
734 		i = (-d.x+-d.y)/2;
735 		if(i>=Dx(r) || i>=Dy(r))
736 			break;
737 		i = -min(-i, a[Right]);
738 		i = -min(-i, a[Bottom]);
739 		r.max.x -= i;
740 		r.max.y -= i;
741 		break;
742 	}
743 	if(Dx(r)<3 || Dy(r)<3){
744 		*rp = oldr;
745 		return 0;
746 	}
747 	*rp = r;
748 	return !eqrect(r, oldr);
749 }
750 
751 void
rlist(Rectangle r,Rectangle * ra)752 rlist(Rectangle r, Rectangle *ra)
753 {
754 	Rectangle tr;
755 
756 	tr = r;
757 	tr.max.y = r.min.y+Dy(r)/4;
758 	ra[0] = tr;
759 	ra[0].max.x = tr.min.x+Dx(tr)/4;
760 	ra[1] = tr;
761 	ra[1].min.x = ra[0].max.x;
762 	ra[1].max.x = tr.max.x-Dx(tr)/4;
763 	ra[2] = tr;
764 	ra[2].min.x = ra[1].max.x;
765 
766 	tr.min.y = tr.max.y;
767 	tr.max.y = r.max.y-Dy(r)/4;
768 	ra[3] = tr;
769 	ra[3].max.x = tr.min.x+Dx(tr)/4;
770 	ra[4] = tr;
771 	ra[4].min.x = ra[3].max.x;
772 	ra[4].max.x = tr.max.x-Dx(tr)/4;
773 	ra[5] = tr;
774 	ra[5].min.x = ra[4].max.x;
775 
776 	tr.min.y = tr.max.y;
777 	tr.max.y = r.max.y;
778 	ra[6] = tr;
779 	ra[6].max.x = tr.min.x+Dx(tr)/4;
780 	ra[7] = tr;
781 	ra[7].min.x = ra[6].max.x;
782 	ra[7].max.x = tr.max.x-Dx(tr)/4;
783 	ra[8] = tr;
784 	ra[8].min.x = ra[7].max.x;
785 }
786 
787 int
abs(int a)788 abs(int a)
789 {
790 	if(a < 0)
791 		return -a;
792 	return a;
793 }
794 
795 void
usage(void)796 usage(void)
797 {
798 	fprint(2, "usage: mug [file.bit]\n");
799 	exits("usage");
800 }
801 
802 void
eresized(int new)803 eresized(int new)
804 {
805 	if(new && getwindow(display, Refmesg) < 0)
806 		fprint(2,"can't reattach to window");
807 	drawscreen(1);
808 
809 }
810 
811 /*
812 interface notes
813 
814 cursor changes while in rbig to indicate region.
815 only button 1 works for resizing region
816 only button 1 works for moving thingy in ramp
817 
818 button-3 menu: Reset, Depth, Undo, Save, Write
819 */
820 
821 Cursor tl = {
822 	{-4, -4},
823 	{0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff,
824 	 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff,
825 	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
826 	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, },
827 	{0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00,
828 	 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00,
829 	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
830 	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, }
831 };
832 
833 Cursor t = {
834 	{-7, -8},
835 	{0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0,
836 	 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f,
837 	 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff,
838 	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
839 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
840 	 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80,
841 	 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00,
842 	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
843 };
844 
845 Cursor tr = {
846 	{-11, -4},
847 	{0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1,
848 	 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88,
849 	 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88,
850 	 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, },
851 	{0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e,
852 	 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70,
853 	 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
854 	 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, }
855 };
856 
857 Cursor r = {
858 	{-8, -7},
859 	{0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58,
860 	 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02,
861 	 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58,
862 	 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, },
863 	{0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80,
864 	 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc,
865 	 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80,
866 	 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
867 };
868 
869 Cursor br = {
870 	{-11, -11},
871 	{0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
872 	 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88,
873 	 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05,
874 	 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, },
875 	{0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
876 	 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70,
877 	 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa,
878 	 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, }
879 };
880 
881 Cursor b = {
882 	{-7, -7},
883 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
884 	 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
885 	 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70,
886 	 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, },
887 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
888 	 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe,
889 	 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80,
890 	 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }
891 };
892 
893 Cursor bl = {
894 	{-4, -11},
895 	{0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
896 	 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00,
897 	 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01,
898 	 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, },
899 	{0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
900 	 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00,
901 	 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe,
902 	 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, }
903 };
904 
905 Cursor l = {
906 	{-7, -7},
907 	{0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20,
908 	 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20,
909 	 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20,
910 	 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, },
911 	{0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0,
912 	 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0,
913 	 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0,
914 	 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, }
915 };
916 
917 Cursor boxcursor = {
918 	{-7, -7},
919 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
920 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
921 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
922 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, },
923 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
924 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
925 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
926 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, }
927 };
928 
929 Cursor clearcursor;
930 
931 Cursor *corners[10] = {
932 	&tl,	&t,	&tr,
933 	&l,	&boxcursor,	&r,
934 	&bl,	&b,	&br,
935 	nil,	/* default arrow */
936 };
937 
938 char *item[] = {
939 	"Reset",
940 	"Depth",
941 	"Undo",
942 	"Write",
943 	"Exit",
944 	nil
945 };
946 
947 Menu menu = {
948 	item,
949 	nil,
950 	2
951 };
952 
953 /*BUG make less flashy */
954 void
moveface(Image * back,Point lastp,Image * face,Point p,Point d)955 moveface(Image *back, Point lastp, Image *face, Point p, Point d)
956 {
957 	draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min);
958 	draw(back, back->r, screen, nil, addpt(back->r.min, subpt(p, d)));
959 	border(screen, rectaddpt(face->r, subpt(p, d)),
960 		 -1, display->black, ZP);
961 	draw(screen, rectaddpt(face->r, subpt(p, d)),
962 		face, nil, face->r.min);
963 }
964 
965 int
dragface(Mouse * m,Image * im,Point d,int x)966 dragface(Mouse *m, Image *im, Point d, int x)
967 {
968 	int i;
969 	Point lastp;
970 	static Image *back;
971 
972 	if(back == nil){
973 		back = allocimage(display, Rect(-1,-1,49,49), display->image->chan, 0, DNofill);
974 		if(back == nil)
975 			sysfatal("dragface backing store: %r");
976 	}
977 
978 	lastp = m->xy;
979 	draw(back, back->r, screen, nil, addpt(back->r.min, subpt(lastp, d)));
980 	esetcursor(&clearcursor);
981 	do{
982 		moveface(back, lastp, im, m->xy, d);
983 		lastp = m->xy;
984 	}while(*m=emouse(), m->buttons==1);
985 
986 	draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min);
987 	esetcursor(nil);
988 	if(m->buttons==0){
989 		for(i=0; i<nelem(face); i++)
990 			if(ptinrect(m->xy, rface[i]))
991 				return i;
992 		if(ptinrect(m->xy, rsmall))
993 			return -1;
994 		return x;
995 	}
996 	while(*m=emouse(), m->buttons)
997 		;
998 	return x;
999 }
1000 
1001 void
initstate(void)1002 initstate(void)
1003 {
1004 	state.black = 0.0;
1005 	state.white = 1.0;
1006 	state.stretch = 1.0;
1007 	state.depth = 4;
1008 	state.gamma = 1.0;
1009 	setgtab(&state);
1010 	state.selr = insetrect(orig->r, 5);
1011 	sdx = Dx(state.selr);
1012 	sdy = Dy(state.selr);
1013 	if(sdx > sdy)
1014 		state.selr.max.x = state.selr.min.x+sdy;
1015 	else
1016 		state.selr.max.y = state.selr.min.y+sdx;
1017 }
1018 
1019 void
main(int argc,char ** argv)1020 main(int argc, char **argv)
1021 {
1022 	int ccursor, i, fd, k, n, y;
1023 	uchar *data;
1024 	double gammatab[256];
1025 	Event e;
1026 	Mouse m;
1027 	Point lastp, p;
1028 	Rectangle nselr, rbig9[9];
1029 
1030 	ARGBEGIN{
1031 	default:
1032 		usage();
1033 	}ARGEND
1034 
1035 	if(argc > 1)
1036 		usage();
1037 	if(argc == 1){
1038 		if((fd = open(argv[0], OREAD)) < 0)
1039 			sysfatal("open %s: %r", argv[0]);
1040 	}else
1041 		fd = 0;
1042 
1043 	if (initdraw(0, 0, "mug") < 0)
1044 		sysfatal("initdraw failed");
1045 
1046 	if((orig = readimage(display, fd, 0)) == nil)
1047 		sysfatal("readimage: %r");
1048 
1049 	orig = grey8image(orig);
1050 
1051 	initramp();
1052 	initclamp();
1053 	initval2cmap();
1054 	bkgd = allocimagemix(display, DPaleyellow, DWhite);
1055 	small = allocimage(display, Rect(0,0,48,48), GREY4, 0, DWhite);
1056 	tmp8 = allocimage(display, Rect(0,0,48,48), GREY8, 0, DWhite);
1057 	red = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DRed);
1058 	green = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DGreen);
1059 	blue = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DBlue);
1060 	if(bkgd==nil || small==nil || tmp8==nil || red==nil || green==nil || blue==nil)
1061 		sysfatal("allocimage: %r");
1062 
1063 	n = Dx(orig->r)*Dy(orig->r);
1064 	data = emalloc(n*sizeof data[0]);
1065 	rdata = emalloc(n*sizeof rdata[0]);
1066 
1067 	if(unloadimage(orig, orig->r, data, n) != n)
1068 		sysfatal("unloadimage: %r");
1069 
1070 	for(i=0; i<256; i++)
1071 		gammatab[i] = pow((255-i)/(double)255.0, GAMMA);
1072 
1073 	for(i=0; i<n; i++)
1074 		rdata[i] = gammatab[255-data[i]];
1075 
1076 	initstate();
1077 	process(rdata, orig->r, state.selr, small);
1078 	drawscreen(1);
1079 	flushimage(display, 1);
1080 	einit(Emouse|Ekeyboard);
1081 	ccursor = 9;
1082 	for(;;){
1083 		if((n=eread(Emouse|Ekeyboard, &e))==Ekeyboard)
1084 			continue;
1085 		if(n != Emouse)
1086 			break;
1087 
1088 		m = e.mouse;
1089 		if(m.buttons&4){
1090 			ccursor = 9;
1091 			esetcursor(corners[ccursor]);
1092 			switch(emenuhit(3, &m, &menu)){
1093 			case -1:
1094 				continue;
1095 			case 0:	/* Reset */
1096 				mark();
1097 				initstate();
1098 				small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite);
1099 				if(small == nil)
1100 					sysfatal("allocimage: %r");
1101 				process(rdata, orig->r, state.selr, small);
1102 				drawface(-1);
1103 				drawscreen(0);
1104 				break;
1105 			case 1:	/* Depth */
1106 				mark();
1107 				/* osmall = small, so no freeimage */
1108 				state.depth /= 2;
1109 				if(state.depth == 0)
1110 					state.depth = 8;
1111 				small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite);
1112 				if(small == nil)
1113 					sysfatal("allocimage: %r");
1114 				process(rdata, orig->r, state.selr, small);
1115 				drawface(-1);
1116 				break;
1117 			case 2:	/* Undo */
1118 				undo();
1119 				break;
1120 			case 3:	/* Write */
1121 				writeface(nil, small);
1122 				break;
1123 			case 4:	/* Exit */
1124 				exits(nil);
1125 				break;
1126 			}
1127 		}
1128 
1129 		if(ptinrect(m.xy, rbig)){
1130 			rlist(rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), rbig9);
1131 			for(i=0; i<9; i++)
1132 				if(ptinrect(m.xy, rbig9[i]))
1133 					break;
1134 			if(i != ccursor){
1135 				ccursor = i;
1136 				esetcursor(corners[ccursor]);
1137 			}
1138 			if(i==9)
1139 				continue;
1140 
1141 			if(m.buttons & 1){
1142 				mark();
1143 				lastp = m.xy;
1144 				while(m=emouse(), m.buttons&1){
1145 					if(move(state.selr, orig->r, subpt(m.xy, lastp), i, &nselr)){
1146 						moveframe(state.selr, nselr);
1147 						state.selr = nselr;
1148 						lastp = m.xy;
1149 						process(rdata, orig->r, state.selr, small);
1150 						drawface(-1);
1151 					}
1152 				}
1153 			}
1154 			continue;
1155 		}
1156 
1157 		if(ccursor != 9){	/* default cursor */
1158 			ccursor = 9;
1159 			esetcursor(corners[ccursor]);
1160 		}
1161 
1162 		if(ptinrect(m.xy, rramp)){
1163 			if(m.buttons != 1)
1164 				continue;
1165 			mark();
1166 			y = gamma2y(state.gamma);
1167 			if(abs(y-(m.xy.y-rramp.min.y)) > 5)
1168 				continue;
1169 			k = section(m.xy.x-rramp.min.x);
1170 			drawrampbar(green, &state);
1171 			lastp = m.xy;
1172 			while(m=emouse(), m.buttons&1){
1173 				if(!ptinrect(m.xy, rramp))
1174 					continue;
1175 				switch(k){
1176 				case -1:
1177 					continue;
1178 				case 0:
1179 					if((m.xy.x-rramp.min.x)/255.0 < state.white){
1180 						state.black = (m.xy.x-rramp.min.x)/255.0;
1181 						break;
1182 					}
1183 					continue;
1184 				case 1:
1185 					state.gamma = y2gamma(m.xy.y-rramp.min.y);
1186 					setgtab(&state);
1187 					break;
1188 				case 2:
1189 					if((m.xy.x-rramp.min.x)/255.0 > state.black){
1190 						state.white = (m.xy.x-rramp.min.x)/255.0;
1191 						break;
1192 					}
1193 					continue;
1194 				case 10:
1195 					state.black += (m.xy.x-lastp.x)/255.0;
1196 					state.white += (m.xy.x-lastp.x)/255.0;
1197 					state.gamma = y2gamma(p.y);
1198 					break;
1199 				}
1200 				process(rdata, orig->r, state.selr, small);
1201 				drawface(-1);
1202 				drawrampbar(green, &state);
1203 			}
1204 			if(m.buttons == 0){
1205 				process(rdata, orig->r, state.selr, small);
1206 				drawface(-1);
1207 				drawrampbar(red, &state);
1208 			}else
1209 				undo();
1210 			continue;
1211 		}
1212 
1213 		if(ptinrect(m.xy, rsmall)){
1214 			if(m.buttons != 1)
1215 				continue;
1216 			n=dragface(&m, small, subpt(m.xy, rsmall.min), -1);
1217 			if(n == -1)
1218 				continue;
1219 			saveface(nil, n);
1220 		}
1221 
1222 		for(i=0; i<nelem(face); i++)
1223 			if(ptinrect(m.xy, rface[i]))
1224 				break;
1225 		if(i<nelem(face) && face[i] != nil){
1226 			if(m.buttons != 1)
1227 				continue;
1228 			n=dragface(&m, face[i]->small, subpt(m.xy, rface[i].min), i);
1229 			if(n == i)
1230 				continue;
1231 			saveface(face[i], n);
1232 			continue;
1233 		}
1234 
1235 		do
1236 			m = emouse();
1237 		while(m.buttons==1);
1238 	}
1239 	exits(nil);
1240 }
1241