tgif.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tgif.c (8948B) | |
--- | |
1 #include <u.h> | |
2 #include <libc.h> | |
3 #include <bio.h> | |
4 #include <draw.h> | |
5 #include <event.h> | |
6 #include "imagefile.h" | |
7 | |
8 int cflag = 0; | |
9 int dflag = 0; | |
10 int eflag = 0; | |
11 int nineflag = 0; | |
12 int threeflag = 0; | |
13 int output = 0; | |
14 ulong outchan = CMAP8; | |
15 Image **allims; | |
16 Image **allmasks; | |
17 int which; | |
18 int defaultcolor = 1; | |
19 | |
20 enum{ | |
21 Border = 2, | |
22 Edge = 5 | |
23 }; | |
24 | |
25 char *show(int, char*); | |
26 | |
27 Rectangle | |
28 imager(void) | |
29 { | |
30 Rectangle r; | |
31 | |
32 if(allims==nil || allims[0]==nil) | |
33 return screen->r; | |
34 r = insetrect(screen->clipr, Edge+Border); | |
35 r.max.x = r.min.x+Dx(allims[0]->r); | |
36 r.max.y = r.min.y+Dy(allims[0]->r); | |
37 return r; | |
38 } | |
39 | |
40 void | |
41 eresized(int new) | |
42 { | |
43 Rectangle r; | |
44 | |
45 if(new && getwindow(display, Refnone) < 0){ | |
46 fprint(2, "gif: can't reattach to window\n"); | |
47 exits("resize"); | |
48 } | |
49 if(allims==nil || allims[which]==nil) | |
50 return; | |
51 r = rectaddpt(allims[0]->r, subpt(screen->r.min, allims[0]->r.mi… | |
52 if(!new && !winsize) | |
53 drawresizewindow(r); | |
54 r = rectaddpt(r, subpt(allims[which]->r.min, allims[0]->r.min)); | |
55 drawop(screen, r, allims[which], allmasks[which], allims[which]-… | |
56 flushimage(display, 1); | |
57 } | |
58 | |
59 void | |
60 usage(void) | |
61 { | |
62 fprint(2, "usage: gif -39cdektv -W winsize [file.gif ...]\n"); | |
63 exits("usage"); | |
64 } | |
65 | |
66 | |
67 void | |
68 main(int argc, char *argv[]) | |
69 { | |
70 int fd, i; | |
71 char *err; | |
72 | |
73 ARGBEGIN{ | |
74 case 'W': | |
75 winsize = EARGF(usage()); | |
76 break; | |
77 case '3': /* produce encoded, compressed, three-c… | |
78 threeflag++; | |
79 /* fall through */ | |
80 case 't': /* produce encoded, compressed, true-co… | |
81 cflag++; | |
82 dflag++; | |
83 output++; | |
84 defaultcolor = 0; | |
85 outchan = RGB24; | |
86 break; | |
87 case 'c': /* produce encoded, compressed, bitmap … | |
88 cflag++; | |
89 dflag++; | |
90 output++; | |
91 if(defaultcolor) | |
92 outchan = CMAP8; | |
93 break; | |
94 case 'd': /* suppress display of image */ | |
95 dflag++; | |
96 break; | |
97 case 'e': /* disable floyd-steinberg error diffus… | |
98 eflag++; | |
99 break; | |
100 case 'k': /* force black and white */ | |
101 defaultcolor = 0; | |
102 outchan = GREY8; | |
103 break; | |
104 case 'v': /* force RGBV */ | |
105 defaultcolor = 0; | |
106 outchan = CMAP8; | |
107 break; | |
108 case '9': /* produce plan 9, uncompressed, bitmap… | |
109 nineflag++; | |
110 dflag++; | |
111 output++; | |
112 if(defaultcolor) | |
113 outchan = CMAP8; | |
114 break; | |
115 default: | |
116 usage(); | |
117 }ARGEND; | |
118 | |
119 err = nil; | |
120 if(argc == 0) | |
121 err = show(0, "<stdin>"); | |
122 else{ | |
123 for(i=0; i<argc; i++){ | |
124 fd = open(argv[i], OREAD); | |
125 if(fd < 0){ | |
126 fprint(2, "gif: can't open %s: %r\n", ar… | |
127 err = "open"; | |
128 }else{ | |
129 err = show(fd, argv[i]); | |
130 close(fd); | |
131 } | |
132 if(output && argc>1 && err==nil){ | |
133 fprint(2, "gif: exiting after one file\n… | |
134 break; | |
135 } | |
136 } | |
137 } | |
138 exits(err); | |
139 } | |
140 | |
141 Image* | |
142 transparency(Rawimage *r, char *name) | |
143 { | |
144 Image *i; | |
145 int j, index; | |
146 uchar *pic, *mpic, *mask; | |
147 | |
148 if((r->gifflags&TRANSP) == 0) | |
149 return nil; | |
150 i = allocimage(display, r->r, GREY8, 0, 0); | |
151 if(i == nil){ | |
152 fprint(2, "gif: allocimage for mask of %s failed: %r\n",… | |
153 return nil; | |
154 } | |
155 pic = r->chans[0]; | |
156 mask = malloc(r->chanlen); | |
157 if(mask == nil){ | |
158 fprint(2, "gif: malloc for mask of %s failed: %r\n", nam… | |
159 freeimage(i); | |
160 return nil; | |
161 } | |
162 index = r->giftrindex; | |
163 mpic = mask; | |
164 for(j=0; j<r->chanlen; j++) | |
165 if(*pic++ == index) | |
166 *mpic++ = 0; | |
167 else | |
168 *mpic++ = 0xFF; | |
169 if(loadimage(i, i->r, mask, r->chanlen) < 0){ | |
170 fprint(2, "gif: loadimage for mask of %s failed: %r\n", … | |
171 free(mask); | |
172 freeimage(i); | |
173 return nil; | |
174 } | |
175 free(mask); | |
176 return i; | |
177 } | |
178 | |
179 /* interleave alpha values of 0xFF in data stream. alpha value comes fir… | |
180 uchar* | |
181 expand(uchar *u, int chanlen, int nchan) | |
182 { | |
183 int j, k; | |
184 uchar *v, *up, *vp; | |
185 | |
186 v = malloc(chanlen*(nchan+1)); | |
187 if(v == nil){ | |
188 fprint(2, "gif: malloc fails: %r\n"); | |
189 exits("malloc"); | |
190 } | |
191 up = u; | |
192 vp = v; | |
193 for(j=0; j<chanlen; j++){ | |
194 *vp++ = 0xFF; | |
195 for(k=0; k<nchan; k++) | |
196 *vp++ = *up++; | |
197 } | |
198 return v; | |
199 } | |
200 | |
201 void | |
202 addalpha(Rawimage *i) | |
203 { | |
204 char buf[32]; | |
205 | |
206 switch(outchan){ | |
207 case CMAP8: | |
208 i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); | |
209 i->chanlen = 2*(i->chanlen/1); | |
210 i->chandesc = CRGBVA16; | |
211 outchan = CHAN2(CMap, 8, CAlpha, 8); | |
212 break; | |
213 | |
214 case GREY8: | |
215 i->chans[0] = expand(i->chans[0], i->chanlen/1, 1); | |
216 i->chanlen = 2*(i->chanlen/1); | |
217 i->chandesc = CYA16; | |
218 outchan = CHAN2(CGrey, 8, CAlpha, 8); | |
219 break; | |
220 | |
221 case RGB24: | |
222 i->chans[0] = expand(i->chans[0], i->chanlen/3, 3); | |
223 i->chanlen = 4*(i->chanlen/3); | |
224 i->chandesc = CRGBA32; | |
225 outchan = RGBA32; | |
226 break; | |
227 | |
228 default: | |
229 chantostr(buf, outchan); | |
230 fprint(2, "gif: can't add alpha to type %s\n", buf); | |
231 exits("err"); | |
232 } | |
233 } | |
234 | |
235 /* | |
236 * Called only when writing output. If the output is RGBA32, | |
237 * we must write four bytes per pixel instead of two. | |
238 * There's always at least two: data plus alpha. | |
239 * r is used only for reference; the image is already in c. | |
240 */ | |
241 void | |
242 blackout(Rawimage *r, Rawimage *c) | |
243 { | |
244 int i, trindex; | |
245 uchar *rp, *cp; | |
246 | |
247 rp = r->chans[0]; | |
248 cp = c->chans[0]; | |
249 trindex = r->giftrindex; | |
250 if(outchan == RGBA32) | |
251 for(i=0; i<r->chanlen; i++){ | |
252 if(*rp == trindex){ | |
253 *cp++ = 0x00; | |
254 *cp++ = 0x00; | |
255 *cp++ = 0x00; | |
256 *cp++ = 0x00; | |
257 }else{ | |
258 *cp++ = 0xFF; | |
259 cp += 3; | |
260 } | |
261 rp++; | |
262 } | |
263 else | |
264 for(i=0; i<r->chanlen; i++){ | |
265 if(*rp == trindex){ | |
266 *cp++ = 0x00; | |
267 *cp++ = 0x00; | |
268 }else{ | |
269 *cp++ = 0xFF; | |
270 cp++; | |
271 } | |
272 rp++; | |
273 } | |
274 } | |
275 | |
276 int | |
277 init(void) | |
278 { | |
279 static int inited; | |
280 | |
281 if(inited == 0){ | |
282 if(initdraw(0, 0, 0) < 0){ | |
283 fprint(2, "gif: initdraw failed: %r\n"); | |
284 return -1; | |
285 } | |
286 einit(Ekeyboard|Emouse); | |
287 inited++; | |
288 } | |
289 return 1; | |
290 } | |
291 | |
292 char* | |
293 show(int fd, char *name) | |
294 { | |
295 Rawimage **images, **rgbv; | |
296 Image **ims, **masks; | |
297 int j, k, n, ch, nloop, loopcount, dt; | |
298 char *err; | |
299 char buf[32]; | |
300 | |
301 err = nil; | |
302 images = readgif(fd, CRGB); | |
303 if(images == nil){ | |
304 fprint(2, "gif: decode %s failed: %r\n", name); | |
305 return "decode"; | |
306 } | |
307 for(n=0; images[n]; n++) | |
308 ; | |
309 ims = malloc((n+1)*sizeof(Image*)); | |
310 masks = malloc((n+1)*sizeof(Image*)); | |
311 rgbv = malloc((n+1)*sizeof(Rawimage*)); | |
312 if(masks==nil || rgbv==nil || ims==nil){ | |
313 fprint(2, "gif: malloc of masks for %s failed: %r\n", na… | |
314 err = "malloc"; | |
315 goto Return; | |
316 } | |
317 memset(masks, 0, (n+1)*sizeof(Image*)); | |
318 memset(ims, 0, (n+1)*sizeof(Image*)); | |
319 memset(rgbv, 0, (n+1)*sizeof(Rawimage*)); | |
320 if(!dflag){ | |
321 if(init() < 0){ | |
322 err = "initdraw"; | |
323 goto Return; | |
324 } | |
325 if(defaultcolor && screen->depth>8) | |
326 outchan = RGB24; | |
327 } | |
328 | |
329 for(k=0; k<n; k++){ | |
330 if(outchan == CMAP8) | |
331 rgbv[k] = torgbv(images[k], !eflag); | |
332 else{ | |
333 if(outchan==GREY8 || (images[k]->chandesc==CY &&… | |
334 rgbv[k] = totruecolor(images[k], CY); | |
335 else | |
336 rgbv[k] = totruecolor(images[k], CRGB24); | |
337 } | |
338 if(rgbv[k] == nil){ | |
339 fprint(2, "gif: converting %s to local format fa… | |
340 err = "torgbv"; | |
341 goto Return; | |
342 } | |
343 if(!dflag){ | |
344 masks[k] = transparency(images[k], name); | |
345 if(rgbv[k]->chandesc == CY) | |
346 ims[k] = allocimage(display, rgbv[k]->r,… | |
347 else | |
348 ims[k] = allocimage(display, rgbv[k]->r,… | |
349 if(ims[k] == nil){ | |
350 fprint(2, "gif: allocimage %s failed: %r… | |
351 err = "allocimage"; | |
352 goto Return; | |
353 } | |
354 if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0… | |
355 fprint(2, "gif: loadimage %s failed: %r\… | |
356 err = "loadimage"; | |
357 goto Return; | |
358 } | |
359 } | |
360 } | |
361 | |
362 allims = ims; | |
363 allmasks = masks; | |
364 loopcount = images[0]->gifloopcount; | |
365 if(!dflag){ | |
366 nloop = 0; | |
367 do{ | |
368 for(k=0; k<n; k++){ | |
369 which = k; | |
370 eresized(0); | |
371 dt = images[k]->gifdelay*10; | |
372 if(dt < 50) | |
373 dt = 50; | |
374 while(n==1 || ecankbd()){ | |
375 if((ch=ekbd())=='q' || ch==0x7F … | |
376 exits(nil); | |
377 if(ch == '\n') | |
378 goto Out; | |
379 }sleep(dt); | |
380 } | |
381 /* loopcount -1 means no loop (this code's rule)… | |
382 }while(loopcount==0 || ++nloop<loopcount); | |
383 /* loop count has run out */ | |
384 ekbd(); | |
385 Out: | |
386 drawop(screen, screen->clipr, display->white, nil, ZP, S… | |
387 } | |
388 if(n>1 && output) | |
389 fprint(2, "gif: warning: only writing first image in %d-… | |
390 if(nineflag){ | |
391 if(images[0]->gifflags&TRANSP){ | |
392 addalpha(rgbv[0]); | |
393 blackout(images[0], rgbv[0]); | |
394 } | |
395 chantostr(buf, outchan); | |
396 print("%11s %11d %11d %11d %11d ", buf, | |
397 rgbv[0]->r.min.x, rgbv[0]->r.min.y, rgbv[0]->r.m… | |
398 if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv… | |
399 fprint(2, "gif: %s: write error %r\n", name); | |
400 return "write"; | |
401 } | |
402 }else if(cflag){ | |
403 if(images[0]->gifflags&TRANSP){ | |
404 addalpha(rgbv[0]); | |
405 blackout(images[0], rgbv[0]); | |
406 } | |
407 if(writerawimage(1, rgbv[0]) < 0){ | |
408 fprint(2, "gif: %s: write error: %r\n", name); | |
409 return "write"; | |
410 } | |
411 } | |
412 | |
413 Return: | |
414 allims = nil; | |
415 allmasks = nil; | |
416 for(k=0; images[k]; k++){ | |
417 for(j=0; j<images[k]->nchans; j++) | |
418 free(images[k]->chans[j]); | |
419 free(images[k]->cmap); | |
420 if(rgbv[k]) | |
421 free(rgbv[k]->chans[0]); | |
422 freeimage(ims[k]); | |
423 freeimage(masks[k]); | |
424 free(images[k]); | |
425 free(rgbv[k]); | |
426 } | |
427 free(images); | |
428 free(masks); | |
429 free(ims); | |
430 return err; | |
431 } |