xref: /plan9-contrib/sys/src/cmd/jpg/toico.c (revision 8fa2b2a7bbf95d4253e48d836188c408a39b43ab)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <draw.h>
5 
6 enum
7 {
8 	FileHdrLen=	6,
9 	IconDescrLen=	16,
10 	IconHdrLen=	40,
11 };
12 
13 typedef struct Icon Icon;
14 struct Icon
15 {
16 	Icon	*next;
17 	char	*file;
18 
19 	uchar	w;		/* icon width */
20 	uchar	h;		/* icon height */
21 	ushort	ncolor;		/* number of colors */
22 	ushort	nplane;		/* number of bit planes */
23 	ushort	bits;		/* bits per pixel */
24 	ulong	len;		/* length of data */
25 	ulong	offset;		/* file offset to data */
26 	uchar	map[4*256];	/* color map */
27 
28 	Image	*img;
29 
30 	uchar	*xor;
31 	int	xorlen;
32 	uchar	*and;
33 	int	andlen;
34 };
35 
36 typedef struct Header Header;
37 struct Header
38 {
39 	uint	n;
40 	Icon	*first;
41 	Icon	*last;
42 };
43 
44 void
Bputs(Biobuf * b,ushort x)45 Bputs(Biobuf *b, ushort x)
46 {
47 	Bputc(b, x&0xff);
48 	Bputc(b, x>>8);
49 }
50 
51 void
Bputl(Biobuf * b,ulong x)52 Bputl(Biobuf *b, ulong x)
53 {
54 	Bputs(b, x&0xffff);
55 	Bputs(b, x>>16);
56 }
57 
58 Header h;
59 
60 void*	emalloc(int);
61 void	mk8bit(Icon*, int);
62 void	mkxorand(Icon*, int);
63 void	readicon(char*);
64 
65 void
main(int argc,char ** argv)66 main(int argc, char **argv)
67 {
68 	int i;
69 	Biobuf *b, out;
70 	Icon *icon;
71 	ulong offset;
72 	ulong len;
73 
74 	ARGBEGIN{
75 	}ARGEND;
76 
77 	/* read in all the images */
78 	display = initdisplay(nil, nil, nil);
79 	if(argc < 1){
80 		readicon("/fd/0");
81 	} else {
82 		for(i = 0; i < argc; i++)
83 			readicon(argv[i]);
84 	}
85 
86 	/* create the .ico file */
87 	b = &out;
88 	Binit(b, 1, OWRITE);
89 
90 	/* offset to first icon */
91 	offset = FileHdrLen + h.n*IconDescrLen;
92 
93 	/* file header is */
94 	Bputs(b, 0);
95 	Bputs(b, 1);
96 	Bputs(b, h.n);
97 
98 	/* icon description */
99 	for(icon = h.first; icon != nil; icon = icon->next){
100 		Bputc(b, icon->w);
101 		Bputc(b, icon->h);
102 		Bputc(b, icon->ncolor);
103 		Bputc(b, 0);
104 		Bputs(b, icon->nplane);
105 		Bputs(b, icon->bits);
106 		len = IconHdrLen + icon->ncolor*4 + icon->xorlen + icon->andlen;
107 		Bputl(b, len);
108 		Bputl(b, offset);
109 		offset += len;
110 	}
111 
112 	/* icons */
113 	for(icon = h.first; icon != nil; icon = icon->next){
114 		/* icon header (BMP like) */
115 		Bputl(b, IconHdrLen);
116 		Bputl(b, icon->w);
117 		Bputl(b, 2*icon->h);
118 		Bputs(b, icon->nplane);
119 		Bputs(b, icon->bits);
120 		Bputl(b, 0);	/* compression info */
121 		Bputl(b, 0);
122 		Bputl(b, 0);
123 		Bputl(b, 0);
124 		Bputl(b, 0);
125 		Bputl(b, 0);
126 
127 		/* color map */
128 		if(Bwrite(b, icon->map, 4*icon->ncolor) < 0)
129 			sysfatal("writing color map: %r");
130 
131 		/* xor bits */
132 		if(Bwrite(b, icon->xor, icon->xorlen) < 0)
133 			sysfatal("writing xor bits: %r");
134 
135 		/* and bits */
136 		if(Bwrite(b, icon->and, icon->andlen) < 0)
137 			sysfatal("writing and bits: %r");
138 	}
139 
140 	Bterm(b);
141 	exits(0);
142 }
143 
144 void
readicon(char * file)145 readicon(char *file)
146 {
147 	int fd;
148 	Icon *icon;
149 
150 	fd = open(file, OREAD);
151 	if(fd < 0)
152 		sysfatal("opening %s: %r", file);
153 	icon = emalloc(sizeof(Icon));
154 	icon->img = readimage(display, fd, 0);
155 	if(icon->img == nil)
156 		sysfatal("reading image %s: %r", file);
157 	close(fd);
158 
159 	if(h.first)
160 		h.last->next = icon;
161 	else
162 		h.first = icon;
163 	h.last = icon;
164 	h.n++;
165 
166 	icon->h = Dy(icon->img->r);
167 	icon->w = Dx(icon->img->r);
168 	icon->bits = 1<<icon->img->depth;
169 	icon->nplane = 1;
170 
171 	/* convert to 8 bits per pixel */
172 	switch(icon->img->chan){
173 	case GREY8:
174 	case CMAP8:
175 		break;
176 	case GREY1:
177 	case GREY2:
178 	case GREY4:
179 		mk8bit(icon, 1);
180 		break;
181 	default:
182 		mk8bit(icon, 0);
183 		break;
184 	}
185 	icon->bits = 8;
186 	icon->file = file;
187 
188 	/* create xor/and masks, minimizing bits per pixel */
189 	mkxorand(icon, icon->img->chan == GREY8);
190 }
191 
192 void*
emalloc(int len)193 emalloc(int len)
194 {
195 	void *x;
196 
197 	x = mallocz(len, 1);
198 	if(x == nil)
199 		sysfatal("memory: %r");
200 	return x;
201 }
202 
203 /* convert to 8 bit */
204 void
mk8bit(Icon * icon,int grey)205 mk8bit(Icon *icon, int grey)
206 {
207 	Image *img;
208 
209 	img = allocimage(display, icon->img->r, grey ? GREY8 : CMAP8, 0, DNofill);
210 	if(img == nil)
211 		sysfatal("can't allocimage: %r");
212 	draw(img, img->r, icon->img, nil, ZP);
213 	freeimage(icon->img);
214 	icon->img = img;
215 }
216 
217 /* make xor and and mask */
218 void
mkxorand(Icon * icon,int grey)219 mkxorand(Icon *icon, int grey)
220 {
221 	int i, x, y, s, sa;
222 	uchar xx[256];
223 	uchar *data, *p, *e;
224 	int ndata;
225 	uchar *mp;
226 	int ncolor;
227 	ulong color;
228 	int bits;
229 	uchar andbyte, xorbyte;
230 	uchar *ato, *xto;
231 	int xorrl, andrl;
232 
233 	ndata = icon->h * icon->w;
234 	data = emalloc(ndata);
235 	if(unloadimage(icon->img, icon->img->r, data, ndata) < 0)
236 		sysfatal("can't unload %s: %r", icon->file);
237 	e = data + ndata;
238 
239 	/* find colors used */
240 	memset(xx, 0, sizeof xx);
241 	for(p = data; p < e; p++)
242 		xx[*p]++;
243 
244 	/* count the colors and create a mapping from plan 9 */
245 	mp = icon->map;
246 	ncolor = 0;
247 	for(i = 0; i < 256; i++){
248 		if(xx[i] == 0)
249 			continue;
250 		if(grey){
251 			*mp++ = i;
252 			*mp++ = i;
253 			*mp++ = i;
254 			*mp++ = 0;
255 		} else {
256 			color = cmap2rgb(i);
257 			*mp++ = color;
258 			*mp++ = color>>8;
259 			*mp++ = color>>16;
260 			*mp++ = 0;
261 		}
262 		xx[i] = ncolor;
263 		ncolor++;
264 	}
265 
266 	/* get minimum number of pixels per bit (with a color map) */
267 	if(ncolor <= 2){
268 		ncolor = 2;
269 		bits = 1;
270 	} else if(ncolor <= 4){
271 		ncolor = 4;
272 		bits = 2;
273 	} else if(ncolor <= 16){
274 		ncolor = 16;
275 		bits = 4;
276 	} else {
277 		ncolor = 256;
278 		bits = 8;
279 	}
280 	icon->bits = bits;
281 	icon->ncolor = ncolor;
282 
283 	/* the xor mask rows are justified to a 32 bit boundary */
284 	/* the and mask is 1 bit grey */
285 	xorrl = 4*((bits*icon->w + 31)/32);
286 	andrl = 4*((icon->w + 31)/32);
287 	icon->xor = emalloc(xorrl * icon->h);
288 	icon->and = emalloc(andrl * icon->h);
289 	icon->xorlen = xorrl*icon->h;
290 	icon->andlen = andrl*icon->h;
291 
292 	/* make both masks.  they're upside down relative to plan9 ones */
293 	p = data;
294 	for(y = 0; y < icon->h; y++){
295 		andbyte = 0;
296 		xorbyte = 0;
297 		sa = s = 0;
298 		xto = icon->xor + (icon->h-1-y)*xorrl;
299 		ato = icon->and + (icon->h-1-y)*andrl;
300 		for(x = 0; x < icon->w; x++){
301 			xorbyte <<= bits;
302 			xorbyte |= xx[*p];
303 			s += bits;
304 			if(s == 8){
305 				*xto++ = xorbyte;
306 				xorbyte = 0;
307 				s = 0;
308 			}
309 			andbyte <<= 1;
310 			if(*p == 0xff)
311 				andbyte |= 1;
312 			sa++;
313 			if(sa == 0){
314 				*ato++ = andbyte;
315 				sa = 0;
316 				andbyte = 0;
317 			}
318 			p++;
319 		}
320 	}
321 	free(data);
322 }
323