icrop—interactive image cropping Reference: /n/atom/patch/applied/icrop Date: Wed Jan 29 02:02:38 CET 2014 Signed-off-by: quanstro@quanstro.net --- /sys/man/1/crop Wed Jan 29 02:02:10 2014 +++ /sys/man/1/crop Wed Jan 29 02:02:11 2014 @@ -1,6 +1,6 @@ .TH CROP 1 .SH NAME -crop, iconv \- frame, crop, and convert image +crop, icrop, iconv \- frame, crop, and convert image .SH SYNOPSIS .B crop [ @@ -60,6 +60,15 @@ .I file ] .PP +.B icrop +[ +.B -n +] [ +.B -r +.I x +.I y +] +.PP .B iconv [ .B -u @@ -130,6 +139,20 @@ option is given a negative argument. This can be used to draw a monochrome frame around the image. The default color is black. +.PP +.I Icrop +also reads an image from standard input, and produces a cropped image +on standard output, but the cropping is interactive. A new window +may be opened with the +.B -n +flag. An initial crop region size ise specified with +.BR -r . +The region may be manipulated like an +.IR rio (1) +window. B3 allows for +region creation. B1 allows the region to be extended. B2 cancels the +current crop. Entering ‘w’ writes the current cropped image, translated +to the orgin and quits while ‘q’ quits without writing the image. .PP .I Iconv changes the format of pixels in the image --- /sys/src/cmd/icrop/bpoly.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/icrop/bpoly.c Wed Jan 29 02:02:12 2014 @@ -0,0 +1,21 @@ +static void +bpoly(Rectangle r) +{ + Point p[5]; + + if(Dx(r) < 2*W) + r.max.x = r.min.x+2*W; + if(Dy(r) < 2*W) + r.max.y = r.min.y+2*W; + p[0] = r.min; + p[1] = (Point){r.max.x, r.min.y}; + p[2] = r.max; + p[3] = (Point){r.min.x, r.max.y}; + p[4] = r.min; + +// poly(screen, p, 5, Endsquare, Endsquare, W, red, ZP); + USED(p); + + drawimggetrect(grimgs, r, 1); +} + --- /sys/src/cmd/icrop/cursor.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/icrop/cursor.c Wed Jan 29 02:02:12 2014 @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include + +static 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, } +}; + +static 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, } +}; + +static 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, } +}; + +static 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, } +}; + +static 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, } +}; + +static 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, } +}; + +static 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, } +}; + +static 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, } +}; + +static Cursor *corners[9] = { + &tl, &t, &tr, + &l, nil, &r, + &bl, &b, &br, +}; + +static Cursor dummyc; + +int +cornernum(Cursor *cursor) +{ + int i; + + if(cursor == nil) + return -1; + for(i = 0; i < 9; i++) + if(cursor == corners[i]) + return i; + return -1; +} + +static int +tri(int pt, int min, int max) +{ + if(pt > max-10) + return 2; + if(pt < min+10) + return 0; + return 1; +} + +int +findcorner(Cursor **cc, Rectangle cropr, Point p) +{ + int in, sect; + Cursor *c; + + c = &dummyc; + in = ptinrect(p, insetrect(cropr, W)); + if(!eqrect(cropr, ZR) && ptinrect(p, cropr) && !in){ + sect = tri(p.x, cropr.min.x, cropr.max.x); + sect += 3*tri(p.y, cropr.min.y, cropr.max.y); + c = corners[sect]; + }else if(*cc != nil) + c = nil; + *cc = c; + if(c != &dummyc) + return 0; + return -1; +} + --- /sys/src/cmd/icrop/icrop.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/icrop/icrop.c Wed Jan 29 02:02:13 2014 @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int mainstacksize = Stack; +int displayr; +Image *image; +Rectangle cropr; +Cursor *cursor; +char msgbuf[3][100]; +Image *grimgs[5]; + +void +init(void) +{ +} + +int +nontrivrect(Rectangle r) +{ + return Dx(r) > 5 && Dy(r) > 5; +} + +void +dbgmsgs(Image *screen) +{ + char *s; + int i; + Point p; + + if(displayr && !eqrect(cropr, ZR)) + snprint(msgbuf[0], sizeof msgbuf[0], "%R", cropr); + else + msgbuf[0][0] = 0; + + for(i = 0; i < nelem(msgbuf); i++){ + s = msgbuf[i]; + if(s[0] != 0){ + p = addpt(screen->r.min, (Point){50,50+font->height*i}); + stringbg(screen, p, display->black, ZP, font, s, display->white, ZP); + } + } + flushimage(display, 1); +} + +void +decorate(Image *screen) +{ + if(nontrivrect(cropr)) + drawimggetrect(grimgs, cropr, 1); + dbgmsgs(screen); +} + +void +redraw(Image *screen) +{ + draw(screen, screen->r, image, nil, ZP); + decorate(screen); +} + +void +eresized(int new) +{ + if(new && getwindow(display, Refnone) < 0) + fprint(2,"can't reattach to window"); + redraw(screen); +} + +void +writecrop(int fd, Rectangle r) +{ + Image *i; + Rectangle δ; + Point p; + + if(eqrect(r, ZR)) + writeimage(fd, image, 0); + else{ + δ.min = ZP; + δ.max = subpt(r.max, r.min); + p = subpt(r.min, screen->r.min); + i = allocimage(display, δ, image->chan, 0, -1); + draw(i, δ, image, nil, p); + writeimage(fd, i, 0); + freeimage(i); + } +} + +void +mouseproc(void*) +{ + int i; + Rune r; + Rectangle zr; + Keyboardctl *k; + Mousectl *m; + Mouse mc; + enum{Amouse, Akbd, Aresize, Nalt}; + Alt alts[Nalt+1]; + + m = initmouse(nil, display->image); + k = initkeyboard(nil); + for(i = 0; i <= nelem(alts); i++){ + alts[i].c = 0; + alts[i].v = 0; + alts[i].op = CHANNOP; + } + alts[Amouse].c = m->c; + alts[Amouse].v = &mc; + alts[Amouse].op = CHANRCV; + alts[Akbd].c = k->c; + alts[Akbd].v = &r; + alts[Akbd].op = CHANRCV; + alts[Aresize].c = m->resizec; + alts[Aresize].v = nil; + alts[Aresize].op = CHANRCV; + alts[Nalt].op = CHANEND; + while((i = alt(alts)) >= 0){ + switch(i){ + case Amouse: + if(m->buttons == 0){ + if(findcorner(&cursor, cropr, m->xy) == 0) + setcursor(m, cursor); + dbgmsgs(screen); + continue; + } + if((m->buttons&7) == 1 && (i = cornernum(cursor)) != -1){ + cropr = getrectxy(1, cropr, (i%3) - 1, i/3 - 1, m); + if(!nontrivrect(cropr)) + cropr = ZR; + eresized(0); + }else if((m->buttons&7) == 1 && ptinrect(m->xy, cropr)){ + cropr = getrecttrans(1, cropr, m); + eresized(0); + } + if((m->buttons&7) == 2){ + cropr = ZR; + eresized(0); + } + if((m->buttons&7) == 4){ + cropr = getrectxy(3, ZR, 1, 1, m); + if(!nontrivrect(cropr)) + cropr = ZR; + eresized(0); + } + break; + case Akbd: + switch(r){ + case 'r': + displayr ^= 1; + break; + case 'p': + zr = rectsubpt(cropr, screen->r.min); + print("crop %d %d %d %d\n", zr.min.x, zr.min.y, + zr.max.x, zr.max.y); + case 'q': + case 0x7f: + threadexitsall(""); + break; + case 'w': + writecrop(1, cropr); + threadexitsall(""); + break; + } + break; + case Aresize: + eresized(1); + break; + } + } + threadexits(""); +} + +void +loadinput(int fd) +{ + char buf[ERRMAX]; + + image = readimage(display, fd, 0); + if(image == nil){ + rerrstr(buf, sizeof buf); + if(buf[0] != 0) + sysfatal("readimage: %r"); + threadexitsall(""); + } +} + +void +usage(void) +{ + fprint(2, "usage: icrop [-n] [-r x y]\n"); + threadexitsall("usage"); +} + +void +threadmain(int argc, char **argv) +{ + int flagn; + Point p; + + flagn = 0; + ARGBEGIN{ + case 'n': + flagn = 1; + break; + case 'r': + p.x = atoi(EARGF(usage())); + p.y = atoi(EARGF(usage())); + cropr = Rect(0, 0, p.x, p.y); + break; + default: + usage(); + }ARGEND + + if(flagn) + newwindow(nil); + if(initdraw(nil, nil, "icrop") < 0){ + fprint(2, "icrop: initdraw failed: %r\n"); + threadexitsall("initdraw"); + } + init(); + loadinput(0); + resize(-1, Dx(image->r), Dy(image->r)); + if(!eqrect(cropr, ZR)) + cropr = rectaddpt(cropr, screen->r.min); + eresized(0); + mouseproc(nil); +} --- /sys/src/cmd/icrop/icrop.h Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/icrop/icrop.h Wed Jan 29 02:02:14 2014 @@ -0,0 +1,14 @@ +void resize(int, int, int); + +void drawimggetrect(Image**, Rectangle, int); +Rectangle getrecttrans(int, Rectangle, Mousectl*); +Rectangle getrectxy(int, Rectangle, int, int, Mousectl*); +int findcorner(Cursor**, Rectangle, Point); +int cornernum(Cursor*); + +enum { + W = Borderwidth, + Stack = 32*1024, +}; + +extern Image *grimgs[]; --- /sys/src/cmd/icrop/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/icrop/mkfile Wed Jan 29 02:02:14 2014 @@ -0,0 +1,14 @@ + +#include +#include + +static int +max(int a, int b) +{ + if(a > b) + return a; + return b; +} + +Rectangle +screenrect(void) +{ + char buf[12*5]; + int fd; + + fd = open("/dev/screen", OREAD); + if(fd == -1) + fd = open("/mnt/term/dev/screen", OREAD); + if(fd == -1) + sysfatal("window read"); + if(read(fd, buf, sizeof buf) != sizeof buf) + sysfatal("can't read /dev/screen: %r\n"); + close(fd); + return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48)); +} + +void +resize(int fd, int dx, int dy) +{ + int opened; + Rectangle sr, r, or; + + dx += 2*Borderwidth; + dy += 2*Borderwidth; + opened = fd < 0; + if(fd < 0) + fd = open("/dev/wctl", OWRITE); + if(fd < 0) + return; + + r = insetrect(screen->r, -Borderwidth); + if(Dx(r) >= dx && Dy(r) >= dy) + goto done; + + sr = screenrect(); + or = r; + + r.max.x = max(r.min.x+dx, r.max.x); + r.max.y = max(r.min.y+dy, r.max.y); + if(r.max.x > sr.max.x){ + if(Dx(r) > Dx(sr)){ + r.min.x = 0; + r.max.x = sr.max.x; + }else + r = rectaddpt(r, Pt(sr.max.x-r.max.x, 0)); + } + if(r.max.y > sr.max.y){ + if(Dy(r) > Dy(sr)){ + r.min.y = 0; + r.max.y = sr.max.y; + }else + r = rectaddpt(r, Pt(0, sr.max.y-r.max.y)); + } + + /* + * Sometimes we can't actually grow the window big enough, + * and resizing it to the same shape makes it flash. + */ + if(Dx(r) == Dx(or) && Dy(r) == Dy(or)) + goto done; + fprint(fd, "resize -minx %d -miny %d -maxx %d -maxy %d\n", + r.min.x, r.min.y, r.max.x, r.max.y); +done: + if(opened) + close(fd); +} + --- /sys/src/cmd/icrop/select.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/icrop/select.c Wed Jan 29 02:02:15 2014 @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include + +static Cursor sweep={ + {-7, -7}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x07, + 0xE0, 0x07, 0xE0, 0x07, 0xE3, 0xF7, 0xE3, 0xF7, + 0xE3, 0xE7, 0xE3, 0xF7, 0xE3, 0xFF, 0xE3, 0x7F, + 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,}, + {0x00, 0x00, 0x7F, 0xFE, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x41, 0xE2, + 0x41, 0xC2, 0x41, 0xE2, 0x41, 0x72, 0x40, 0x38, + 0x40, 0x1C, 0x40, 0x0E, 0x7F, 0xE6, 0x00, 0x00,} +}; + +static Cursor bullseye={ + {-7, -7}, + {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, + 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, + 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, + }, + {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, + 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, + 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, + 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, + } +}; + +static int +max(int a, int b) +{ + if(a > b) + return a; + return b; +} + +static uint +αshift(uint color, int bits) +{ + uint i, c, d, m; + + d = 0; + for(i = 0; i <4; i++){ + m = 0xff<0) + c <<= bits; + else + c >>= -bits; + d |= c & m; + } + return d; +} + +static void +getrectalloc(Image **tmp) +{ + int i; + Rectangle r, rc; + + rc = Rect(0, 0, 3000, 3000); + r = Rect(0, 0, max(Dx(display->screenimage->r), Dx(rc)), W); + tmp[0] = allocimage(display, r, screen->chan, 0, -1); + tmp[1] = allocimage(display, r, screen->chan, 0, -1); + r = Rect(0, 0, W, max(Dy(display->screenimage->r), Dy(rc))); + tmp[2] = allocimage(display, r, screen->chan, 0, -1); + tmp[3] = allocimage(display, r, screen->chan, 0, -1); + tmp[4] = allocimage(display, Rect(0,0,1,1), RGBA32, 1, αshift(DPalebluegreen, -1)); + for(i = 0; i < 5; i++) + if(tmp[i] == nil) + sysfatal("getrectalloc: allocimage failed"); +} + +static void +brects(Rectangle r, Rectangle rp[4]) +{ + if(Dx(r) < 2*W) + r.max.x = r.min.x+2*W; + if(Dy(r) < 2*W) + r.max.y = r.min.y+2*W; + rp[0] = Rect(r.min.x, r.min.y, r.max.x, r.min.y+W); + rp[1] = Rect(r.min.x, r.max.y-W, r.max.x, r.max.y); + rp[2] = Rect(r.min.x, r.min.y+W, r.min.x+W, r.max.y-W); + rp[3] = Rect(r.max.x-W, r.min.y+W, r.max.x, r.max.y-W); +} + +void +drawimggetrect(Image **tmp, Rectangle rc, int up) +{ + int i; + Rectangle rects[4]; + + if(tmp[0] == 0) + getrectalloc(tmp); + brects(rc, rects); + if(!up){ + for(i=0; i<4; i++) + draw(screen, rects[i], tmp[i], nil, ZP); + return; + } + for(i=0; i<4; i++){ + draw(tmp[i], Rect(0, 0, Dx(rects[i]), Dy(rects[i])), screen, nil, rects[i].min); + draw(screen, rects[i], tmp[4], nil, ZP); /* red */ + } +} + +Rectangle +getrectxy(int but, Rectangle r0, int xfree, int yfree, Mousectl *mc) +{ + int extend; + Rectangle r, rc; + + extend = !eqrect(r0, ZR); + but = 1<<(but-1); + setcursor(mc, &sweep); + if(!extend){ + while(mc->buttons) + readmouse(mc); + while(!(mc->buttons & but)){ + readmouse(mc); + if(mc->buttons & (7^but)) + goto Return; + } + r.min = mc->xy; + r.max = mc->xy; + }else{ + r = r0; + drawimggetrect(grimgs, r0, 0); + flushimage(display, 1); + } + + do{ + rc = canonrect(r); + drawimggetrect(grimgs, rc, 1); + readmouse(mc); + drawimggetrect(grimgs, rc, 0); + + if(xfree == 1) + r.max.x = mc->xy.x; + if(xfree == -1) + r.min.x = mc->xy.x; + if(yfree == 1) + r.max.y = mc->xy.y; + if(yfree == -1) + r.min.y = mc->xy.y; + }while(mc->buttons == but); + + Return: + setcursor(mc, nil); + if(mc->buttons & (7^but)){ + rc = ZR; + while(mc->buttons) + readmouse(mc); + } + return rc; +} + +Rectangle +getrecttrans(int but, Rectangle r0, Mousectl *mc) +{ + Point zp; + Rectangle r, rc; + + but = 1<<(but-1); + setcursor(mc, &bullseye); /* fixme? */ + r = r0; + zp = mc->xy; + + drawimggetrect(grimgs, r, 0); + do{ + rc = canonrect(r); + drawimggetrect(grimgs, rc, 1); + readmouse(mc); + drawimggetrect(grimgs, rc, 0); + r = rectaddpt(r0, subpt(mc->xy, zp)); + }while(mc->buttons == but); + + setcursor(mc, nil); + if(mc->buttons & (7^but)){ + rc = ZR; + while(mc->buttons) + readmouse(mc); + } + return rc; +} +