diff bd1305a0b9841d65f95b4d81fa0d29067425833a uncommitted --- /dev/null +++ b/sys/man/1/vcrop @@ -1,0 +1,28 @@ +.TH VCROP 1 +.SH NAME +vcrop \- crop image visually +.SH SYNOPSIS +.B vcrop +[ +.I file +] +.SH DESCRIPTION +.I Vcrop +displays an image read either from +.B file +or from +.B standard input +and allows to crop it interactively. +.PP +Button 1 pans the image within the window +.PP +Button 2 triggers the cropping area selection. Area can then be selected by sweeping Button 1. +.PP +Button 3 brings up a menu of actions. +.SH SOURCE +/sys/src/cmd/vcrop.c +.SH "SEE ALSO" +.IR crop (1) +.SH HISTORY +vcrop first appeared in 9front (May, 2024). + --- /dev/null +++ b/sys/src/cmd/vcrop.c @@ -1,0 +1,212 @@ +#include +#include +#include +#include +#include +#include + +enum +{ + Threshold = 5, +}; + +enum +{ + Emouse, + Eresize, + Ekeyboard, +}; + +Mousectl *mctl; +Keyboardctl *kctl; +Image *bg; +Image *p; +Image *n; +Point pos, oldpos; +enum { Mcrop, Mundo, Msave, Mexit }; +char *menustr[] = { "crop", "undo", "save", "exit", 0 }; +Menu menu = { menustr }; + +void +redraw(void) +{ + draw(screen, screen->r, bg, nil, ZP); + draw(screen, rectaddpt(n->r, addpt(pos, screen->r.min)), n, nil, n->r.min); + flushimage(display, 1); +} + +void +translate(Point d) +{ + Rectangle r, nr; + + if(d.x==0 && d.y==0) + return; + r = rectaddpt(n->r, addpt(pos, screen->r.min)); + pos = addpt(pos, d); + nr = rectaddpt(r, d); + draw(screen, screen->r, bg, nil, ZP); + draw(screen, nr, n, nil, n->r.min); + flushimage(display, 1); +} + +void +crop(void) +{ + Rectangle r; + Point o; + Image *i; + + r = getrect(1, mctl); + if(eqrect(r, ZR) || badrect(r) || (Dx(r)r.min); + o = addpt(n->r.min, o); + o = subpt(o, addpt(n->r.min, pos)); + /* clamp rect to image bounds */ + if(o.x < n->r.min.x) o.x = n->r.min.x; + if(o.y < n->r.min.y) o.y = n->r.min.y; + if((o.x + Dx(r)) > n->r.max.x) r.max.x = r.min.x + n->r.max.x - o.x; + if((o.y + Dy(r)) > n->r.max.y) r.max.y = r.min.y + n->r.max.y - o.y; + i = allocimage(display, r, n->chan, 0, DTransparent); + if(i==nil) + sysfatal("allocimage: %r"); + draw(i, i->r, n, nil, o); + if(p) + freeimage(p); + p = n; + n = i; + oldpos = pos; + pos = subpt(ZP, n->r.min); + redraw(); +} + +void +save(void) +{ + char buf[4096] = {0}; + int i, fd; + + i = enter("Save as:", buf, sizeof buf, mctl, kctl, nil); + if(i<=0) + return; + fd = create(buf, OWRITE, 0644); + if(fd<0) + sysfatal("create: %r"); + i = writeimage(fd, n, 0); + if(i<0) + sysfatal("writeimage: %r"); + close(fd); +} + +void +undo(void) +{ + if(p==nil) + return; + freeimage(n); + n = p; + p = nil; + pos = oldpos; + redraw(); +} + +void +menu3hit(void) +{ + int i; + + i = menuhit(3, mctl, &menu, nil); + switch(i){ + case Mcrop: + crop(); + break; + case Mundo: + undo(); + break; + case Msave: + save(); + break; + case Mexit: + threadexitsall(nil); + } +} + +void +usage(char *n) +{ + fprint(2, "usage: %s <[image]>\n", n); + exits("usage"); +} + +void +threadmain(int argc, char *argv[]) +{ + Mouse m; + Rune k; + Point o; + int fd; + Alt a[] = { + { nil, &m, CHANRCV }, + { nil, nil, CHANRCV }, + { nil, &k, CHANRCV }, + { nil, nil, CHANEND }, + }; + + if(argc > 2) + usage(argv[0]); + fd = 0; + if(argc==2){ + fd = open(argv[1], OREAD); + if(fd<0) + sysfatal("open: %r"); + } + if(initdraw(nil, nil, "vcrop")<0) + sysfatal("initdraw: %r"); + display->locking = 0; + if((mctl = initmouse(nil, screen)) == nil) + sysfatal("initmouse: %r"); + if((kctl = initkeyboard(nil)) == nil) + sysfatal("initkeyboard: %r"); + a[Emouse].c = mctl->c; + a[Eresize].c = mctl->resizec; + a[Ekeyboard].c = kctl->c; + bg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF); + n = readimage(display, fd, 0); + if(n==nil) + sysfatal("readimage: %r"); + close(fd); + p = nil; + pos = subpt(ZP, n->r.min); + oldpos = pos; + redraw(); + for(;;){ + switch(alt(a)){ + case Emouse: + if(m.buttons==1){ + for(;;) { + o = m.xy; + if(!readmouse(mctl)) + break; + if((mctl->buttons & 1) == 0) + break; + translate(subpt(mctl->xy, o)); + } + }else if(m.buttons==2){ + mctl->buttons = 1; + crop(); + }else if(m.buttons==4) + menu3hit(); + break; + case Eresize: + if(getwindow(display, Refnone)<0) + sysfatal("cannot reattach: %r"); + redraw(); + break; + case Ekeyboard: + if(k==Kdel) + threadexitsall(nil); + break; + } + } +}