diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/mug.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/mug.c')
-rwxr-xr-x | sys/src/cmd/mug.c | 1240 |
1 files changed, 1240 insertions, 0 deletions
diff --git a/sys/src/cmd/mug.c b/sys/src/cmd/mug.c new file mode 100755 index 000000000..27c4d6644 --- /dev/null +++ b/sys/src/cmd/mug.c @@ -0,0 +1,1240 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <event.h> +#include <cursor.h> + +#define initstate muginitstate + +typedef struct State State; +struct State { + double black; + double white; + double stretch; + double gamma; + int depth; + int gtab[1001]; + Rectangle selr; +}; + +typedef struct Face Face; +struct Face { + Rectangle r; + State state; + Image *small; +}; + +double GAMMA = 1.0; /* theory tells me this should be 2.2, but 1.0 sure looks better */ +enum { + Left=0, + Right, + Top, + Bottom, + + RTopLeft=0, + RTop, + RTopRight, + RLeft, + RMiddle, + RRight, + RBotLeft, + RBot, + RBotRight, +}; + +void* +emalloc(ulong sz) +{ + void *v; + + v = malloc(sz); + if(v == nil) + sysfatal("malloc %lud fails", sz); + memset(v, 0, sz); + return v; +} + +Face *face[8]; +int nface; +uchar grey2cmap[256]; +Image *bkgd; +Image *orig; +Image *ramp, *small, *osmall, *tmp8, *red, *green, *blue; +State state, ostate; +uchar val2cmap[256]; +uchar clamp[3*256]; +Rectangle rbig, rramp, rface[nelem(face)], rsmall; +double *rdata; +int sdy, sdx; + +void +geometry(Rectangle r) +{ + int i; + Rectangle fr[9]; + + rramp.min = addpt(r.min, Pt(4,4)); + rramp.max = addpt(rramp.min, Pt(256,256)); + + rbig.min = Pt(rramp.max.x+6, rramp.min.y); + rbig.max = addpt(rbig.min, Pt(Dx(orig->r), Dy(orig->r))); + + for(i=0; i<9; i++) + fr[i] = rectaddpt(Rect(0,0,48,48), Pt(rramp.min.x+48+56*(i%3), rramp.max.y+6+56*(i/3))); + + rsmall = fr[4]; + for(i=0; i<4; i++) + rface[i] = fr[i]; + for(i=4; i<8; i++) + rface[i] = fr[i+1]; +} + +double +y2gamma(int y) +{ + double g; + + g = (double)y / 128.0; + return 0.5+g*g; /* gamma from 0.5 to 4.5, with 1.0 near the middle */ +} + +int +gamma2y(double g) +{ + g -= 0.5; + return (int)(128.0*sqrt(g)+0.5); +} + +void +drawface(int i) +{ + if(i==-1){ + border(screen, rsmall, -3, blue, ZP); + draw(screen, rsmall, small, nil, ZP); + return; + } + border(screen, rface[i], -1, display->black, ZP); + if(face[i]) + draw(screen, rface[i], face[i]->small, nil, ZP); + else + draw(screen, rface[i], display->white, nil, ZP); +} + +void +drawrampbar(Image *color, State *s) +{ + Rectangle liner, r; + static Rectangle br; + + if(Dx(br)) + draw(screen, br, ramp, nil, subpt(br.min, rramp.min)); + + r = rramp; + r.max.x = r.min.x + (int)(s->white*255.0); + r.min.x += (int)(s->black*255.0); + r.min.y += gamma2y(s->gamma); + r.max.y = r.min.y+1; + rectclip(&r, rramp); + draw(screen, r, color, nil, ZP); + br = r; + + r.min.y -= 2; + r.max.y += 2; + + liner = r; + r.min.x += Dx(liner)/3; + r.max.x -= Dx(liner)/3; + rectclip(&r, rramp); + draw(screen, r, color, nil, ZP); + combinerect(&br, r); + + r = liner; + r.max.x = r.min.x+3; + rectclip(&r, rramp); + draw(screen, r, color, nil, ZP); + combinerect(&br, r); + + r = liner; + r.min.x = r.max.x-3; + rectclip(&r, rramp); + draw(screen, r, color, nil, ZP); + combinerect(&br, r); +} + +void +drawscreen(int clear) +{ + int i; + + if(clear){ + geometry(screen->r); + draw(screen, screen->r, bkgd, nil, ZP); + } + + border(screen, rbig, -1, display->black, ZP); + draw(screen, rbig, orig, nil, orig->r.min); + + border(screen, rramp, -1, display->black, ZP); + draw(screen, rramp, ramp, nil, ramp->r.min); + drawrampbar(red, &state); + + border(screen, rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), -2, red, ZP); + if(clear){ + drawface(-1); + for(i=0; i<nelem(face); i++) + drawface(i); + } +} + +void +moveframe(Rectangle old, Rectangle new) +{ + border(screen, rectaddpt(old, subpt(rbig.min, orig->r.min)), -2, orig, old.min); + border(screen, rectaddpt(new, subpt(rbig.min, orig->r.min)), -2, red, ZP); +} + + +/* + * Initialize gamma ramp; should dither for + * benefit of non-true-color displays. + */ +void +initramp(void) +{ + int k, x, y; + uchar dat[256*256]; + double g; + + k = 0; + for(y=0; y<256; y++) { + g = y2gamma(y); + for(x=0; x<256; x++) + dat[k++] = 255.0 * pow(x/255.0, g); + } + assert(k == sizeof dat); + + ramp = allocimage(display, Rect(0,0,256,256), GREY8, 0, DNofill); + if(ramp == nil) + sysfatal("allocimage: %r"); + + if(loadimage(ramp, ramp->r, dat, sizeof dat) != sizeof dat) + sysfatal("loadimage: %r"); +} + +void +initclamp(void) +{ + int i; + + for(i=0; i<256; i++) { + clamp[i] = 0; + clamp[256+i] = i; + clamp[512+i] = 255; + } +} + +void +changestretch(double stretch) +{ + state.stretch = stretch; +} + +/* + * There is greyscale data for the rectangle datar in data; + * extract square r and write it into the 48x48 pixel image small. + */ +void +process(double *data, Rectangle datar, Rectangle r, Image *small) +{ + double black, center, delta, *k, shrink, sum, *tmp[48], *tt, w, white, x; + int datadx, dp, dx, dy, error, i, ii, j, jj; + int ksize, ksizeby2, sdata[48*48], sd, sh, sm, sv, u, uu, uuu, v, vv; + uchar bdata[48*48]; + + datadx = Dx(datar); + dx = Dx(r); + dy = Dy(r); + shrink = dx/48.0; + + ksize = 1+2*(int)(shrink/2.0); + if(ksize <= 2) + return; + + k = emalloc(ksize*sizeof(k[0])); + + /* center of box */ + for(i=1; i<ksize-1; i++) + k[i] = 1.0; + + /* edges */ + x = shrink - floor(shrink); + k[0] = x; + k[ksize-1] = x; + + sum = 0.0; + for(i=0; i<ksize; i++) + sum += k[i]; + + for(i=0; i<ksize; i++) + k[i] /= sum; + + ksizeby2 = ksize/2; + + for(i=0; i<48; i++) + tmp[i] = emalloc(datadx*sizeof(tmp[i][0])); + + /* squeeze vertically */ + for(i=0; i<48; i++) { + ii = r.min.y+i*dy/48; + tt = tmp[i]; + uu = ii - ksizeby2; + for(j=r.min.x-ksize; j<r.max.x+ksize; j++) { + if(j<datar.min.x || j>=datar.max.x) + continue; + w = 0.0; + + uuu = uu*datadx+j; + if(uu>=datar.min.y && uu+ksize<datar.max.y) + for(u=0; u<ksize; u++){ + w += k[u]*data[uuu]; + uuu += datadx; + } + else + for(u=0; u<ksize; u++){ + if(uu+u>=datar.min.y && uu+u<datar.max.y) + w += k[u]*data[uuu]; + uuu+=datadx; + } + tt[j-datar.min.x] = w; + } + } + + /* stretch value scale */ + center = (state.black+state.white)/2; + delta = state.stretch*(state.white-state.black)/2; + black = center - delta; + white = center + delta; + + /* squeeze horizontally */ + for(i=0; i<48; i++) { + tt = tmp[i]; + for(j=0; j<48; j++) { + jj = r.min.x+j*dx/48; + w = 0.0; + for(v=0; v<ksize; v++) { + vv = jj - ksizeby2 + v; + if(vv<datar.min.x || vv>=datar.max.x) { + w += k[v]; /* assume white surround */ + continue; + } + w += k[v]*tt[vv-datar.min.x]; + } + if(w < black || black==white) + w = 0.0; + else if(w > white) + w = 1.0; + else + w = (w-black)/(white-black); + sdata[i*48+j] = state.gtab[(int)(1000.0*w)]; + } + } + + /* dither to lower depth before copying into GREY8 version */ + if(small->chan != GREY8) { + u = 0; + dp = small->depth; + for(i=0; i<48; i++) { + sm = 0xFF ^ (0xFF>>dp); + sh = 0; + v = 0; + for(j=0; j<48; j++) { + ii = 48*i+j; + sd = clamp[sdata[ii]+256]; + sv = sd&sm; + v |= sv>>sh; + sh += dp; + if(sh == 8) { + bdata[u++] = v; + v = 0; + sh = 0; + } + + /* propagate error, with decay (sum errors < 1) */ + error = sd - sv; + if(ii+49 < 48*48) { /* one test is enough, really */ + sdata[ii+1] = sdata[ii+1]+((3*error)>>4); + sdata[ii+48] = sdata[ii+48]+((3*error)>>4); + sdata[ii+49] = sdata[ii+49]+((3*error)>>3); + } + + /* produce correct color map value by copying bits */ + switch(dp){ + case 1: + sv |= sv>>1; + case 2: + sv |= sv>>2; + case 4: + sv |= sv>>4; + } + sdata[ii] = sv; + } + } + for(i=0; i<nelem(bdata); i++) + bdata[i] = sdata[i]; + if(loadimage(tmp8, tmp8->r, bdata, sizeof bdata) != sizeof bdata) + sysfatal("loadimage: %r"); + draw(small, small->r, tmp8, nil, tmp8->r.min); + } else { + for(i=0; i<nelem(bdata); i++) + bdata[i] = sdata[i]; + if(loadimage(small, small->r, bdata, sizeof bdata) != sizeof bdata) + sysfatal("loadimage: %r"); + } + + free(k); + for(i=0; i<48; i++) + free(tmp[i]); +} + +void +initval2cmap(void) +{ + int i; + + for(i=0; i<256; i++) + val2cmap[i] = rgb2cmap(i, i, i); +} + +void +setgtab(State *s) +{ + int i; + + for(i=0; i<=1000; i++) + s->gtab[i] = val2cmap[(int)(255.0*pow((i/1000.0), 1.0/s->gamma))]; +} + +int +section(int x) +{ + int ib, iw; + + ib = state.black * 255.0; + iw = state.white * 255.0; + + if(x<ib-5 || iw+5<x) + return -1; + + iw -= ib; + x -= ib; + if(x < iw/3) + return 0; + if(x < 2*iw/3) + return 1; + return 2; +} + +Image* +copyimage(Image *i) +{ + Image *n; + + if(i == nil) + return nil; + + n = allocimage(display, i->r, i->chan, 0, DNofill); + if(n == nil) + sysfatal("allocimage: %r"); + + draw(n, n->r, i, nil, i->r.min); + return n; +} + +Image* +grey8image(Image *i) +{ + Image *n; + + if(i->chan == GREY8) + return i; + + n = allocimage(display, i->r, GREY8, 0, DNofill); + if(n == nil) + sysfatal("allocimage: %r"); + + draw(n, n->r, i, nil, i->r.min); + freeimage(i); + return n; +} + + +void +mark(void) +{ + if(osmall != small){ + freeimage(osmall); + osmall = small; + } + ostate = state; +} + +void +undo(void) +{ + if(small != osmall){ + freeimage(small); + small = osmall; + } + state = ostate; + process(rdata, orig->r, state.selr, small); + drawface(-1); + drawscreen(0); +} + +void +saveface(Face *f, int slot) +{ + if(slot == -1){ + mark(); + state = f->state; + small = copyimage(f->small); + drawface(-1); + drawscreen(0); + return; + } + + if(face[slot]==nil) + face[slot] = emalloc(sizeof(*face[slot])); + else{ + freeimage(face[slot]->small); + face[slot]->small = nil; + } + + if(f == nil){ + face[slot]->small = copyimage(small); + face[slot]->state = state; + }else{ + face[slot]->small = copyimage(f->small); + face[slot]->state = f->state; + } + drawface(slot); +} + +int +writeface(char *outfile, Image *image) +{ + int i, fd, rv, y; + uchar data[48*48/2]; + + if(outfile == nil) + fd = 1; + else{ + if((fd = create(outfile, OWRITE, 0666)) < 0) + return -1; + } + + switch(image->chan) { + default: + rv = -1; + break; + + case GREY1: + if(unloadimage(image, image->r, data, 48*48/8) != 48*48/8) + sysfatal("unloadimage: %r"); + for(y=0; y<48; y++) { + for(i=0; i<3; i++) + fprint(fd, "0x%.2x%.2x,", data[y*6+i*2+0], data[y*6+i*2+1]); + fprint(fd, "\n"); + } + rv = 0; + break; + + case GREY2: + if(unloadimage(image, image->r, data, 48*48/4) != 48*48/4) + sysfatal("unloadimage: %r"); + for(y=0; y<48; y++) { + for(i=0; i<3; i++) + fprint(fd, "0x%.2x%.2x,%.2x%.2x,", + data[y*12+i*4+0], data[y*12+i*4+1], + data[y*12+i*4+2], data[y*12+i*4+3]); + fprint(fd, "\n"); + } + rv = 0; + break; + + case GREY4: + case GREY8: + rv = writeimage(fd, image, 0); /* dolock? */ + break; + } + + if(outfile) + close(fd); + return rv; +} + +void +room(Rectangle out, Rectangle in, int *a) +{ + a[Left] = out.min.x - in.min.x; + a[Right] = out.max.x - in.max.x; + a[Top] = out.min.y - in.min.y; + a[Bottom] = out.max.y - in.max.y; +} + +int +min(int a, int b) +{ + if(a < b) + return a; + return b; +} + +int +max(int a, int b) +{ + if(a > b) + return a; + return b; +} + +int +move(Rectangle r, Rectangle picr, Point d, int k, Rectangle *rp) +{ + int a[4], i; + Rectangle oldr; + static int toggle; + + oldr = r; + room(picr, r, a); + switch(k){ + case RTopLeft: + i = (d.x+d.y)/2; + if(i>=Dx(r) || i>=Dy(r)) + break; + i = max(i, a[Left]); + i = max(i, a[Top]); + r.min.x += i; + r.min.y += i; + break; + case RTop: + i = d.y; + if(i < 0){ + /* + * should really check i/2, but this is safe and feedback + * makes the control feel right + */ + i = -min(-i, a[Right]); + i = max(i, a[Left]); + } + i = max(i, a[Top]); + if(i >= Dy(r)) + break; + r.min.y += i; + /* divide the half bit equally */ + toggle = 1-toggle; + if(toggle){ + r.min.x += i/2; + r.max.x = r.min.x+Dy(r); + }else{ + r.max.x -= i/2; + r.min.x = r.max.x-Dy(r); + } + break; + case RTopRight: + i = (-d.x+d.y)/2; + if(i>=Dx(r) || i>=Dy(r)) + break; + i = -min(-i, a[Right]); + i = max(i, a[Top]); + r.max.x -= i; + r.min.y += i; + break; + case RLeft: + i = d.x; + if(i < 0){ + i = -min(-i, a[Bottom]); + i = max(i, a[Top]); + } + i = max(i, a[Left]); + if(i >= Dx(r)) + break; + r.min.x += i; + /* divide the half bit equally */ + toggle = 1-toggle; + if(toggle){ + r.min.y += i/2; + r.max.y = r.min.y+Dx(r); + }else{ + r.max.y -= i/2; + r.min.y = r.max.y-Dx(r); + } + break; + case RMiddle: + if(d.x >= 0) + d.x = min(d.x, a[Right]); + else + d.x = max(d.x, a[Left]); + if(d.y >= 0) + d.y = min(d.y, a[Bottom]); + else + d.y = max(d.y, a[Top]); + r = rectaddpt(r, d); + break; + case RRight: + i = d.x; + if(i > 0){ + i = min(i, a[Bottom]); + i = -max(-i, a[Top]); + } + i = min(i, a[Right]); + if(-i >= Dx(r)) + break; + r.max.x += i; + /* divide the half bit equally */ + toggle = 1-toggle; + if(toggle){ + r.min.y -= i/2; + r.max.y = r.min.y+Dx(r); + }else{ + r.max.y += i/2; + r.min.y = r.max.y-Dx(r); + } + break; + case RBotLeft: + i = (d.x+-d.y)/2; + if(i>=Dx(r) || i>=Dy(r)) + break; + i = max(i, a[Left]); + i = -min(-i, a[Bottom]); + r.min.x += i; + r.max.y -= i; + break; + case RBot: + i = d.y; + if(i > 0){ + i = min(i, a[Right]); + i = -max(-i, a[Left]); + } + i = min(i, a[Bottom]); + if(i >= Dy(r)) + break; + r.max.y += i; + /* divide the half bit equally */ + toggle = 1-toggle; + if(toggle){ + r.min.x -= i/2; + r.max.x = r.min.x+Dy(r); + }else{ + r.max.x += i/2; + r.min.x = r.max.x-Dy(r); + } + break; + case RBotRight: + i = (-d.x+-d.y)/2; + if(i>=Dx(r) || i>=Dy(r)) + break; + i = -min(-i, a[Right]); + i = -min(-i, a[Bottom]); + r.max.x -= i; + r.max.y -= i; + break; + } + if(Dx(r)<3 || Dy(r)<3){ + *rp = oldr; + return 0; + } + *rp = r; + return !eqrect(r, oldr); +} + +void +rlist(Rectangle r, Rectangle *ra) +{ + Rectangle tr; + + tr = r; + tr.max.y = r.min.y+Dy(r)/4; + ra[0] = tr; + ra[0].max.x = tr.min.x+Dx(tr)/4; + ra[1] = tr; + ra[1].min.x = ra[0].max.x; + ra[1].max.x = tr.max.x-Dx(tr)/4; + ra[2] = tr; + ra[2].min.x = ra[1].max.x; + + tr.min.y = tr.max.y; + tr.max.y = r.max.y-Dy(r)/4; + ra[3] = tr; + ra[3].max.x = tr.min.x+Dx(tr)/4; + ra[4] = tr; + ra[4].min.x = ra[3].max.x; + ra[4].max.x = tr.max.x-Dx(tr)/4; + ra[5] = tr; + ra[5].min.x = ra[4].max.x; + + tr.min.y = tr.max.y; + tr.max.y = r.max.y; + ra[6] = tr; + ra[6].max.x = tr.min.x+Dx(tr)/4; + ra[7] = tr; + ra[7].min.x = ra[6].max.x; + ra[7].max.x = tr.max.x-Dx(tr)/4; + ra[8] = tr; + ra[8].min.x = ra[7].max.x; +} + +int +abs(int a) +{ + if(a < 0) + return -a; + return a; +} + +void +usage(void) +{ + fprint(2, "usage: mug [file.bit]\n"); + exits("usage"); +} + +void +eresized(int new) +{ + if(new && getwindow(display, Refmesg) < 0) + fprint(2,"can't reattach to window"); + drawscreen(1); + +} + +/* +interface notes + +cursor changes while in rbig to indicate region. +only button 1 works for resizing region +only button 1 works for moving thingy in ramp + +button-3 menu: Reset, Depth, Undo, Save, Write +*/ + +Cursor tl = { + {-4, -4}, + {0xfe, 0x00, 0x82, 0x00, 0x8c, 0x00, 0x87, 0xff, + 0xa0, 0x01, 0xb0, 0x01, 0xd0, 0x01, 0x11, 0xff, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x1f, 0x00, }, + {0x00, 0x00, 0x7c, 0x00, 0x70, 0x00, 0x78, 0x00, + 0x5f, 0xfe, 0x4f, 0xfe, 0x0f, 0xfe, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, 0x00, } +}; + +Cursor t = { + {-7, -8}, + {0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x06, 0xc0, + 0x1c, 0x70, 0x10, 0x10, 0x0c, 0x60, 0xfc, 0x7f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, 0x03, 0x80, + 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } +}; + +Cursor tr = { + {-11, -4}, + {0x00, 0x7f, 0x00, 0x41, 0x00, 0x31, 0xff, 0xe1, + 0x80, 0x05, 0x80, 0x0d, 0x80, 0x0b, 0xff, 0x88, + 0x00, 0x88, 0x0, 0x88, 0x00, 0x88, 0x00, 0x88, + 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0xf8, }, + {0x00, 0x00, 0x00, 0x3e, 0x00, 0x0e, 0x00, 0x1e, + 0x7f, 0xfa, 0x7f, 0xf2, 0x7f, 0xf0, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, } +}; + +Cursor r = { + {-8, -7}, + {0x07, 0xc0, 0x04, 0x40, 0x04, 0x40, 0x04, 0x58, + 0x04, 0x68, 0x04, 0x6c, 0x04, 0x06, 0x04, 0x02, + 0x04, 0x06, 0x04, 0x6c, 0x04, 0x68, 0x04, 0x58, + 0x04, 0x40, 0x04, 0x40, 0x04, 0x40, 0x07, 0xc0, }, + {0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x90, 0x03, 0x90, 0x03, 0xf8, 0x03, 0xfc, + 0x03, 0xf8, 0x03, 0x90, 0x03, 0x90, 0x03, 0x80, + 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, } +}; + +Cursor br = { + {-11, -11}, + {0x00, 0xf8, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, + 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, + 0xff, 0x88, 0x80, 0x0b, 0x80, 0x0d, 0x80, 0x05, + 0xff, 0xe1, 0x00, 0x31, 0x00, 0x41, 0x00, 0x7f, }, + {0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x0, 0x70, 0x00, 0x70, 0x00, 0x70, 0x00, 0x70, + 0x00, 0x70, 0x7f, 0xf0, 0x7f, 0xf2, 0x7f, 0xfa, + 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x3e, 0x00, 0x00, } +}; + +Cursor b = { + {-7, -7}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0xfc, 0x7f, 0x0c, 0x60, 0x10, 0x10, 0x1c, 0x70, + 0x06, 0xc0, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, }, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, + 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x03, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } +}; + +Cursor bl = { + {-4, -11}, + {0x1f, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, + 0x11, 0xff, 0xd0, 0x01, 0xb0, 0x01, 0xa0, 0x01, + 0x87, 0xff, 0x8c, 0x00, 0x82, 0x00, 0xfe, 0x00, }, + {0x00, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x0e, 0x00, + 0x0e, 0x00, 0x0f, 0xfe, 0x4f, 0xfe, 0x5f, 0xfe, + 0x78, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x00, 0x0, } +}; + +Cursor l = { + {-7, -7}, + {0x03, 0xe0, 0x02, 0x20, 0x02, 0x20, 0x1a, 0x20, + 0x16, 0x20, 0x36, 0x20, 0x60, 0x20, 0x40, 0x20, + 0x60, 0x20, 0x36, 0x20, 0x16, 0x20, 0x1a, 0x20, + 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x03, 0xe0, }, + {0x00, 0x00, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, + 0x09, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x3f, 0xc0, + 0x1f, 0xc0, 0x09, 0xc0, 0x09, 0xc0, 0x01, 0xc0, + 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x00, 0x00, } +}; + +Cursor boxcursor = { + {-7, -7}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, + 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }, + {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, + 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00, } +}; + +Cursor clearcursor; + +Cursor *corners[10] = { + &tl, &t, &tr, + &l, &boxcursor, &r, + &bl, &b, &br, + nil, /* default arrow */ +}; + +char *item[] = { + "Reset", + "Depth", + "Undo", + "Write", + "Exit", + nil +}; + +Menu menu = { + item, + nil, + 2 +}; + +/*BUG make less flashy */ +void +moveface(Image *back, Point lastp, Image *face, Point p, Point d) +{ + draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min); + draw(back, back->r, screen, nil, addpt(back->r.min, subpt(p, d))); + border(screen, rectaddpt(face->r, subpt(p, d)), + -1, display->black, ZP); + draw(screen, rectaddpt(face->r, subpt(p, d)), + face, nil, face->r.min); +} + +int +dragface(Mouse *m, Image *im, Point d, int x) +{ + int i; + Point lastp; + static Image *back; + + if(back == nil){ + back = allocimage(display, Rect(-1,-1,49,49), display->image->chan, 0, DNofill); + if(back == nil) + sysfatal("dragface backing store: %r"); + } + + lastp = m->xy; + draw(back, back->r, screen, nil, addpt(back->r.min, subpt(lastp, d))); + esetcursor(&clearcursor); + do{ + moveface(back, lastp, im, m->xy, d); + lastp = m->xy; + }while(*m=emouse(), m->buttons==1); + + draw(screen, rectaddpt(back->r, subpt(lastp, d)), back, nil, back->r.min); + esetcursor(nil); + if(m->buttons==0){ + for(i=0; i<nelem(face); i++) + if(ptinrect(m->xy, rface[i])) + return i; + if(ptinrect(m->xy, rsmall)) + return -1; + return x; + } + while(*m=emouse(), m->buttons) + ; + return x; +} + +void +initstate(void) +{ + state.black = 0.0; + state.white = 1.0; + state.stretch = 1.0; + state.depth = 4; + state.gamma = 1.0; + setgtab(&state); + state.selr = insetrect(orig->r, 5); + sdx = Dx(state.selr); + sdy = Dy(state.selr); + if(sdx > sdy) + state.selr.max.x = state.selr.min.x+sdy; + else + state.selr.max.y = state.selr.min.y+sdx; +} + +void +main(int argc, char **argv) +{ + int ccursor, i, fd, k, n, y; + uchar *data; + double gammatab[256]; + Event e; + Mouse m; + Point lastp, p; + Rectangle nselr, rbig9[9]; + + ARGBEGIN{ + default: + usage(); + }ARGEND + + if(argc > 1) + usage(); + if(argc == 1){ + if((fd = open(argv[0], OREAD)) < 0) + sysfatal("open %s: %r", argv[0]); + }else + fd = 0; + + if (initdraw(0, 0, "mug") < 0) + sysfatal("initdraw failed"); + + if((orig = readimage(display, fd, 0)) == nil) + sysfatal("readimage: %r"); + + orig = grey8image(orig); + + initramp(); + initclamp(); + initval2cmap(); + bkgd = allocimagemix(display, DPaleyellow, DWhite); + small = allocimage(display, Rect(0,0,48,48), GREY4, 0, DWhite); + tmp8 = allocimage(display, Rect(0,0,48,48), GREY8, 0, DWhite); + red = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DRed); + green = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DGreen); + blue = allocimage(display, Rect(0,0,1,1), display->image->chan, 1, DBlue); + if(bkgd==nil || small==nil || tmp8==nil || red==nil || green==nil || blue==nil) + sysfatal("allocimage: %r"); + + n = Dx(orig->r)*Dy(orig->r); + data = emalloc(n*sizeof data[0]); + rdata = emalloc(n*sizeof rdata[0]); + + if(unloadimage(orig, orig->r, data, n) != n) + sysfatal("unloadimage: %r"); + + for(i=0; i<256; i++) + gammatab[i] = pow((255-i)/(double)255.0, GAMMA); + + for(i=0; i<n; i++) + rdata[i] = gammatab[255-data[i]]; + + initstate(); + process(rdata, orig->r, state.selr, small); + drawscreen(1); + flushimage(display, 1); + einit(Emouse|Ekeyboard); + ccursor = 9; + for(;;){ + if((n=eread(Emouse|Ekeyboard, &e))==Ekeyboard) + continue; + if(n != Emouse) + break; + + m = e.mouse; + if(m.buttons&4){ + ccursor = 9; + esetcursor(corners[ccursor]); + switch(emenuhit(3, &m, &menu)){ + case -1: + continue; + case 0: /* Reset */ + mark(); + initstate(); + small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite); + if(small == nil) + sysfatal("allocimage: %r"); + process(rdata, orig->r, state.selr, small); + drawface(-1); + drawscreen(0); + break; + case 1: /* Depth */ + mark(); + /* osmall = small, so no freeimage */ + state.depth /= 2; + if(state.depth == 0) + state.depth = 8; + small = allocimage(display, Rect(0,0,48,48), CHAN1(CGrey, state.depth), 0, DWhite); + if(small == nil) + sysfatal("allocimage: %r"); + process(rdata, orig->r, state.selr, small); + drawface(-1); + break; + case 2: /* Undo */ + undo(); + break; + case 3: /* Write */ + writeface(nil, small); + break; + case 4: /* Exit */ + exits(nil); + break; + } + } + + if(ptinrect(m.xy, rbig)){ + rlist(rectaddpt(state.selr, subpt(rbig.min, orig->r.min)), rbig9); + for(i=0; i<9; i++) + if(ptinrect(m.xy, rbig9[i])) + break; + if(i != ccursor){ + ccursor = i; + esetcursor(corners[ccursor]); + } + if(i==9) + continue; + + if(m.buttons & 1){ + mark(); + lastp = m.xy; + while(m=emouse(), m.buttons&1){ + if(move(state.selr, orig->r, subpt(m.xy, lastp), i, &nselr)){ + moveframe(state.selr, nselr); + state.selr = nselr; + lastp = m.xy; + process(rdata, orig->r, state.selr, small); + drawface(-1); + } + } + } + continue; + } + + if(ccursor != 9){ /* default cursor */ + ccursor = 9; + esetcursor(corners[ccursor]); + } + + if(ptinrect(m.xy, rramp)){ + if(m.buttons != 1) + continue; + mark(); + y = gamma2y(state.gamma); + if(abs(y-(m.xy.y-rramp.min.y)) > 5) + continue; + k = section(m.xy.x-rramp.min.x); + drawrampbar(green, &state); + lastp = m.xy; + while(m=emouse(), m.buttons&1){ + if(!ptinrect(m.xy, rramp)) + continue; + switch(k){ + case -1: + continue; + case 0: + if((m.xy.x-rramp.min.x)/255.0 < state.white){ + state.black = (m.xy.x-rramp.min.x)/255.0; + break; + } + continue; + case 1: + state.gamma = y2gamma(m.xy.y-rramp.min.y); + setgtab(&state); + break; + case 2: + if((m.xy.x-rramp.min.x)/255.0 > state.black){ + state.white = (m.xy.x-rramp.min.x)/255.0; + break; + } + continue; + case 10: + state.black += (m.xy.x-lastp.x)/255.0; + state.white += (m.xy.x-lastp.x)/255.0; + state.gamma = y2gamma(p.y); + break; + } + process(rdata, orig->r, state.selr, small); + drawface(-1); + drawrampbar(green, &state); + } + if(m.buttons == 0){ + process(rdata, orig->r, state.selr, small); + drawface(-1); + drawrampbar(red, &state); + }else + undo(); + continue; + } + + if(ptinrect(m.xy, rsmall)){ + if(m.buttons != 1) + continue; + n=dragface(&m, small, subpt(m.xy, rsmall.min), -1); + if(n == -1) + continue; + saveface(nil, n); + } + + for(i=0; i<nelem(face); i++) + if(ptinrect(m.xy, rface[i])) + break; + if(i<nelem(face) && face[i] != nil){ + if(m.buttons != 1) + continue; + n=dragface(&m, face[i]->small, subpt(m.xy, rface[i].min), i); + if(n == i) + continue; + saveface(face[i], n); + continue; + } + + do + m = emouse(); + while(m.buttons==1); + } + exits(nil); +} |