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}