📄 mug.c
字号:
#include <u.h>#include <libc.h>#include <draw.h>#include <event.h>#include <cursor.h>#define initstate muginitstatetypedef 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\n", 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;voidgeometry(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];}doubley2gamma(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 */}intgamma2y(double g){ g -= 0.5; return (int)(128.0*sqrt(g)+0.5);}voiddrawface(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);}voiddrawrampbar(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);}voiddrawscreen(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); }}voidmoveframe(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. */voidinitramp(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");}voidinitclamp(void){ int i; for(i=0; i<256; i++) { clamp[i] = 0; clamp[256+i] = i; clamp[512+i] = 255; }}voidchangestretch(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. */voidprocess(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]);}voidinitval2cmap(void){ int i; for(i=0; i<256; i++) val2cmap[i] = rgb2cmap(i, i, i);}voidsetgtab(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))];}intsection(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;}voidmark(void){ if(osmall != small){ freeimage(osmall); osmall = small; } ostate = state;}voidundo(void){ if(small != osmall){ freeimage(small); small = osmall; } state = ostate; process(rdata, orig->r, state.selr, small); drawface(-1); drawscreen(0);}voidsaveface(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);}intwriteface(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;}voidroom(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;}intmin(int a, int b){ if(a < b) return a; return b;}intmax(int a, int b){ if(a > b) return a; return b;}intmove(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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -