rsc | 228bb71 | 2004-12-28 23:13:17 +0000 | [diff] [blame] | 1 | #include <u.h> |
| 2 | #include <libc.h> |
| 3 | #include <draw.h> |
| 4 | #include <memdraw.h> |
| 5 | |
| 6 | enum |
| 7 | { |
| 8 | None, |
| 9 | Inset, /* move border in or out uniformly */ |
| 10 | Insetxy, /* move border in or out; different parameters for x and y */ |
| 11 | Set, /* set rectangle to absolute values */ |
rsc | cbeb0b2 | 2006-04-01 19:24:03 +0000 | [diff] [blame] | 12 | Blank /* cut off blank region according to color value */ |
rsc | 228bb71 | 2004-12-28 23:13:17 +0000 | [diff] [blame] | 13 | /* Blank is not actually set as a mode; it can be combined with others */ |
| 14 | }; |
| 15 | |
| 16 | void |
| 17 | usage(void) |
| 18 | { |
| 19 | fprint(2, "usage: crop [-c rgb] [-i ±inset | -r R | -x ±inset | -y ±inset] [-t tx ty] [-b rgb ] [imagefile]\n"); |
| 20 | fprint(2, "\twhere R is a rectangle minx miny maxx maxy\n"); |
| 21 | fprint(2, "\twhere rgb is a color red green blue\n"); |
| 22 | exits("usage"); |
| 23 | } |
| 24 | |
| 25 | int |
| 26 | getint(char *s) |
| 27 | { |
| 28 | if(s == nil) |
| 29 | usage(); |
| 30 | if(*s == '+') |
| 31 | return atoi(s+1); |
| 32 | if(*s == '-') |
| 33 | return -atoi(s+1); |
| 34 | return atoi(s); |
| 35 | } |
| 36 | |
| 37 | Rectangle |
| 38 | crop(Memimage *m, ulong c) |
| 39 | { |
| 40 | Memimage *n; |
| 41 | int x, y, bpl, wpl; |
| 42 | int left, right, top, bottom; |
| 43 | ulong *buf; |
| 44 | |
| 45 | left = m->r.max.x; |
| 46 | right = m->r.min.x; |
| 47 | top = m->r.max.y; |
| 48 | bottom = m->r.min.y; |
| 49 | n = nil; |
| 50 | if(m->chan != RGBA32){ |
| 51 | /* convert type for simplicity */ |
| 52 | n = allocmemimage(m->r, RGBA32); |
| 53 | if(n == nil) |
| 54 | sysfatal("can't allocate temporary image: %r"); |
| 55 | memimagedraw(n, n->r, m, m->r.min, nil, ZP, S); |
| 56 | m = n; |
| 57 | } |
| 58 | wpl = wordsperline(m->r, m->depth); |
| 59 | bpl = wpl*sizeof(ulong); |
| 60 | buf = malloc(bpl); |
| 61 | if(buf == nil) |
| 62 | sysfatal("can't allocate buffer: %r"); |
| 63 | |
| 64 | for(y=m->r.min.y; y<m->r.max.y; y++){ |
| 65 | x = unloadmemimage(m, Rect(m->r.min.x, y, m->r.max.x, y+1), (uchar*)buf, bpl); |
| 66 | if(x != bpl) |
| 67 | sysfatal("unloadmemimage"); |
| 68 | for(x=0; x<wpl; x++) |
| 69 | if(buf[x] != c){ |
| 70 | if(x < left) |
| 71 | left = x; |
| 72 | if(x > right) |
| 73 | right = x; |
| 74 | if(y < top) |
| 75 | top = y; |
| 76 | bottom = y; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | if(n != nil) |
| 81 | freememimage(n); |
| 82 | return Rect(left, top, right+1, bottom+1); |
| 83 | } |
| 84 | |
| 85 | void |
| 86 | main(int argc, char *argv[]) |
| 87 | { |
| 88 | int fd, mode, red, green, blue; |
| 89 | Rectangle r, rparam; |
| 90 | Point t; |
| 91 | Memimage *m, *new; |
| 92 | char *file; |
| 93 | ulong bg, cropval; |
| 94 | long dw; |
| 95 | |
| 96 | memimageinit(); |
| 97 | mode = None; |
| 98 | bg = 0; |
| 99 | cropval = 0; |
| 100 | t = ZP; |
| 101 | memset(&rparam, 0, sizeof rparam); |
| 102 | |
| 103 | ARGBEGIN{ |
| 104 | case 'b': |
| 105 | if(bg != 0) |
| 106 | usage(); |
| 107 | red = getint(ARGF())&0xFF; |
| 108 | green = getint(ARGF())&0xFF; |
| 109 | blue = getint(ARGF())&0xFF; |
| 110 | bg = (red<<24)|(green<<16)|(blue<<8)|0xFF; |
| 111 | break; |
| 112 | case 'c': |
| 113 | if(cropval != 0) |
| 114 | usage(); |
| 115 | red = getint(ARGF())&0xFF; |
| 116 | green = getint(ARGF())&0xFF; |
| 117 | blue = getint(ARGF())&0xFF; |
| 118 | cropval = (red<<24)|(green<<16)|(blue<<8)|0xFF; |
| 119 | break; |
| 120 | case 'i': |
| 121 | if(mode != None) |
| 122 | usage(); |
| 123 | mode = Inset; |
| 124 | rparam.min.x = getint(ARGF()); |
| 125 | break; |
| 126 | case 'x': |
| 127 | if(mode != None && mode != Insetxy) |
| 128 | usage(); |
| 129 | mode = Insetxy; |
| 130 | rparam.min.x = getint(ARGF()); |
| 131 | break; |
| 132 | case 'y': |
| 133 | if(mode != None && mode != Insetxy) |
| 134 | usage(); |
| 135 | mode = Insetxy; |
| 136 | rparam.min.y = getint(ARGF()); |
| 137 | break; |
| 138 | case 'r': |
| 139 | if(mode != None) |
| 140 | usage(); |
| 141 | mode = Set; |
| 142 | rparam.min.x = getint(ARGF()); |
| 143 | rparam.min.y = getint(ARGF()); |
| 144 | rparam.max.x = getint(ARGF()); |
| 145 | rparam.max.y = getint(ARGF()); |
| 146 | break; |
| 147 | case 't': |
| 148 | t.x = getint(ARGF()); |
| 149 | t.y = getint(ARGF()); |
| 150 | break; |
| 151 | default: |
| 152 | usage(); |
| 153 | }ARGEND |
| 154 | |
| 155 | if(mode == None && cropval == 0 && eqpt(ZP, t)) |
| 156 | usage(); |
| 157 | |
| 158 | file = "<stdin>"; |
| 159 | fd = 0; |
| 160 | if(argc > 1) |
| 161 | usage(); |
| 162 | else if(argc == 1){ |
| 163 | file = argv[0]; |
| 164 | fd = open(file, OREAD); |
| 165 | if(fd < 0) |
| 166 | sysfatal("can't open %s: %r", file); |
| 167 | } |
| 168 | |
| 169 | m = readmemimage(fd); |
| 170 | if(m == nil) |
| 171 | sysfatal("can't read %s: %r", file); |
| 172 | |
| 173 | r = m->r; |
| 174 | if(cropval != 0){ |
| 175 | r = crop(m, cropval); |
| 176 | m->clipr = r; |
| 177 | } |
| 178 | |
| 179 | switch(mode){ |
| 180 | case None: |
| 181 | break; |
| 182 | case Inset: |
| 183 | r = insetrect(r, rparam.min.x); |
| 184 | break; |
| 185 | case Insetxy: |
| 186 | r.min.x += rparam.min.x; |
| 187 | r.max.x -= rparam.min.x; |
| 188 | r.min.y += rparam.min.y; |
| 189 | r.max.y -= rparam.min.y; |
| 190 | break; |
| 191 | case Set: |
| 192 | r = rparam; |
| 193 | break; |
| 194 | } |
| 195 | |
| 196 | new = allocmemimage(r, m->chan); |
| 197 | if(new == nil) |
| 198 | sysfatal("can't allocate new image: %r"); |
| 199 | if(bg != 0) |
| 200 | memfillcolor(new, bg); |
| 201 | else |
| 202 | memfillcolor(new, 0x000000FF); |
| 203 | |
| 204 | memimagedraw(new, m->clipr, m, m->clipr.min, nil, ZP, S); |
| 205 | dw = byteaddr(new, ZP) - byteaddr(new, t); |
| 206 | new->r = rectaddpt(new->r, t); |
| 207 | new->zero += dw; |
| 208 | if(writememimage(1, new) < 0) |
| 209 | sysfatal("write error on output: %r"); |
| 210 | exits(nil); |
| 211 | } |