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