plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

ico.c (8791B)


      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
     42 gets(uchar *p)
     43 {
     44 	return p[0] | (p[1]<<8);
     45 }
     46 
     47 ulong
     48 getl(uchar *p)
     49 {
     50 	return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
     51 }
     52 
     53 int
     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*
    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*
    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*
    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
    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
    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
    299 buttons(int ud)
    300 {
    301 	while((mouse.buttons==0) != ud)
    302 		mouse = emouse();
    303 }
    304 
    305 void
    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
    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
    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
    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
    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
    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
    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
    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 }