plan9fox/sys/src/cmd/crop.c
qwx dc8da7c232 crop: allow no-ops for pipelines
unlike other tools like iconv(1), a crop(1) without arguments or with
ones resulting in a no-op, like `-t 0 0', errors out.  other options
like `-i 0' do not error.  this breaks assumptions and results in
tedious intermediary steps or hacks like:

	foo | {crop -t $1 $2 >[2]/null || cat} > baz.bit

instead, just ignore the check.  subsequent code doesn't make
assumptions on that.
2021-04-12 10:29:54 +02:00

205 lines
3.9 KiB
C

#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 [-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();
return strtol(s, 0, 0);
}
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; 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;
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
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);
}