gif.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.min)); 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]->r.min, S); 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-color bitmap file; no display by default */ 78 threeflag++; 79 /* fall through */ 80 case 't': /* produce encoded, compressed, true-color bitmap file; no display by default */ 81 cflag++; 82 dflag++; 83 output++; 84 defaultcolor = 0; 85 outchan = RGB24; 86 break; 87 case 'c': /* produce encoded, compressed, bitmap file; no display by default */ 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 diffusion */ 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 file; no display by default */ 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", argv[i]); 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", name); 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", name); 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", name); 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 first, then b g r */ 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", name); 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 && threeflag==0)) 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 failed: %r\n", name); 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, GREY8, 0, 0); 347 else 348 ims[k] = allocimage(display, rgbv[k]->r, outchan, 0, 0); 349 if(ims[k] == nil){ 350 fprint(2, "gif: allocimage %s failed: %r\n", name); 351 err = "allocimage"; 352 goto Return; 353 } 354 if(loadimage(ims[k], ims[k]->r, rgbv[k]->chans[0], rgbv[k]->chanlen) < 0){ 355 fprint(2, "gif: loadimage %s failed: %r\n", name); 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 || ch==0x04) /* an odd, democratic list */ 376 exits(nil); 377 if(ch == '\n') 378 goto Out; 379 }sleep(dt); 380 } 381 /* loopcount -1 means no loop (this code's rule), loopcount 0 means loop forever (netscape'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-image GIF %s\n", n, name); 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.max.x, rgbv[0]->r.max.y); 398 if(write(1, rgbv[0]->chans[0], rgbv[0]->chanlen) != rgbv[0]->chanlen){ 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 }