xref: /plan9/sys/src/cmd/crop.c (revision 0cc39a83e201996cab5ee319ea497ce6109e8d23)
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 */
12 	Blank,	/* cut off blank region according to color value */
13 			/* Blank is not actually set as a mode; it can be combined with others */
14 };
15 
16 void
usage(void)17 usage(void)
18 {
19 	fprint(2, "usage: crop [-b rgb] [-c rgb] [-i ±inset | -r R | -x ±inset | -y ±inset] [-t tx ty] [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
getint(char * s)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
crop(Memimage * m,ulong c)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
main(int argc,char * argv[])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 }
212