blob: 10a08f77deddeea9e1a7883f6c6bfc3d5bf1afec [file] [log] [blame]
#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);
}