mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
212 lines
4 KiB
C
212 lines
4 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 [-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, 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
|
||
|
|
||
|
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);
|
||
|
}
|