#include #include #include #include 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 [-b rgb] [-c rgb] [-i ±inset | -r R | -x ±inset | -y ±inset] [-t tx ty] [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, ulong c) { Memimage *n; int x, y, bpl, wpl; int left, right, top, bottom; ulong *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(ulong); buf = malloc(bpl); if(buf == nil) sysfatal("can't allocate buffer: %r"); for(y=m->r.min.y; yr.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 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; ulong 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 = ""; 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); }