xref: /inferno-os/appl/grid/demo/blur.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Blur;
2
3include "tk.m";
4	tk: Tk;
5include "tkclient.m";
6	tkclient: Tkclient;
7include "sys.m";
8	sys : Sys;
9include "daytime.m";
10	daytime: Daytime;
11include "draw.m";
12	draw: Draw;
13	Display, Chans, Point, Rect, Image: import draw;
14include "readdir.m";
15	readdir: Readdir;
16include "grid/demo/exproc.m";
17	exproc: Exproc;
18include "grid/demo/block.m";
19	block: Block;
20
21display : ref draw->Display;
22context : ref draw->Context;
23path := "/tmp/blur/";
24
25Blur : module {
26	init : fn (ctxt : ref Draw->Context, nil : list of string);
27	getslavedata : fn (lst: list of string);
28	doblock : fn (block: int, bpath: string);
29	readblock : fn (block: int, dir: string, chanout: chan of string): int;
30	finish : fn (waittime: int, tkchan: chan of string);
31};
32
33init(ctxt : ref Draw->Context, argv : list of string)
34{
35	sys = load Sys Sys->PATH;
36	if (sys == nil)
37		badmod(Sys->PATH);
38	draw = load Draw Draw->PATH;
39	if (draw == nil)
40		badmod(Draw->PATH);
41	daytime = load Daytime Daytime->PATH;
42	if (daytime == nil)
43		badmod(Daytime->PATH);
44	tk = load Tk Tk->PATH;
45	if (tk == nil)
46		badmod(Tk->PATH);
47	tkclient = load Tkclient Tkclient->PATH;
48	if (tkclient == nil)
49		badmod(Tkclient->PATH);
50	tkclient->init();
51	readdir = load Readdir Readdir->PATH;
52	if (readdir == nil)
53		badmod(Readdir->PATH);
54	exproc = load Exproc "$self";
55	if (exproc == nil)
56		badmod(sys->sprint("Exproc: %r"));
57	block = load Block Block->PATH;
58	if (block == nil)
59		badmod(Block->PATH);
60	if (ctxt == nil) {
61		display = Display.allocate(nil);
62		if (display == nil)
63			usage(sys->sprint("failed to get a display: %r"));
64		context = nil;
65	}
66	else {
67		display = ctxt.display;
68		context = ctxt;
69	}
70	spawn blurit(argv);
71}
72
73blurit(argv: list of string)
74{
75	mast := 0;
76	size = 12;
77	blocks = Point (10,6);
78	filename := "";
79
80	argv = tl argv;
81	if (len argv > 2)
82		usage("too many arguments");
83
84	for (; argv != nil; argv = tl argv) {
85		(n,dir) := sys->stat(hd argv);
86		if (n == -1)
87			usage("file/directory '"+hd argv+"' does not exist");
88		if (dir.mode & sys->DMDIR)
89			path = hd argv;
90		else {
91			filename = hd argv;
92			mast = 1;
93		}
94	}
95	if (mast && context == nil)
96		usage("nil context - cannot be used as master");
97	if (path[len path - 1] != '/')
98		path[len path] = '/';
99	if (len path < 5 || path[len path - 5:] != "blur/")
100		path += "blur/";
101	block->init(path, exproc);
102	if (mast)
103		spawn master(filename);
104	else {
105		sys->print("starting slave\n");
106		spawn block->slave();
107	}
108}
109
110usage(err: string)
111{
112	sys->print("usage: blur [dir] [image]\n");
113	if (err != nil) {
114		sys->print("Error: %s\n",err);
115		raise "fail:error";
116	}
117	else
118		exit;
119}
120
121getslavedata(lst: list of string)
122{
123	if (lst == nil || len lst < 5)
124		block->err("Cannot read data file");
125	size = int hd lst;
126	blocks = Point(int hd tl lst, int hd tl tl lst);
127	bsize = Point(int hd tl tl tl lst, int hd tl tl tl tl lst);
128	blockimg = display.newimage(((0,0),bsize), draw->RGB24,0,draw->Red);
129}
130
131blocks, bsize: Draw->Point;
132size: int;
133newimg: ref Draw->Image;
134
135getxy(i, w: int): (int, int)
136{
137	y := i / w;
138	x := i - (y * w);
139	return (x,y);
140}
141
142master(filename: string)
143{
144	block->cleanfiles(path);
145	img := display.open(filename);
146	if (img == nil)
147		block->err("cannot read image: "+filename);
148	if (img.chans.depth() != 24)
149			block->err("wrong image depth! (must be 24bit)\n");
150	sys->create(path, sys->OREAD, 8r777 | sys->DMDIR);
151
152	blocks.x = img.r.dx() / 70;
153	if (blocks.x < 1)
154		blocks.x = 1;
155	blocks.y = img.r.dy() / 70;
156	if (blocks.y < 1)
157		blocks.y = 1;
158
159	bsize = Point(img.r.dx()/blocks.x, img.r.dy()/blocks.y);
160
161	data := sys->sprint("%d\n%d\n%d\n%d\n%d\n",size,blocks.x,blocks.y,bsize.x,bsize.y);
162	noblocks := blocks.x * blocks.y;
163
164	n := 0;
165	for (y := 0; y < blocks.y; y++) {
166		for (x := 0; x < blocks.x; x++) {
167			r2 := Rect(((x*bsize.x)-size, (y*bsize.y)-size),
168					(((1+x)*bsize.x)+size, ((1+y)*bsize.y)+size));
169			if (r2.min.x < 0)
170				r2.min.x = 0;
171			if (r2.min.y < 0)
172				r2.min.y = 0;
173			if (r2.max.x > img.r.max.x)
174				r2.max.x = img.r.max.x;
175			if (r2.max.y > img.r.max.y)
176				r2.max.y = img.r.max.y;
177
178			tmpimg := display.newimage(r2,draw->RGB24,0,draw->Black);
179			tmpimg.draw(r2, img, nil, r2.min);
180			fdtmp := sys->create(path+"imgdata."+string n+".bit", sys->OWRITE, 8r666);
181			if (fdtmp == nil)
182				sys->print("couldn't write image: '%s' %r\n",path+"imgdata."+string n+".bit");
183			display.writeimage(fdtmp, tmpimg);
184			n++;
185		}
186	}
187	block->writedata(data);
188	block->masterinit(noblocks);
189
190	(top, titlebar) := tkclient->toplevel(context, "", "Blur", Tkclient->Hide);
191	tkcmd(top, "frame .f");
192	r2 := Rect((0,0),(blocks.x*bsize.x,blocks.y*bsize.y));
193	newimg = display.newimage(r2,draw->RGB24,0,draw->Black);
194	newimg.draw(r2,img,nil,(0,0));
195	tkcmd(top, sys->sprint("panel .f.p -height %d -width %d", r2.dy(), r2.dx()));
196	tk->putimage(top, ".f.p", newimg, nil);
197	tkcmd(top, "label .f.l1 -text {Processed: }");
198	tkcmd(top, "label .f.l2 -text {0%} -width 30");
199	tkcmd(top, "grid .f.p -row 0 -column 0 -columnspan 2");
200	tkcmd(top, "grid .f.l1 -row 1 -column 0 -sticky e");
201	tkcmd(top, "grid .f.l2 -row 1 -column 1 -sticky w");
202	tkcmd(top, "pack .f");
203	tkcmd(top, "bind .Wm_t <Button-1> +{focus .}");
204	tkcmd(top, "bind .Wm_t.title <Button-1> +{focus .}");
205	tkcmd(top, "focus .; update");
206
207	tkchan := chan of string;
208	sync := chan of int;
209	spawn block->reader(noblocks, tkchan, sync);
210	readerpid := <-sync;
211	spawn window(top, titlebar, newimg, tkchan, readerpid);
212}
213
214blockimg: ref Draw->Image;
215
216doblock(block: int, bpath: string)
217{
218	(x,y) := getxy(block, blocks.x);
219	procimg := display.open(path+"imgdata."+string block+".bit");
220	if (procimg == nil)
221		sys->print("Error nil image! '%s' %r\n",path+"imgdata."+string block+".bit");
222	blurred := procblock(procimg, x,y,0,size,bsize);
223	sketched := procblock(procimg, x,y,1,3,bsize);
224	for (i := 0; i < len blurred; i++) {
225		if (sketched[i] != byte 127)
226			blurred[i] = sketched[i];
227	}
228	blockimg.writepixels(((0,0),bsize), blurred);
229	fd := sys->create(path + bpath+"/img.bit",sys->OWRITE,8r666);
230	display.writeimage(fd, blockimg);
231	fd = nil;
232	sys->create(path + bpath+"/done", sys->OWRITE, 8r666);
233}
234
235window(top: ref Tk->Toplevel, titlebar: chan of string,
236		img: ref Image, tkchan: chan of string, readerpid: int)
237{
238	total := blocks.x * blocks.y;
239	done := 0;
240	tkclient->onscreen(top, nil);
241	tkclient->startinput(top, "kbd"::"ptr"::nil);
242	finished := 0;
243	main: for(;;) alt {
244		s := <-top.ctxt.kbd =>
245			tk->keyboard(top, s);
246		s := <-top.ctxt.ptr =>
247			tk->pointer(top, *s);
248		inp := <- tkchan =>
249			(n, lst) := sys->tokenize(inp, " \n\t");
250			case hd lst {
251				"done" =>
252					done++;
253					tkcmd(top, ".f.l2 configure -text {"+string ((100*done)/total)+"%}");
254					tkcmd(top, ".f.p dirty");
255				"time" =>
256					tkcmd(top, ".f.l1 configure -text {Time taken:}");
257					tkcmd(top, ".f.l2 configure -text {"+hd tl lst+"} -width 80");
258					finished = 1;
259				* =>
260					tkcmd(top, ".f.l2 configure -text {"+inp+"%}");
261			}
262			tkcmd(top, "update");
263
264		title := <-top.ctxt.ctl or
265		title = <-top.wreq or
266		title = <- titlebar =>
267			if (title == "exit") {
268				if (finished) {
269					kill(readerpid);
270					break main;
271				}
272			}
273			else
274				tkclient->wmctl(top, title);
275	}
276	spawn block->cleanfiles(path);
277}
278
279readblock(block: int, dir: string, chanout: chan of string): int
280{
281	img := display.open(dir+"img.bit");
282	if (img == nil)
283		return -1;
284	(ix,iy) := getxy(block, blocks.x);
285	newimg.draw(img.r.addpt(Point(ix*bsize.x, iy*bsize.y)),img,nil,(0,0));
286	chanout <-= "done";
287	return 0;
288}
289
290finish(waittime: int, tkchan: chan of string)
291{
292	hrs := waittime / 360;
293	mins := (waittime - (360 * hrs)) / 60;
294	secs := waittime - (360 * hrs) - (60 * mins);
295	time := addzeros(sys->sprint("%d:%d:%d",hrs,mins,secs));
296	if (hrs == 0) time = time[3:];
297	tkchan <-= "time "+time;
298	block->cleanfiles(path);
299}
300
301procblock(procimg: ref Image, x,y, itype, size: int, bsize: Point): array of byte
302{
303	r := Rect((x*bsize.x, y*bsize.y), ((1+x)*bsize.x, (1+y)*bsize.y));
304	r2 : Rect;
305	if (itype == 0)
306		r2 = procimg.r;
307	else
308		r2 = Rect((x*bsize.x, y*bsize.y), (((1+x)*bsize.x)+1, ((1+y)*bsize.y)+1));
309	if (r2.min.x < 0)
310		r2.min.x = 0;
311	if (r2.min.y < 0)
312		r2.min.y = 0;
313	if (r2.max.x > procimg.r.max.x)
314		r2.max.x = procimg.r.max.x;
315	if (r2.max.y > procimg.r.max.y)
316		r2.max.y = procimg.r.max.y;
317
318	buf := array[3 * r2.dx() * r2.dy()] of byte;
319	procimg.readpixels(r2,buf);
320	pad := Rect((r.min.x-r2.min.x, r.min.y-r2.min.y), (r2.max.x - r.max.x, r2.max.y-r.max.y));
321	if (itype == 0)
322		return blurblock(size,r,pad,buf);
323	if (itype == 1)
324		return gradblock(10,r,pad,buf);
325	return nil;
326}
327
328makepic(buf: array of int, w,nw,nh: int): array of byte
329{
330	newbuf := array[3*nw*nh] of byte;
331	n := 0;
332	for (y := 0; y < nh; y++) {
333		for (x := 0; x < nw; x++) {
334			val := byte buf[(y*w)+x];
335			if (val < byte 0) val = -val;
336			if (val > byte 255) val = byte 255;
337			for (i := 0; i < 3; i++)
338				newbuf[n++] = val;
339		}
340	}
341	return newbuf;
342}
343
344gradblock(threshold: int, r, pad: Rect, buffer: array of byte) : array of byte
345{
346	gradbufx := array[3] of array of int;
347	gradbufy := array[3] of array of int;
348	width: int;
349	cleaning := 3;
350	for (rgb := 0; rgb < 3; rgb++) {
351
352		greybuf := array[len buffer] of { * => 0 };
353		n := 0;
354		width = r.dx()+pad.max.x;
355		for (y := 0; y < r.dy()+pad.max.y; y++) {
356			for (x := 0; x < r.dx()+pad.max.x; x++) {
357				greybuf[n++] = int buffer[(3* ((y*width) + x ))+rgb];
358			}
359		}
360
361		for(i := 0; i < 2; i++) {
362			padx := pad.max.x;
363			pady := pad.max.y;
364			width = r.dx();
365			height := r.dy();
366			gradbuf: array of int;
367			(gradbuf, width, height, padx, pady) = getgrad(greybuf, i, width,height, padx, pady);
368			width = r.dx();
369			if (i == 0) {
370				gradbufx[rgb] = clean(hyster(gradbuf,1,width,threshold), width,5,4);
371				for (k := 0; k < cleaning; k++)
372					gradbufx[rgb] = clean(gradbufx[rgb], width,2,2);
373			}
374			else {
375				gradbufy[rgb] = clean(hyster(gradbuf, 0,width,threshold), width,5,4);
376				for (k := 0; k < cleaning; k++)
377					gradbufy[rgb] = clean(gradbufy[rgb], width,2,2);
378			}
379		}
380
381	}
382	newbuf := array[len gradbufx[0]] of int;
383	for (i := 0; i < len newbuf; i++) {
384		val := 127;
385		n := 0;
386		for (rgb = 0; rgb < 3; rgb++) {
387			if (gradbufx[rgb][i] != 127) {
388				n++;
389				val = gradbufx[rgb][i];
390			}
391			else if (gradbufy[rgb][i] != 127) {
392				val = gradbufy[rgb][i];
393				n++;
394			}
395		}
396		if (n > 1)
397			newbuf[i] = val;
398		else
399			newbuf[i] = 127;
400	}
401	if (sat(newbuf) > 25 && threshold > 4)
402		return gradblock(threshold - 2,r,pad,buffer);
403	return makepic(newbuf,width,r.dx(),r.dy());
404}
405
406X: con 0;
407Y: con 1;
408
409getgrad(buf: array of int, dir, w,h, px, py: int): (array of int, int, int, int, int)
410{
411	npx := px - 1;
412	npy := py - 1;
413	if (npx < 0) npx = 0;
414	if (npy < 0) npy = 0;
415	gradbuf := array[(w+npx)*(h+npy)] of int;
416	n := 0;
417	val1, val2: int;
418	for (y := 0; y < h+npy; y++) {
419		for (x := 0; x < w+npx; x++) {
420			val1 = buf[(y*(w+px)) + x];
421			if ((dir == X && x-w >= npx) ||
422				(dir == Y && y-h >= npy))
423				val2 = val1;
424			else
425				val2 = buf[((y+dir)*(w+px)) + x + 1 - dir];
426			gradbuf[n++] = val2 - val1;
427		}
428	}
429	return (norm(gradbuf,0,255), w, h, px,py);
430}
431
432sat(a: array of int): int
433{
434	n := 0;
435	for (i := 0; i < len a; i++)
436		if (a[i] != 127)
437			n++;
438	return (100 * n)/ len a;
439}
440
441hyster(a: array of int, gox, width: int, lim: int): array of int
442{
443	min, max: int;
444	av := 0;
445	for (i := 0; i < len a; i++) {
446		if (i == 0)
447			min = max = a[i];
448		if (a[i] < min)
449			min = a[i];
450		if (a[i] > max)
451			max = a[i];
452		av += a[i];
453	}
454#	sys->print("%d/%d = %d\n",av,len a,av / len a);
455	av = av/len a;
456	upper := av + ((max-av)/lim);
457	lower := av - ((av-min)/ lim);
458	low := 0;
459#	sys->print("len a: %d %d %d %d\n",len a,av,min,max);
460	i = 0;
461	x := 0;
462	y := 0;
463	height := len a / width;
464	newline := 1;
465#	sys->print("width: %d gox: %d\n",width,gox);
466	for (k := 0; k < len a; k++) {
467		i = (y*width) + x;
468		if (newline) {
469#			if (a[i] < av) low = 1;
470#			else low = 0;
471			low = a[i] > av;
472			newline = 0;
473		}
474		oldlow := low;
475		if (low == 0) {
476			if (a[i] > upper)
477				low = 1;
478		}
479		else if (low == 1) {
480			if (a[i] < lower)
481				low = 0;
482		}
483#		sys->print("a[i]: %d bound: %d %d low %d => %d\n",a[i],lower,upper,oldlow,low);
484		if (oldlow == low)
485			a[i] =127;
486		else
487			a[i] = low * 255;
488
489		if (gox) {
490			i++;
491			x++;
492			if (x == width) {
493				x = 0;
494				y++;
495				newline = 1;
496			}
497		}
498		else {
499			i += width;
500			y++;
501			if (y == height) {
502#				sys->print("y: %d\n",y);
503				y = 0;
504				i = x;
505				x++;
506				newline = 1;
507			}
508		}
509	}
510	return a;
511}
512
513clean(a: array of int, width, r, d: int): array of int
514{
515	height := len a / width;
516	csize := (2*r) ** 2;
517	for (y := 0; y < height; y++) {
518		for (x := 0; x < width; x++) {
519			i := (width*y)+x;
520			if (a[i] != 127) {
521				sx := x - r;
522				if (sx < 0) sx = 0;
523				ex := x + r;
524				if (ex > width) ex = width;
525				sy := y - r;
526				if (sy < 0) sy = 0;
527				ey := y + r;
528				n := 0;
529				if (ey > height) ey = height;
530				for (iy := sy; iy < ey; iy++) {
531					for (ix := sx; ix < ex; ix++) {
532						if (a[(width*iy)+ix] == a[i])
533							n++;
534					}
535				}
536				#sys->print("%f\n",real ((ex-sx)*(ey-sy))/ real csize);
537#				if (n < int (real d * (real ((ex-sx)*(ey-sy))/ real csize)))
538				if (n < d)
539					a[i] = 127;
540			}
541		}
542	}
543	return a;
544}
545
546
547norm(a: array of int, lower, upper: int): array of int
548{
549	min, max: int;
550	for (i := 0; i < len a; i++) {
551		if (i == 0)
552			min = max = a[i];
553		if (a[i] < min)
554			min = a[i];
555		if (a[i] > max)
556			max = a[i];
557	}
558	multi : real = (real (upper - lower)) / (real (max - min));
559	add := real (lower - min);
560	for (i = 0; i < len a; i++) {
561		a[i] = int ((add + real a[i]) * multi);
562		if (a[i] < lower)
563			a[i] = lower;
564		if (a[i] > upper)
565			a[i] = upper;
566	}
567	return a;
568}
569
570opt := 2;
571
572blurblock(size: int, r, pad: Rect, buffer: array of byte) : array of byte
573{
574	newbuf := array[3 * r.dx() * r.dy()] of byte;
575	n := 0;
576	width := r.dx()+pad.min.x+pad.max.x;
577	for (y := 0; y < r.dy(); y++) {
578		for (x := 0; x < r.dx(); x++) {
579			r2 := Rect((x-size,y-size),(x+size+1,y+size+1));
580			if (r2.min.x < -pad.min.x)
581				r2.min.x = -pad.min.x;
582			if (r2.min.y < -pad.min.y)
583				r2.min.y = -pad.min.y;
584			if (r2.max.x > r.dx()+pad.max.x)
585				r2.max.x = r.dx()+pad.max.x;
586			if (r2.max.y > r.dy()+pad.max.y)
587				r2.max.y = r.dy()+pad.max.y;
588			nosamples := r2.dx()*r2.dy();
589
590			r2.min.x += pad.min.x;
591			r2.min.y += pad.min.y;
592			r2.max.x += pad.min.x;
593			r2.max.y += pad.min.y;
594			pixel := array[3] of { * => 0};
595			for (sy := r2.min.y; sy < r2.max.y; sy++) {
596				for (sx := r2.min.x; sx < r2.max.x; sx++) {
597					for (i := 0; i < 3; i++)
598						pixel[i] += int buffer[(3* ( ((sy)*width) + (sx) ) )+ i];
599				}
600			}
601			for (i := 0; i < 3; i++) {
602				if (opt == 0)
603					newbuf[n++] = byte (pixel[i] / nosamples);
604				if (opt == 1)
605					newbuf[n++] = byte (255 - (pixel[i] / nosamples));
606				if (opt == 2)
607					newbuf[n++] = byte (63 + (pixel[i] / (2*nosamples)));
608
609			}
610
611		}
612	}
613	return newbuf;
614}
615
616tkcmd(top: ref Tk->Toplevel, cmd: string): string
617{
618	e := tk->cmd(top, cmd);
619	if (e != "" && e[0] == '!') sys->print("tk error: '%s': %s\n",cmd,e);
620	return e;
621}
622
623addzeros(s: string): string
624{
625	s[len s] = ' ';
626	rs := "";
627	start := 0;
628	isnum := 0;
629	for (i := 0; i < len s; i++) {
630		if (s[i] < '0' || s[i] > '9') {
631			if (isnum && i - start < 2) rs[len rs] = '0';
632			rs += s[start:i+1];
633			start = i+1;
634			isnum = 0;
635		}
636		else isnum = 1;
637	}
638	i = len rs - 1;
639	while (i >= 0 && rs[i] == ' ') i--;
640	return rs[:i+1];
641}
642
643kill(pid: int)
644{
645	pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE);
646	if (pctl != nil)
647		sys->write(pctl, array of byte "kill", len "kill");
648}
649
650badmod(path: string)
651{
652	sys->print("Blur: failed to load: %s\n",path);
653	exit;
654}