Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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*
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
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
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
102
gamma2y(double g)
103
{
104
	g -= 0.5;
105
	return (int)(128.0*sqrt(g)+0.5);
106
}
107
 
108
void
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
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
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
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
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
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
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
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
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
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
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*
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*
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
472
mark(void)
473
{
474
	if(osmall != small){
475
		freeimage(osmall);
476
		osmall = small;
477
	}
478
	ostate = state;
479
}
480
 
481
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
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
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
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
586
min(int a, int b)
587
{
588
	if(a < b)
589
		return a;
590
	return b;
591
}
592
 
593
int
594
max(int a, int b)
595
{
596
	if(a > b)
597
		return a;
598
	return b;
599
}
600
 
601
int
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
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
788
abs(int a)
789
{
790
	if(a < 0)
791
		return -a;
792
	return a;
793
}
794
 
795
void
796
usage(void)
797
{
798
	fprint(2, "usage: mug [file.bit]\n");
799
	exits("usage");
800
}
801
 
802
void
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
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
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
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
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
}