Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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();
        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);
}