URI: 
       tgif.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR 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.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 }