|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <draw.h> | 
|  | #include <memdraw.h> | 
|  |  | 
|  | enum | 
|  | { | 
|  | None, | 
|  | Inset,	/* move border in or out uniformly */ | 
|  | Insetxy,	/* move border in or out; different parameters for x and y */ | 
|  | Set,		/* set rectangle to absolute values */ | 
|  | Blank	/* cut off blank region according to color value */ | 
|  | /* Blank is not actually set as a mode; it can be combined with others */ | 
|  | }; | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: crop [-c rgb] [-i ±inset | -r R | -x ±inset | -y ±inset] [-t tx ty] [-b rgb ] [imagefile]\n"); | 
|  | fprint(2, "\twhere R is a rectangle minx miny maxx maxy\n"); | 
|  | fprint(2, "\twhere rgb is a color red green blue\n"); | 
|  | exits("usage"); | 
|  | } | 
|  |  | 
|  | int | 
|  | getint(char *s) | 
|  | { | 
|  | if(s == nil) | 
|  | usage(); | 
|  | if(*s == '+') | 
|  | return atoi(s+1); | 
|  | if(*s == '-') | 
|  | return -atoi(s+1); | 
|  | return atoi(s); | 
|  | } | 
|  |  | 
|  | Rectangle | 
|  | crop(Memimage *m, uint32 c) | 
|  | { | 
|  | Memimage *n; | 
|  | int x, y, bpl, wpl; | 
|  | int left, right, top, bottom; | 
|  | uint32 *buf; | 
|  |  | 
|  | left = m->r.max.x; | 
|  | right = m->r.min.x; | 
|  | top = m->r.max.y; | 
|  | bottom = m->r.min.y; | 
|  | n = nil; | 
|  | if(m->chan != RGBA32){ | 
|  | /* convert type for simplicity */ | 
|  | n = allocmemimage(m->r, RGBA32); | 
|  | if(n == nil) | 
|  | sysfatal("can't allocate temporary image: %r"); | 
|  | memimagedraw(n, n->r, m, m->r.min, nil, ZP, S); | 
|  | m = n; | 
|  | } | 
|  | wpl = wordsperline(m->r, m->depth); | 
|  | bpl = wpl*sizeof(uint32); | 
|  | buf = malloc(bpl); | 
|  | if(buf == nil) | 
|  | sysfatal("can't allocate buffer: %r"); | 
|  |  | 
|  | for(y=m->r.min.y; y<m->r.max.y; y++){ | 
|  | x = unloadmemimage(m, Rect(m->r.min.x, y, m->r.max.x, y+1), (uchar*)buf, bpl); | 
|  | if(x != bpl) | 
|  | sysfatal("unloadmemimage"); | 
|  | for(x=0; x<wpl; x++) | 
|  | if(buf[x] != c){ | 
|  | if(x < left) | 
|  | left = x; | 
|  | if(x > right) | 
|  | right = x; | 
|  | if(y < top) | 
|  | top = y; | 
|  | bottom = y; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(n != nil) | 
|  | freememimage(n); | 
|  | return Rect(left, top, right+1, bottom+1); | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int fd, mode, red, green, blue; | 
|  | Rectangle r, rparam; | 
|  | Point t; | 
|  | Memimage *m, *new; | 
|  | char *file; | 
|  | uint32 bg, cropval; | 
|  | long dw; | 
|  |  | 
|  | memimageinit(); | 
|  | mode = None; | 
|  | bg = 0; | 
|  | cropval = 0; | 
|  | t = ZP; | 
|  | memset(&rparam, 0, sizeof rparam); | 
|  |  | 
|  | ARGBEGIN{ | 
|  | case 'b': | 
|  | if(bg != 0) | 
|  | usage(); | 
|  | red = getint(ARGF())&0xFF; | 
|  | green = getint(ARGF())&0xFF; | 
|  | blue = getint(ARGF())&0xFF; | 
|  | bg = (red<<24)|(green<<16)|(blue<<8)|0xFF; | 
|  | break; | 
|  | case 'c': | 
|  | if(cropval != 0) | 
|  | usage(); | 
|  | red = getint(ARGF())&0xFF; | 
|  | green = getint(ARGF())&0xFF; | 
|  | blue = getint(ARGF())&0xFF; | 
|  | cropval = (red<<24)|(green<<16)|(blue<<8)|0xFF; | 
|  | break; | 
|  | case 'i': | 
|  | if(mode != None) | 
|  | usage(); | 
|  | mode = Inset; | 
|  | rparam.min.x = getint(ARGF()); | 
|  | break; | 
|  | case 'x': | 
|  | if(mode != None && mode != Insetxy) | 
|  | usage(); | 
|  | mode = Insetxy; | 
|  | rparam.min.x = getint(ARGF()); | 
|  | break; | 
|  | case 'y': | 
|  | if(mode != None && mode != Insetxy) | 
|  | usage(); | 
|  | mode = Insetxy; | 
|  | rparam.min.y = getint(ARGF()); | 
|  | break; | 
|  | case 'r': | 
|  | if(mode != None) | 
|  | usage(); | 
|  | mode = Set; | 
|  | rparam.min.x = getint(ARGF()); | 
|  | rparam.min.y = getint(ARGF()); | 
|  | rparam.max.x = getint(ARGF()); | 
|  | rparam.max.y = getint(ARGF()); | 
|  | break; | 
|  | case 't': | 
|  | t.x = getint(ARGF()); | 
|  | t.y = getint(ARGF()); | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | }ARGEND | 
|  |  | 
|  | if(mode == None && cropval == 0 && eqpt(ZP, t)) | 
|  | usage(); | 
|  |  | 
|  | file = "<stdin>"; | 
|  | fd = 0; | 
|  | if(argc > 1) | 
|  | usage(); | 
|  | else if(argc == 1){ | 
|  | file = argv[0]; | 
|  | fd = open(file, OREAD); | 
|  | if(fd < 0) | 
|  | sysfatal("can't open %s: %r", file); | 
|  | } | 
|  |  | 
|  | m = readmemimage(fd); | 
|  | if(m == nil) | 
|  | sysfatal("can't read %s: %r", file); | 
|  |  | 
|  | r = m->r; | 
|  | if(cropval != 0){ | 
|  | r = crop(m, cropval); | 
|  | m->clipr = r; | 
|  | } | 
|  |  | 
|  | switch(mode){ | 
|  | case None: | 
|  | break; | 
|  | case Inset: | 
|  | r = insetrect(r, rparam.min.x); | 
|  | break; | 
|  | case Insetxy: | 
|  | r.min.x += rparam.min.x; | 
|  | r.max.x -= rparam.min.x; | 
|  | r.min.y += rparam.min.y; | 
|  | r.max.y -= rparam.min.y; | 
|  | break; | 
|  | case Set: | 
|  | r = rparam; | 
|  | break; | 
|  | } | 
|  |  | 
|  | new = allocmemimage(r, m->chan); | 
|  | if(new == nil) | 
|  | sysfatal("can't allocate new image: %r"); | 
|  | if(bg != 0) | 
|  | memfillcolor(new, bg); | 
|  | else | 
|  | memfillcolor(new, 0x000000FF); | 
|  |  | 
|  | memimagedraw(new, m->clipr, m, m->clipr.min, nil, ZP, S); | 
|  | dw = byteaddr(new, ZP) - byteaddr(new, t); | 
|  | new->r = rectaddpt(new->r, t); | 
|  | new->zero += dw; | 
|  | if(writememimage(1, new) < 0) | 
|  | sysfatal("write error on output: %r"); | 
|  | exits(nil); | 
|  | } |