1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 #include <event.h>
6 #include <cursor.h>
7 
8 typedef struct Icon Icon;
9 struct Icon
10 {
11 	Icon	*next;
12 
13 	uchar	w;		/* icon width */
14 	uchar	h;		/* icon height */
15 	ushort	ncolor;		/* number of colors */
16 	ushort	nplane;		/* number of bit planes */
17 	ushort	bits;		/* bits per pixel */
18 	ulong	len;		/* length of data */
19 	ulong	offset;		/* file offset to data */
20 
21 	Image	*img;
22 	Image	*mask;
23 
24 	Rectangle r;		/* relative */
25 	Rectangle sr;		/* abs */
26 };
27 
28 typedef struct Header Header;
29 struct Header
30 {
31 	uint	n;
32 	Icon	*first;
33 	Icon	*last;
34 };
35 
36 int debug;
37 Mouse mouse;
38 Header h;
39 Image *background;
40 
41 ushort
gets(uchar * p)42 gets(uchar *p)
43 {
44 	return p[0] | (p[1]<<8);
45 }
46 
47 ulong
getl(uchar * p)48 getl(uchar *p)
49 {
50 	return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
51 }
52 
53 int
Bgetheader(Biobuf * b,Header * h)54 Bgetheader(Biobuf *b, Header *h)
55 {
56 	Icon *icon;
57 	int i;
58 	uchar buf[40];
59 
60 	memset(h, 0, sizeof(*h));
61 	if(Bread(b, buf, 6) != 6)
62 		goto eof;
63 	if(gets(&buf[0]) != 0)
64 		goto header;
65 	if(gets(&buf[2]) != 1)
66 		goto header;
67 	h->n = gets(&buf[4]);
68 
69 	for(i = 0; i < h->n; i++){
70 		icon = mallocz(sizeof(*icon), 1);
71 		if(icon == nil)
72 			sysfatal("malloc: %r");
73 		if(Bread(b, buf, 16) != 16)
74 			goto eof;
75 		icon->w = buf[0];
76 		icon->h = buf[1];
77 		icon->ncolor = buf[2] == 0 ? 256 : buf[2];
78 		if(buf[3] != 0)
79 			goto header;
80 		icon->nplane = gets(&buf[4]);
81 		icon->bits = gets(&buf[6]);
82 		icon->len = getl(&buf[8]);
83 		icon->offset = getl(&buf[12]);
84 
85 		if(i == 0)
86 			h->first = icon;
87 		else
88 			h->last->next = icon;
89 		h->last = icon;
90 	}
91 	return 0;
92 
93 eof:
94 	werrstr("unexpected EOF");
95 	return -1;
96 header:
97 	werrstr("unknown header format");
98 	return -1;
99 }
100 
101 uchar*
transcmap(Icon * icon,uchar * map)102 transcmap(Icon *icon, uchar *map)
103 {
104 	uchar *m, *p;
105 	int i;
106 
107 	p = m = malloc(sizeof(int)*(1<<icon->bits));
108 	for(i = 0; i < icon->ncolor; i++){
109 		*p++ = rgb2cmap(map[2], map[1], map[0]);
110 		map += 4;
111 	}
112 	return m;
113 }
114 
115 Image*
xor2img(Icon * icon,uchar * xor,uchar * map)116 xor2img(Icon *icon, uchar *xor, uchar *map)
117 {
118 	uchar *data;
119 	Image *img;
120 	int inxlen;
121 	uchar *from, *to;
122 	int s, byte, mask;
123 	int x, y;
124 
125 	inxlen = 4*((icon->bits*icon->w+31)/32);
126 	to = data = malloc(icon->w*icon->h);
127 
128 	/* rotate around the y axis, go to 8 bits, and convert color */
129 	mask = (1<<icon->bits)-1;
130 	for(y = 0; y < icon->h; y++){
131 		s = -1;
132 		byte = 0;
133 		from = xor + (icon->h - 1 - y)*inxlen;
134 		for(x = 0; x < icon->w; x++){
135 			if(s < 0){
136 				byte = *from++;
137 				s = 8-icon->bits;
138 			}
139 			*to++ = map[(byte>>s) & mask];
140 			s -= icon->bits;
141 		}
142 	}
143 
144 	/* stick in an image */
145 	img = allocimage(display, Rect(0,0,icon->w,icon->h), CMAP8, 0, DNofill);
146 	loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*icon->w);
147 
148 	free(data);
149 	return img;
150 }
151 
152 Image*
and2img(Icon * icon,uchar * and)153 and2img(Icon *icon, uchar *and)
154 {
155 	uchar *data;
156 	Image *img;
157 	int inxlen;
158 	int outxlen;
159 	uchar *from, *to;
160 	int x, y;
161 
162 	inxlen = 4*((icon->w+31)/32);
163 	to = data = malloc(inxlen*icon->h);
164 
165 	/* rotate around the y axis and invert bits */
166 	outxlen = (icon->w+7)/8;
167 	for(y = 0; y < icon->h; y++){
168 		from = and + (icon->h - 1 - y)*inxlen;
169 		for(x = 0; x < outxlen; x++){
170 			*to++ = ~(*from++);
171 		}
172 	}
173 
174 	/* stick in an image */
175 	img = allocimage(display, Rect(0,0,icon->w,icon->h), GREY1, 0, DNofill);
176 	loadimage(img, Rect(0,0,icon->w,icon->h), data, icon->h*outxlen);
177 
178 	free(data);
179 	return img;
180 }
181 
182 int
Bgeticon(Biobuf * b,Icon * icon)183 Bgeticon(Biobuf *b, Icon *icon)
184 {
185 	ulong l;
186 	ushort s;
187 	uchar *xor;
188 	uchar *and;
189 	uchar *cm;
190 	uchar *buf;
191 	uchar *map2map;
192 	Image *img;
193 
194 	Bseek(b, icon->offset, 0);
195 	buf = malloc(icon->len);
196 	if(buf == nil)
197 		return -1;
198 	if(Bread(b, buf, icon->len) != icon->len){
199 		werrstr("unexpected EOF");
200 		return -1;
201 	}
202 
203 	/* this header's info takes precedence over previous one */
204 	if(getl(buf) != 40){
205 		werrstr("bad icon header");
206 		return -1;
207 	}
208 	l = getl(buf+4);
209 	if(l != icon->w)
210 		icon->w = l;
211 	l = getl(buf+8);
212 	if(l>>1 != icon->h)
213 		icon->h = l>>1;
214 	s = gets(buf+12);
215 	if(s != icon->nplane)
216 		icon->nplane = s;
217 	s = gets(buf+14);
218 	if(s != icon->bits)
219 		icon->bits = s;
220 
221 	/* limit what we handle */
222 	switch(icon->bits){
223 	case 1:
224 	case 2:
225 	case 4:
226 	case 8:
227 		break;
228 	default:
229 		werrstr("don't support %d bit pixels", icon->bits);
230 		return -1;
231 	}
232 	if(icon->nplane != 1){
233 		werrstr("don't support %d planes", icon->nplane);
234 		return -1;
235 	}
236 
237 	cm = buf + 40;
238 	xor = cm + 4*icon->ncolor;
239 	and = xor + icon->h*4*((icon->bits*icon->w+31)/32);
240 
241 	/* translate the color map to a plan 9 one */
242 	map2map = transcmap(icon, cm);
243 
244 	/* convert the images */
245 	icon->img = xor2img(icon, xor, map2map);
246 	icon->mask = and2img(icon, and);
247 
248 	/* so that we save an image with a white background */
249 	img = allocimage(display, icon->img->r, CMAP8, 0, DWhite);
250 	draw(img, icon->img->r, icon->img, icon->mask, ZP);
251 	icon->img = img;
252 
253 	free(buf);
254 	free(map2map);
255 	return 0;
256 }
257 
258 void
usage(void)259 usage(void)
260 {
261 	fprint(2, "usage: %s -W winsize [file]\n", argv0);
262 	exits("usage");
263 }
264 
265 enum
266 {
267 	Mimage,
268 	Mmask,
269 	Mexit,
270 
271 	Up= 1,
272 	Down= 0
273 };
274 
275 char	*menu3str[] = {
276 	"write image",
277 	"write mask",
278 	"exit",
279 	0
280 };
281 
282 Menu	menu3 = {
283 	menu3str
284 };
285 
286 Cursor sight = {
287 	{-7, -7},
288 	{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
289 	 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
290 	 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
291 	 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
292 	{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
293 	 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
294 	 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
295 	 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
296 };
297 
298 void
buttons(int ud)299 buttons(int ud)
300 {
301 	while((mouse.buttons==0) != ud)
302 		mouse = emouse();
303 }
304 
305 void
mesg(char * fmt,...)306 mesg(char *fmt, ...)
307 {
308 	va_list arg;
309 	char buf[1024];
310 	static char obuf[1024];
311 
312 	va_start(arg, fmt);
313 	vseprint(buf, buf+sizeof(buf), fmt, arg);
314 	va_end(arg);
315 	string(screen, screen->r.min, background, ZP, font, obuf);
316 	string(screen, screen->r.min, display->white, ZP, font, buf);
317 	strcpy(obuf, buf);
318 }
319 
320 void
doimage(Icon * icon)321 doimage(Icon *icon)
322 {
323 	int rv;
324 	char file[256];
325 	int fd;
326 
327 	rv = -1;
328 	snprint(file, sizeof(file), "%dx%d.img", icon->w, icon->h);
329 	fd = create(file, OWRITE, 0664);
330 	if(fd >= 0){
331 		rv = writeimage(fd, icon->img, 0);
332 		close(fd);
333 	}
334 	if(rv < 0)
335 		mesg("error writing %s: %r", file);
336 	else
337 		mesg("created %s", file);
338 }
339 
340 void
domask(Icon * icon)341 domask(Icon *icon)
342 {
343 	int rv;
344 	char file[64];
345 	int fd;
346 
347 	rv = -1;
348 	snprint(file, sizeof(file), "%dx%d.mask", icon->w, icon->h);
349 	fd = create(file, OWRITE, 0664);
350 	if(fd >= 0){
351 		rv = writeimage(fd, icon->mask, 0);
352 		close(fd);
353 	}
354 	if(rv < 0)
355 		mesg("error writing %s: %r", file);
356 	else
357 		mesg("created %s", file);
358 }
359 
360 void
apply(void (* f)(Icon *))361 apply(void (*f)(Icon*))
362 {
363 	Icon *icon;
364 
365 	esetcursor(&sight);
366 	buttons(Down);
367 	if(mouse.buttons == 4)
368 		for(icon = h.first; icon; icon = icon->next)
369 			if(ptinrect(mouse.xy, icon->sr)){
370 				buttons(Up);
371 				f(icon);
372 				break;
373 			}
374 	buttons(Up);
375 	esetcursor(0);
376 }
377 
378 void
menu(void)379 menu(void)
380 {
381 	int sel;
382 
383 	sel = emenuhit(3, &mouse, &menu3);
384 	switch(sel){
385 	case Mimage:
386 		apply(doimage);
387 		break;
388 	case Mmask:
389 		apply(domask);
390 		break;
391 	case Mexit:
392 		exits(0);
393 		break;
394 	}
395 }
396 
397 void
mousemoved(void)398 mousemoved(void)
399 {
400 	Icon *icon;
401 
402 	for(icon = h.first; icon; icon = icon->next)
403 		if(ptinrect(mouse.xy, icon->sr)){
404 			mesg("%dx%d", icon->w, icon->h);
405 			return;
406 		}
407 	mesg("");
408 }
409 
410 enum
411 {
412 	BORDER= 1
413 };
414 
415 void
eresized(int new)416 eresized(int new)
417 {
418 	Icon *icon;
419 	Rectangle r;
420 
421 	if(new && getwindow(display, Refnone) < 0)
422 		sysfatal("can't reattach to window");
423 	draw(screen, screen->clipr, background, nil, ZP);
424 	r.max.x = screen->r.min.x;
425 	r.min.y = screen->r.min.y + font->height + 2*BORDER;
426 	for(icon = h.first; icon != nil; icon = icon->next){
427 		r.min.x = r.max.x + BORDER;
428 		r.max.x = r.min.x + Dx(icon->img->r);
429 		r.max.y = r.min.y + Dy(icon->img->r);
430 		draw(screen, r, icon->img, nil, ZP);
431 		border(screen, r, -BORDER, display->black, ZP);
432 		icon->sr = r;
433 	}
434 	flushimage(display, 1);
435 }
436 
437 void
main(int argc,char ** argv)438 main(int argc, char **argv)
439 {
440 	Biobuf in;
441 	Icon *icon;
442 	int fd;
443 	Rectangle r;
444 	Event e;
445 
446 	ARGBEGIN{
447 	case 'W':
448 		winsize = EARGF(usage());
449 		break;
450 	case 'd':
451 		debug = 1;
452 		break;
453 	}ARGEND;
454 
455 	fd = -1;
456 	switch(argc){
457 	case 0:
458 		fd = 0;
459 		break;
460 	case 1:
461 		fd = open(argv[0], OREAD);
462 		if(fd < 0)
463 			sysfatal("opening: %r");
464 		break;
465 	default:
466 		usage();
467 		break;
468 	}
469 
470 	Binit(&in, fd, OREAD);
471 
472 	if(Bgetheader(&in, &h) < 0)
473 		sysfatal("reading header: %r");
474 
475 	initdraw(0, nil, "ico");
476 	background = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x808080FF);
477 
478 	einit(Emouse|Ekeyboard);
479 
480 	r.min = Pt(4, 4);
481 	for(icon = h.first; icon != nil; icon = icon->next){
482 		if(Bgeticon(&in, icon) < 0){
483 			fprint(2, "bad rectangle: %r\n");
484 			continue;
485 		}
486 		if(debug)
487 			fprint(2, "w %ud h %ud ncolor %ud bits %ud len %lud offset %lud\n",
488 			   icon->w, icon->h, icon->ncolor, icon->bits, icon->len, icon->offset);
489 		r.max = addpt(r.min, Pt(icon->w, icon->h));
490 		icon->r = r;
491 		r.min.x += r.max.x;
492 	}
493 	eresized(0);
494 
495 	for(;;)
496 		switch(event(&e)){
497 		case Ekeyboard:
498 			break;
499 		case Emouse:
500 			mouse = e.mouse;
501 			if(mouse.buttons & 4)
502 				menu();
503 			else
504 				mousemoved();
505 			break;
506 		}
507 }
508